Skip to main content
Two operations sit outside the conversion flow: reading a balance and moving ENSC between wallets. Neither involves a voucher. As everywhere in the API, ENSC never holds a wallet key and never broadcasts: a transfer comes back as unsigned calldata for your wallet.

Reading a balance

GET /v1/balance (ensc.balance.get()) returns the balance of a whitelisted asset (ENSC, USDC, USDT, CELO) for an account on a chain of your key’s environment. It needs the balances:read scope; publishable keys can hold only this scope. One of asset or contractAddress is required (400 ENSC_VALIDATION_FAILED otherwise). An asset that is not whitelisted on the chain answers ENSC_INVALID_ASSET.
The response is { account, asset, chain, balance, formatted, decimals }. balance is a base-unit integer string and formatted is the same value already divided by the asset’s decimals (ENSC 18, CELO 18, USDC and USDT 6). Do arithmetic on balance with an arbitrary-precision integer, never on formatted and never in floating point. The balance reads any whitelisted asset on any enabled chain of your environment. The converter chains are celo-sepolia (Sandbox) and celo (Live); the other chains in the registry (base, polygon, optimism, ethereum, arbitrum, bsc, mode and their testnets base-sepolia, polygon-amoy, optimism-sepolia, sepolia, arbitrum-sepolia, bsc-testnet, mode-sepolia) carry the ENSC token only, and balance works there where ENSC is deployed. An unknown or disabled chain, or a chain from the other environment, answers ENSC_INVALID_CHAIN. A read is not signed, but it is sealed to your signing key. Send X-ENSC-Key-Id so ENSC knows which key to seal the response to when more than one is active; the SDK does this on every request.

Building a transfer

POST /v1/transfer (ensc.transfer.create()) builds a plain ENSC transfer. It needs the transfer:create scope. ENSC checks the sender’s balance and returns the calldata; it does not sign or broadcast anything.
The response is { unsignedTransaction: { from, to, data, value: "0", chainId }, chain, amount }. Every piece of calldata ENSC returns has the same shape: from is the wallet that must sign it, to is the contract the call goes to, value is always "0" (ENSC never asks for native value) and chainId names the chain. Gas, fees and nonce are yours to fill. transfer works on the converter chain of your environment and on the other chains where ENSC is deployed. ENSC checks the sender’s balance before returning the calldata: a zero amount is 400 ENSC_AMOUNT_TOO_SMALL, and a sender balance below the amount is 400 ENSC_INSUFFICIENT_BALANCE, with the current balance and the requested amount (base units) in details.

Signing with your own infrastructure

signAndBroadcast is a convenience; any EVM signer works with the calldata. If you sign yourself:
  1. Check chainId against the chain your RPC endpoint serves, and from against the account you are signing with.
  2. Estimate gas first, and send with an explicit gas limit. Without one, some nodes estimate at the block gas limit and charge that much gas up front during the simulation.
  3. Send value: 0.
signAndBroadcast does exactly this: it estimates gas without fee fields and sends with the estimate plus 30 percent (gasMarginPercent), or with the limit you pass as gas. It returns { txHash, status?, blockNumber? }; pass waitForReceipt: false to return as soon as the hash is known. If the estimate fails, nothing is broadcast and the node’s reason is in the ENSC_UPSTREAM_FAILED message. signer is a raw wallet private key or any viem account (privateKeyToAccount, mnemonicToAccount, toAccount around an HSM, KMS or custody signer, or the JSON-RPC account of a wallet a user connected). It is a separate secret from the ENSC credentials, never touches the ENSC API and is never stored by the SDK; pass it per call and do not put it in EnscClient config. The helper refuses a signer that is not the transaction’s from. A browser wallet signs the same { from, to, data, value, chainId } calldata directly; nothing about ENSC requires exporting a private key. A receipt that cannot be read raises ENSC_UPSTREAM_FAILED with details.txHash.
A transfer is not a conversion: there is no voucher and no report step. To move between ENSC and a pair token or Naira, use Conversions.