Unified Stablecoin Balance
1. Overview
The Unified Balance abstractly represents a user’s stable value across all operations in Wirex BaaS.
Instead of holding multiple versions of USDT, USDC, or EURC across different blockchains, Wirex normalizes all stable-value into:
- WUSD — Wirex USD, for USD-equivalent value
- WEUR — Wirex EUR, for EUR-equivalent value
These wrapped tokens exist inside Base Chain and are not directly transacted by developers on external blockchains. It is a chain-agnostic, and asset-agnostic model for all internal balances, transfers, and settlements.
Important:
Cross-chain deposits and Smart Deposit Addresses are described in the companion article.
This document focuses exclusively on the mechanics of wrapped stablecoins (WUSD/WEUR) and how they power the Unified Balance model.
2. Why WUSD / WEUR Exist
External stablecoins vary widely:
- different token standards (ERC-20, TRC-20, SPL, KIP)
- different decimal precision
- chain-specific semantics and gas rules
- inconsistent liquidity conditions
- various settlement and finality models
This fragmentation makes unified accounting extremely complex.
WUSD and WEUR solve these issues by acting as the internal canonical stablecoins used for all accounting, settlement, and value transfers inside Wirex.
Problems solved by WUSD/WEUR
| Problem | Solution |
|---|---|
| Different token standards | Internal canonical wrapped asset |
| Multi-chain fragmentation | One currency ledger |
| Complex reconciliation | One accounting source of truth |
| Conversion inconsistency | Always 1:1 |
| Bridge logic | Never needed internally |
| Liquidity fragmentation | Unified global pool |
3. How WUSD/WEUR are Created (Minting)
Wrapped stablecoins are minted in two primary ways:
3.1 Deposit Minting (Wrap)
When users deposit external stablecoins (USDC, USDT, EURC) to their Wirex BaaS registered smart wallet, the system automatically:
- Receives the deposit on the source blockchain
- Verifies the transaction and amount
- Mints equivalent WUSD or WEUR at 1:1 ratio
- Credits the user's Unified Balance
The original tokens are held in reserve, while wrapped tokens represent the internal value.
3.2 Direct Credit Minting
Certain operations create WUSD/WEUR directly without external deposits:
- Card refunds — When merchants refund card transactions
- Bank transfers — When users receive fiat via bank transfer
- Internal transfers — Peer-to-peer transfers between Wirex users
- Cashback and rewards — Promotional credits
These operations mint wrapped tokens directly into the user's balance, maintaining the 1:1 backing through Wirex's reserve management.
Minting Flow Overview
flowchart LR
subgraph Onchain["On-chain Deposits"]
USDC[USDC]
USDT[USDT]
EURC[EURC]
end
subgraph OffChain["Off-chain Credits"]
Refund[Card Refund]
Bank[Bank Transfer]
end
Wrap[Wrap Process<br/>1:1 Conversion]
DirectMint[Direct Mint<br/>Fiat-backed]
AA[Smart Wallet AA<br/>Unified Balance]
USDC -->|Deposit| Wrap
USDT -->|Deposit| Wrap
EURC -->|Deposit| Wrap
Wrap -->|WUSD/WEUR| AA
Refund -->|Credit| DirectMint
Bank -->|Credit| DirectMint
DirectMint -->|WUSD/WEUR| AA
style Wrap fill:#4CAF50
style DirectMint fill:#2196F3
style AA fill:#FF9800
Key Properties
| Property | Description |
|---|---|
| Minting ratio | Always 1:1 with source value |
| Decimal precision | 18 decimals (WUSD/WEUR) |
| Backing | Fully reserved (deposits + fiat reserves) |
| Reversibility | Can be unwrapped back to base tokens |
| Transferability | Internal transfers within Wirex ecosystem |
4. Unwrap Mechanics (Withdrawals)
When sending value to a blockchain address, wrapped assets are unwrapped into external stablecoins.
Unwrap Sequence
sequenceDiagram
participant User
participant App
participant SmartWallet as Smart Wallet (AA)
participant DelayPolicy as Execution Delay Policy
participant SyntheticToken as Wirex Token (WUSD)
participant BaseToken as Base Token (USDC)
participant Recipient
Note over User,Recipient: Phase 1: Initialize Request (First Transaction)
User->>App: Initiate transfer (amount, recipient)
App->>App: Generate unwrap call data
App->>App: Generate ERC20 transfer call data
App->>App: Encode both calls together
App->>App: Calculate custom nonce<br/>from keccak256(encodedCalls)
App->>SmartWallet: Get nonce with custom key
SmartWallet-->>App: Return nonce value
App->>App: Create request() call data<br/>(address, nonce, encodedCalls)
App->>SmartWallet: sendUserOperation(requestCallData)
SmartWallet->>DelayPolicy: Execute request()
DelayPolicy->>DelayPolicy: Store request with delay<br/>(e.g., 24 hours)
DelayPolicy-->>SmartWallet: Request stored
SmartWallet-->>App: Transaction hash
App-->>User: Request initiated ✓
Note over User,Recipient: ⏰ Waiting Period (Delay Timer)
Note over User,Recipient: Phase 2: Execute Request (Second Transaction)
User->>App: Execute transfer<br/>(after delay period)
App->>App: Calculate custom nonce<br/>from keccak256(encodedCalls)
App->>SmartWallet: Get nonce with custom key
SmartWallet-->>App: Return nonce value
App->>SmartWallet: sendUserOperation(encodedCalls, nonce)
SmartWallet->>DelayPolicy: Verify delay passed
DelayPolicy-->>SmartWallet: Approved ✓
SmartWallet->>SyntheticToken: withdraw(amount)
SyntheticToken->>SyntheticToken: Burn wrapped tokens
SyntheticToken->>BaseToken: Release base tokens
BaseToken-->>SmartWallet: Base tokens received
SmartWallet->>BaseToken: transfer(recipient, amount)
BaseToken->>Recipient: Transfer base tokens
BaseToken-->>SmartWallet: Transfer complete
SmartWallet-->>App: Transaction hash
App-->>User: Transfer completed ✓
Supported Unwrap Paths
| Wrapped | Result Token | Chains Supported |
|---|---|---|
| WUSD | USDC | Base |
| WEUR | EURC | Base |
5. API: Retrieve Unified Balance
GET /api/v1/wallet
GET /api/v1/walletResponse:
...
"balances": [
{
"token_symbol": "WEUR",
"token_address": "0x5f0818Cfc6554aC3d018aeF81D310ABC1dcCcCD7",
"balance": 7.27,
"reference_balance": 8.403393,
"reference_currency": "USD"
},
{
"token_symbol": "WUSD",
"token_address": "0xD08C1401dd0E8aA012D9C7b4471d45FdC8f1C97E",
"balance": 1581.07,
"reference_balance": 1581.07,
"reference_currency": "USD"
}
]
...6. API: Unwrap & Send (Withdrawal)
import { encodeFunctionData, erc20Abi, keccak256, parseUnits } from 'viem';
import { getCustomNonceKeyFromString } from '@zerodev/sdk';
// ABIs (inline for self-contained example)
const syntheticAbi = [{ name: 'withdraw', type: 'function', inputs: [{ type: 'uint256' }] }];
const executionDelayPolicyAbi = [{
name: 'request',
type: 'function',
inputs: [{ type: 'address' }, { type: 'uint256' }, { type: 'bytes' }]
}];
interface TransferParams {
tokenAddress: `0x${string}`;
recipientAddress: `0x${string}`;
amount: string;
baseTokenAddress: `0x${string}`;
decimals: number;
baseDecimals: number;
}
/**
* Step 1: Initialize unwrap and transfer request
* Creates a delayed execution request for unwrapping synthetic token and transferring base token
*/
async function initializeUnwrapAndTransfer(
smartWalletClient: any,
executionDelayPolicyContract: `0x${string}`,
params: TransferParams
) {
// 1. Generate unwrap call data
const unwrapCallData = encodeFunctionData({
abi: syntheticAbi,
functionName: 'withdraw',
args: [parseUnits(params.amount, params.decimals)],
});
// 2. Generate ERC20 transfer call data
const transferCallData = encodeFunctionData({
abi: erc20Abi,
functionName: 'transfer',
args: [params.recipientAddress, parseUnits(params.amount, params.baseDecimals)],
});
// 3. Prepare call data array
const callDataArray = [
{ to: params.tokenAddress, value: 0n, data: unwrapCallData },
{ to: params.baseTokenAddress, value: 0n, data: transferCallData }
];
// 4. Encode execution calls
const encodedCalls = await smartWalletClient.account.encodeCalls(callDataArray);
// 5. Generate custom nonce for delayed execution
const nonceKey = keccak256(encodedCalls);
const customNonce = getCustomNonceKeyFromString(nonceKey, '0.7');
const nonce = await smartWalletClient.account.getNonce({ key: customNonce });
// 6. Create execution delay policy request
const requestCallData = encodeFunctionData({
abi: executionDelayPolicyAbi,
functionName: 'request',
args: [smartWalletClient.account.address, nonce, encodedCalls],
});
// 7. Encode and send request
const requestCalls = await smartWalletClient.account.encodeCalls([{
to: executionDelayPolicyContract,
value: 0n,
data: requestCallData,
}]);
const { hash } = await smartWalletClient.sendUserOperation({ callData: requestCalls });
console.log('Request initiated:', hash);
return { hash, encodedCalls };
}
/**
* Step 2: Execute the delayed operation
* After delay period, execute the unwrap and transfer
*/
async function executeUnwrapAndTransfer(
smartWalletClient: any,
withdrawalCallData: `0x${string}`
) {
// 1. Calculate custom nonce from call data
const nonceKey = keccak256(withdrawalCallData);
const customNonce = getCustomNonceKeyFromString(nonceKey, '0.7');
const nonce = await smartWalletClient.account.getNonce({ key: customNonce });
// 2. Send user operation with custom nonce
const { hash } = await smartWalletClient.sendUserOperation({
callData: withdrawalCallData,
nonce
});
console.log('Execution completed:', hash);
return hash;
}
// Usage example
async function example(smartWalletClient: any, executionDelayPolicyContract: `0x${string}`) {
const params: TransferParams = {
tokenAddress: '0x...', // WUSD/WEUR
baseTokenAddress: '0x...', // Base token (USDC/EURC)
recipientAddress: '0x...',
amount: '100',
decimals: 18,
baseDecimals: 6
};
// Step 1: Initialize request (creates delay)
const { encodedCalls } = await initializeUnwrapAndTransfer(
smartWalletClient,
executionDelayPolicyContract,
params
);
// Step 2: After delay period, execute
await executeUnwrapAndTransfer(smartWalletClient, encodedCalls);
}7. Summary
WUSD and WEUR are the foundational wrapped stablecoins powering all Unified Balance mechanics in Wirex BaaS.
They provide:
- A single canonical asset per currency
- Deterministic internal accounting
- Unified global liquidity
- Stable, predictable, chain-agnostic operations
- Seamless unwrap → blockchain send workflows
- Zero need for developers to manage token formats or bridging
By integrating with Unified Balance, developers gain a simple, reliable, and future-proof way to handle stablecoin value across any chain.
Updated 5 days ago
