Product and technical spec
OneSwap Wallet
OneSwap Wallet is a hosted Canton wallet product for external-party wallets. It combines Google-backed encrypted recovery with client-side private-key control, Canton provisioning, token transfers, validator fee reimbursement, consolidation, and Featured App marker automation.
System design
Architecture
The frontend is responsible for wallet key generation/import, encrypted vault storage, Google Drive backup, and local signing. The backend is responsible for wallet-scoped auth, Canton/validator API calls, transaction preparation, transaction submission, balances, activity, and automation.
Encrypts vault, stores local state, signs challenges and tx hashes.
Prepares Canton operations and submits signed payloads.
Participant JSON API, validator API, transfer factory, and marker contracts.
Trust model
Custody and security
- The backend never stores or receives raw private keys.
- The browser stores an encrypted private-key vault in IndexedDB.
- Google Drive stores only the encrypted vault JSON in the hidden appDataFolder.
- Wallet actions require a backend challenge signed locally by the wallet key.
- API access is layered: project API keys identify the integration, wallet sessions come from signed challenges, and write paths can require Turnstile verification before preparation or submission.
- Turnstile and rate limits protect provisioning, writes, reads, and transfers.
Capabilities
Features
Lifecycle
Core flows
Create wallet
- User enters a 6-digit wallet PIN and signs in with Google if backup is required.
- Browser checks for an existing Google Drive backup before creating a new wallet.
- Browser generates a keypair and signs a backend challenge locally.
- Backend prepares topology and activation transactions.
- Browser signs hashes locally; backend submits and activates the wallet.
- Browser saves the encrypted local vault and uploads encrypted backup.
Send asset
- Backend prepares the main transfer and returns a tx hash.
- When reimbursement is enabled, backend estimates traffic and prepares a CC fee transfer.
- Frontend shows a confirmation modal with amount, recipient, and fee.
- Browser signs prepared hashes locally.
- Backend submits the main transfer and then the fee transfer.
Recovery policy
Google backup and one-wallet UX
The desired account policy is one wallet per Google account. On create/import, the app should check Drive appDataFolder first. If an encrypted backup already exists, it should direct the user to restore instead of creating another wallet.
Holdings
Consolidation
Consolidation prepares MergeDelegation setup when required, then prepares merge transactions for assets with multiple unlocked holdings. This keeps wallet balances usable when one asset is split across many small holdings.
Featured App
Marker automation
Completed non-CC wallet activity is recorded and submitted later throughBatchedMarkersProxy_CreateMarkersV2. The service queries FeaturedAppRight and BatchedMarkersProxy, waits for enough batch weight, then submits the marker batch.
Marker submission is backend automation. It does not change wallet custody or signing: user transfers are still signed locally, while eligible wallet activity is batched later for Featured App accounting.
Backend
API surface
All public wallet routes are versioned under /api/v1.
Wallet auth
/api/v1/auth/challenge/api/v1/auth/verifyProvisioning
/api/v1/wallets/provision/prepare/api/v1/wallets/provision/submit/api/v1/wallets/:partyId/preapproval/api/v1/wallets/:partyId/activateOperations
/api/v1/wallets/:partyId/balances/api/v1/wallets/:partyId/incoming-transfers/api/v1/wallets/:partyId/transfers/prepare/api/v1/wallets/:partyId/transfers/submit/api/v1/wallets/:partyId/consolidation/prepare/api/v1/wallets/:partyId/consolidation/delegation/prepareEconomics
Network fee reimbursement
When enabled, sends prepare an additional CC reimbursement transfer to the treasury party. The transfer memo/reason is transfer_fee. The charged amount is the main prepared transfer's estimated traffic cost plus a configurable buffer.
The fee is shown to the user before signing. The wallet signs the primary asset transfer and the separate reimbursement transfer locally, so the user can review the full cost before submit.
Partners
Integration notes
OneSwap Wallet is a hosted web wallet with backend APIs and local browser signing. External apps can open the wallet popup provider at /provider and interact over postMessage. The current partner surface is intentionally narrow: it exposes the active Canton account and exact UTF-8 challenge signing with Ed25519.
Temporary bridge
OneSwap DEX auth challenge
This temporary integration is intentionally limited to wallet identity and auth-challenge signing for OneSwap DEX. It does not expose generic ledger APIs, prepared transaction signing, or broad wallet provider semantics.
partyId and the wallet publicKey.Ed25519.How the DEX flow works
The DEX opens /provider in a popup controlled by the wallet origin.
The DEX asks the popup for the active Canton account so it can identify the user wallet.
The DEX sends the exact OneSwap challenge text and the wallet asks the user to approve signing.
The DEX receives the Ed25519 signature, public key, and partyId and verifies them on its side.
Temporary popup surface
/provider in a popup and communicates over postMessage.canton_listAccounts returns the currently active Canton account.canton_signMessage signs the exact OneSwap auth challenge after user approval.Example popup lifecycle
A typical auth flow from the DEX side looks like this:
// 1. Open the wallet popup
const popup = window.open('https://wallet.oneswap.cc/provider', 'oneswap_wallet_provider');
// 2. Wait for the wallet popup to announce readiness
window.addEventListener('message', (event) => {
if (event.data?.source === 'oneswap_wallet_provider' && event.data?.type === 'ready') {
// 3. Ask for the active account
popup?.postMessage({
source: 'oneswap_wallet_client',
version: 1,
id: 'req_accounts_1',
method: 'canton_listAccounts'
}, 'https://wallet.oneswap.cc');
}
});After receiving the active account, the DEX can request a challenge signature:
popup?.postMessage({
source: 'oneswap_wallet_client',
version: 1,
id: 'req_sign_1',
method: 'canton_signMessage',
params: {
partyId: '<active canton party id>',
message: 'Sign in to OneSwap DEX\nChallenge: 7f6c...\nNonce: 12345',
encoding: 'utf8'
}
}, 'https://wallet.oneswap.cc');Example responses
Account response:
{
"source": "oneswap_wallet_provider",
"version": 1,
"id": "req_accounts_1",
"method": "canton_listAccounts",
"success": true,
"result": [
{
"partyId": "<active canton party id>",
"publicKey": "<wallet public key hex>",
"signingAlgorithm": "ed25519"
}
]
}Challenge-signing response:
{
"source": "oneswap_wallet_provider",
"version": 1,
"id": "req_sign_1",
"method": "canton_signMessage",
"success": true,
"result": {
"partyId": "<active canton party id>",
"publicKey": "<wallet public key hex>",
"signingAlgorithm": "ed25519",
"signature": "<ed25519 signature hex>"
}
}Error handling
wallet_not_readyNo provisioned OneSwap Wallet exists in the browser yet.request_in_progressThe popup is already handling another approval request.user_rejectedThe user explicitly rejected the account-share or signing request.partyId.This section documents only the current OneSwap DEX auth bridge. Full provider compatibility or broader transaction-signing methods can be added later without changing the wallet’s local-signing custody model.