Cobo - Wallet Custody Integration (Waas 2.0)
Available To
Free for the following exchange plans:
On-Premise Plans:
Enterprise Unlimited
This is a wallet custody plugin and is only available on On-Premise (Unlimited) plans. It is not offered on Cloud plans because custody must be controlled by the exchange operator's own infrastructure and Cobo API credentials.
What Is It?
The Cobo Plugin connects a HollaEx exchange to Cobo Wallet-as-a-Service 2.0 using a single shared Custodial Asset Wallet as the custody backend.
It exposes server-side routes only:
Generates per-user deposit addresses inside the shared Cobo wallet.
Credits are deposited when Cobo emits a
transactions.*webhook, and the canonical state is reconfirmed via a signedGET /v2/transactions/{id}API call.Dispatches pending exchange withdrawals to Cobo every minute via
POST /v2/transactions/transfer.Approves only those Cobo callback requests whose
request_idmatches a pending withdrawal it previously dispatched (auto-deny otherwise).
Who Needs It?
This plugin is suitable for exchange operators that:
Use Cobo as their custody provider.
Want a single shared Custodial Asset Wallet with per-user deposit addresses across many chains.
Want pending exchange withdrawals submitted to Cobo automatically.
Need defense-in-depth on deposit credits: every webhook is re-verified against Cobo's authenticated API before any mint is created or updated.
How to Use It?
Install the plugin from the Plugins section inside the Operator Control, then configure the plugin meta with your Cobo API credentials and shared wallet ID.
1. Generate a Cobo API key pair
Cobo authenticates every API request with an Ed25519 key pair.
Generate a key pair using either the Cobo CLI, OpenSSL, or any Ed25519 library. The output you need is two 32-byte hex strings:
API key - the public key in hex (64 hex characters).
API secret - the private key in hex (64 hex characters).
Register the public key on Cobo Portal under API Management -> Register an API key, scoped at minimum to:
wallet.read,wallet.create_addresstransaction.read,transaction.withdrawwebhook.read,webhook.edit,callback.read
2. Create a shared Custodial Asset Wallet
In Cobo Portal, create a new wallet:
wallet_type:Custodialwallet_subtype:Asset
Copy the wallet ID. This is the single shared wallet that will hold per-user deposit addresses for every supported chain. The plugin does not create one wallet per exchange user.
3. Configure the plugin
Open the plugin in the Operator Control and set the following fields:
api_url
yes
https://api.dev.cobo.com/v2
Cobo WaaS 2.0 base URL. Use https://api.cobo.com/v2 for production.
api_key
yes
empty
Your Cobo API public key (hex, 64 chars).
api_secret
yes
empty
Your Cobo API private key (hex, 64 chars). Used to sign every outbound API request.
wallet_id
yes
empty
ID of the shared Custodial Asset Wallet.
webhook_public_key
yes
dev key
Cobo's Ed25519 public key (hex) used to verify inbound webhooks and callbacks. Production: 8d4a482641adb2a34b726f05827dba9a9653e5857469b8749052bf4458a86729. Development: a04ea1d5fa8da71f1dcfccf972b9c4eba0a2d8aba1f6da26f49977b08a0d2718.
The plugin will not initialize unless all required values are present.
4. Register the webhook endpoint on Cobo Portal
In Cobo Portal -> Developers -> Webhook Endpoints, register:
Subscribe, at minimum, to:
transactions.createdtransactions.updated
Cobo signs every event with Biz-Resp-Signature and Biz-Timestamp request headers. The plugin verifies the signature and then re-fetches the canonical transaction via GET /v2/transactions/{id} so the on-disk state, not the webhook body, is what drives the deposit credit or withdrawal status update. Forged or stale webhooks are dropped.
5. Register the callback endpoint on Cobo Portal
Cobo asks an external endpoint to approve every initiated withdrawal before it is signed and broadcast. In Cobo Portal -> Developers -> Callback Endpoints, register:
The plugin replies in plain text with ok or deny:
okonly when the callback'srequest_idmatches a pending exchange withdrawal that the plugin previously dispatched and is inwaiting=true, status=false, dismissed=false, rejected=falsestate.denyfor everything else, including missing/invalid signature, unknownrequest_id, or burns that are no longer in the dispatched state.
6. User-facing address creation
Authenticated users request a deposit address with:
The plugin:
Returns
400if the user already has a wallet for this currency+network.Reuses any existing same-network address the user already has, if one exists.
Otherwise calls
POST /v2/wallets/{wallet_id}/addresseswith{ chain_id, count: 1 }and registers the returned address withtoolsLib.wallet.createUserWalletByKitId.
7. Withdrawal dispatch
Two cron jobs run every minute:
markPendingWithdrawalsProcessingflips eligible pending burns toprocessing: true. A burn is eligible when it is not completed, dismissed, rejected, waiting, processing, or on hold, and its currency maps to a Cobo chain.dispatchPendingWithdrawalspicks upprocessingburns, marks themwaiting: true, processing: false, then callsPOST /v2/transactions/transferwith:request_idset to the exchange's burntransaction_id(used for callback matching and webhook reconciliation).source: { source_type: "Asset", wallet_id }.token_idresolved viaGET /v2/wallets/{wallet_id}/tokens?chain_ids=...(cached in memory).destination.account_output: { address, amount }.
Final completion is recorded later by the webhook (which re-verifies via the Cobo API) and writes status: true plus the on-chain hash to the exchange burn record.
To trigger dispatch manually as an admin:
Health check
Supported networks
Supported chains are loaded from Cobo at startup via GET /v2/wallets/chains?wallet_type=Custodial&wallet_subtype=Asset and cached in memory. The plugin maps Cobo chain names to exchange currency symbols. Built-in mappings include Bitcoin, Bitcoin Cash, Litecoin, Dogecoin, Dash, Ethereum, Tron, Solana, Polygon, Avalanche C-Chain, Arbitrum, Optimism, Base, BNB Smart Chain, XRP, Cosmos, Algorand, Cardano, Polkadot, Stellar, Tezos, TON, NEAR, Aptos, and Sui. Other chains returned by Cobo are best-effort matched by name.
Assets without a mapped Cobo chain are skipped by the withdrawal dispatcher and cannot derive deposit addresses until the mapping is added.
Defense-in-Depth
Every completed credit deposit, and withdrawal, goes through two checks before the kit ledger is touched:
The inbound webhook signature is verified with Cobo's published Ed25519 public key.
The plugin then makes its own signed
GET /v2/transactions/{id}call and uses that response (status, amount, destination address, transaction hash, request_id) to decide what to credit or update.
Even if the webhook signature were ever bypassed or the public key were misconfigured, no funds move unless Cobo's authenticated API confirms the same transaction.
Benefits for HollaEx Operators
The Cobo plugin lets an exchange keep custody operations inside Cobo while preserving the normal HollaEx wallet flow for users. Deposit addresses are derived from a shared Cobo Custodial Asset Wallet; deposit credits depend on signed Cobo webhooks and Cobo API confirmation, outgoing withdrawals are queued and dispatched on a cron, and Cobo callbacks for those withdrawals are auto-approved only when they match a known pending exchange burn. This keeps custody integration centralized, auditable, and aligned with the exchange's existing pending transaction workflow.
Last updated