> ## Documentation Index
> Fetch the complete documentation index at: https://docs.prosperavest.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Transfers and balances

> Read the balance of ENSC or a pair token for any account on a chain of your environment, and build an unsigned ENSC transfer for your own wallet to sign and broadcast.

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.

| Query parameter   | Required                            | Meaning                                                                                                |
| ----------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `account`         | yes                                 | The wallet address to read                                                                             |
| `chain`           | yes                                 | The chain slug, for example `celo` or `celo-sepolia`                                                   |
| `asset`           | one of `asset` or `contractAddress` | `ENSC`, `USDC`, `USDT` or `CELO`                                                                       |
| `contractAddress` | one of `asset` or `contractAddress` | The token contract address, as an alternative to `asset`; it must be a whitelisted token on that chain |

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`.

```ts theme={null}
// Read: the response is sealed to your key and opened for you
const balance = await ensc.balance.get({
  account: '0x…',
  chain: 'celo',
  asset: 'ENSC',
});
```

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.

| Body field        | Required | Meaning                                             |
| ----------------- | -------- | --------------------------------------------------- |
| `from`            | yes      | The wallet that will sign and send                  |
| `recipient`       | yes      | The wallet that receives the ENSC                   |
| `amount`          | yes      | A decimal string in ENSC units, for example `"250"` |
| `chain`           | yes      | The chain slug                                      |
| `asset`           | no       | Only `ENSC`; the default                            |
| `clientReference` | no       | Up to 128 characters, for your own reference        |

```ts theme={null}
import { signAndBroadcast } from '@ensc/sdk/web3';

const { unsignedTransaction, chain, amount } = await ensc.transfer.create({
  from: wallet,
  recipient: '0x…',
  amount: '250',
  chain: 'celo',
});
// unsignedTransaction is { from, to, data, value: '0', chainId }

// Optional helper (needs viem): estimates gas, fills fees and nonce, sends, waits for the receipt
const { txHash } = await signAndBroadcast(unsignedTransaction, signer, { rpcUrl });
```

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`.

<Note>
  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](/guides/conversions).
</Note>
