@extra-wallet/ugtp-private
v0.1.0
Published
Private token operations extension for UGTP SDK — supports esETH, esUSDT, esUSDC, esAVAX on Avalanche C-Chain
Readme
@extra-wallet/ugtp-private-tokens
Private token operations extension for UGTP SDK. Adds support for EXTRASAFE private tokens (esETH, esUSDT, esUSDC, esAVAX) on Avalanche C-Chain through UGTP smart accounts.
What it does
This package extends @extra-wallet/ugtp-sdk with private-token operations:
- Private account registration — register a smart account on the EXTRASAFE privacy registry
- Exchange public → private — deposit public tokens (USDT, USDC, WETH.e, AVAX) into their private equivalents
- Exchange private → public — withdraw private tokens back to public tokens
- Private send — transfer private tokens between registered accounts
- Balance queries — check public, private, and native balances
- Fee management — configurable system fee (bps, fixed, or hybrid)
- Gas sponsorship — all operations execute through UGTP smart accounts with gas supply
Installation
npm install @extra-wallet/ugtp-private-tokens viemPeer dependency (optional, for UGTP smart account adapter):
npm install @extra-wallet/ugtp-sdkConfiguration
Token configuration
The package ships with Avalanche C-Chain mainnet defaults:
| Private Token | Underlying | Address |
|---|---|---|
| esAVAX | AVAX (native) | 0x4a720d8DC36ecD5EcdBbD28E6244dfD148323b8B |
| esETH | WETH.e | 0xB51fA78636C5d1fC4753370fFCdA6d429b5D869D |
| esUSDC | USDC | 0xDE0A8888966Ad19689945D90EB6815D36Ada978A |
| esUSDT | USDT | 0xF291034f00085885E44ff1779c55bF53b24889Ae |
Registry: 0x234e1f95cb62629d2e609E8805db1D2f88eA1749
Fee configuration
const feeConfig = {
enabled: true,
feeRecipient: '0x...',
mode: 'bps', // 'fixed' | 'bps' | 'hybrid'
bps: 20, // 0.20%
}Usage
import { GaslessClient, PrivateKeySigner, NetworkId } from '@extra-wallet/ugtp-sdk'
import {
createPrivateTokensClient,
UgtpSmartAccountAdapter,
mainnetTokens,
mainnetContracts,
generateEncryptionKeyPlaceholder,
} from '@extra-wallet/ugtp-private-tokens'
// 1. Initialize UGTP SDK
const ugtpClient = new GaslessClient({
apiUrl: 'https://ugtp-api.example.com',
networkId: 'AVALANCHE',
signer: new PrivateKeySigner(privateKey),
rpcUrls: { AVALANCHE: 'https://api.avax.network/ext/bc/C/rpc' },
})
// 2. Create smart account adapter
const smartAccount = new UgtpSmartAccountAdapter({
client: ugtpClient,
networkId: 'AVALANCHE',
ownerAddress: '0x...',
})
// 3. Create private tokens client
const privateTokens = createPrivateTokensClient({
smartAccount,
chainId: 43114,
rpcUrl: 'https://api.avax.network/ext/bc/C/rpc',
contracts: mainnetContracts,
tokens: mainnetTokens,
feeConfig: {
enabled: true,
feeRecipient: '0x...',
mode: 'bps',
bps: 20,
},
})Register private account
const encryptionKey = generateEncryptionKeyPlaceholder()
const result = await privateTokens.registerPrivateAccount({
encryptionPublicKey: encryptionKey,
})
console.log('Registered:', result.txHash)Exchange public → private (deposit)
const quote = await privateTokens.getDepositQuote({
fromToken: 'USDT',
privateToken: 'esUSDT',
amount: '10',
})
console.log('Fee:', quote.fee.feeFormatted)
const result = await privateTokens.deposit({
fromToken: 'USDT',
privateToken: 'esUSDT',
amount: '10',
encryptedNewBalance: '0x...', // encrypted balance from client-side encryption
})Private send
const result = await privateTokens.send({
privateToken: 'esUSDT',
to: '0x...',
amount: '5',
encryptedFromBalance: '0x...', // encrypted sender's new balance
encryptedToBalance: '0x...', // encrypted recipient's new balance
})Exchange private → public (withdraw)
const result = await privateTokens.withdraw({
privateToken: 'esUSDT',
toToken: 'USDT',
amount: '5',
encryptedNewBalance: '0x...', // encrypted new balance after withdrawal
})Check balances
const balances = await privateTokens.getAllBalances()
console.log('Native:', balances.nativeBalance.formatted, 'AVAX')
console.log('Public:', balances.publicBalances)
console.log('Private:', balances.privateBalances)Test web app
The examples/test-web directory contains a Vite + React test app for real Avalanche C-Chain testing.
Setup
cd examples/test-web
npm install
cp ../../.env.example .env
# Edit .env with your settings
npm run devSecurity warning
The test web app accepts mnemonic phrases for wallet creation. This is for development/testing only. Do NOT use wallets with real funds unless you understand the risks. The mnemonic is held in memory only and is not persisted to storage.
Contract ABIs
The package includes ABI definitions for the deployed contracts:
PrivateAccountRegistry— registration and public key storagePrivateERC20Token— deposit, withdraw, privateTransfer for ERC-20 backed tokensPrivateNativeToken— deposit (payable), withdraw, privateTransfer for native AVAX
If the contract ABIs change, update the files in src/contracts/abis/.
Encrypted balance model
The private token contracts store balances as encrypted bytes blobs. The contracts do NOT track plaintext balances — all balance tracking happens client-side with encryption/decryption. When calling deposit, withdraw, or privateTransfer, you must provide the encrypted new balance(s).
The generateEncryptionKeyPlaceholder(), encryptBalance(), and decryptBalance() functions are placeholders for the actual encryption protocol. Replace them with the real implementation.
Replacing contract ABIs
- Update files in
src/contracts/abis/ - Ensure the ABI arrays are typed
as constfor proper type inference - Rebuild:
npm run build
Troubleshooting
"Chain X is not supported"
Only Avalanche C-Chain (43114) and Fuji testnet (43113) are supported.
"Token X is not configured"
The token symbol doesn't match any configured private token. Check the tokens array passed to createPrivateTokensClient.
"Invalid token pair"
The public/private token pair doesn't match. For example, you can only deposit USDT → esUSDT, not USDC → esUSDT.
"Recipient not registered"
The recipient address is not registered on the private account registry. They must call registerPrivateAccount first.
Smart account execution failures
Check that the UGTP SDK is properly configured with valid API URLs and signer. In dry-run mode, transactions are simulated without on-chain submission.
Development
npm install
npm run build # compile TypeScript
npm run test # run unit tests
npm run lint # typecheck without emitting
npm run dev # watch mode
npm run dev:test-web # start test web appLicense
MIT
