Tokenization

Deploy, Mint, and Burn Tokens with Institutional Vault

This guide walks through deploying an ERC-20 token contract, registering it with the wallet, and performing mint and burn operations from a sandbox.

Base URL format: https://<sandbox>.api.blockdaemon-wallet.com/

Published documentation: https://vault.docs.blockdaemon.com/


Prerequisites


Step 1: Create a System User and Obtain an API Key

System users provide programmatic API access. Only admin users can create them.

  1. Log in to the Institutional Vault UI at https://<sandbox>.blockdaemon-wallet.com/
  2. Navigate to Settings > System Users
  3. Click Create System Users
  4. Set the system user Name, Role (MarketOps is sufficient for transactions), and Confirmer
  5. Click Create and approve the operation on the BD Approver app
  6. Copy the API Key from the system users list (shown only once)

Pass the API key on every CWP request in the authorization header (no Bearer prefix):

authorization: <your-api-key>

Docs: Quick Start — Get API Key


Step 2: Access the OpenAPI Specification

The full OpenAPI spec for your sandbox (including all CWP paths) is available at:

https://<sandbox>.blockdaemon-wallet.com/swagger.html

Download openapi.yaml from Swagger UI and generate a typed client from the spec for TypeScript, Go, Python, Java, or C#.

Published API reference: https://vault.docs.blockdaemon.com/reference

Generate Client SDKs: https://vault.docs.blockdaemon.com/reference/generate-client-sdks


Step 3: Identify Your Chain, Address, and CAIP-19 Values

API requests use CAIP-19 asset identifiers and an address-only Source (or full AddressSpec).

List registered chains

Bash

curl -s https://<sandbox>.api.blockdaemon-wallet.com/api/cwp/chains/list \
  -H "authorization: <your-api-key>" | jq

Note the chain CAIP-2 (e.g. eip155:11155111 for Ethereum Sepolia).

Create an account and get an address

You need a master key, a named account under that master key, and an on-chain address for the chain you will use. If you already have an address from the wallet UI or a prior API call, skip to Resolve a wallet-managed address.

Use Default for MasterKey unless your deployment is multitenanted and you created a different master key name.

1. Create the account (skip if the account already exists under your master key):

Bash

curl -s -X POST https://<sandbox>.api.blockdaemon-wallet.com/api/cwp/operations/start/createAccount \
  -H "authorization: <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "Name": "<account-name>",
    "MasterKey": "Default",
    "InitiatorID": "<vault-user-email>"
  }' | jq

Poll GET /api/cwp/operations/id/{operationID}/status until Succeeded. Approve on the BD Approver app when required. Optional InitiatorID: registered Vault user email (recommended so the gateway can sync the account to the Vault UI).

2. Get the account's native address for your EVM chain (use the CAIP-2 from the chains list, e.g. Sepolia):

Bash

curl -s -X POST https://<sandbox>.api.blockdaemon-wallet.com/api/cwp/addresses/get \
  -H "authorization: <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "MasterKey": "Default",
    "CAIP2": "eip155:11155111",
    "Account": "<account-name>",
    "Index": 0
  }' | jq

The response Address is the value to use as Source.Address in later makeTransaction calls. Index defaults to 0 (main address) when omitted.

Resolve a wallet-managed address

Confirm the signing address is owned by the vault:

Bash

curl -s -X POST https://<sandbox>.api.blockdaemon-wallet.com/api/cwp/addresses/resolve \
  -H "authorization: <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{"Address": "<your-wallet-address>"}' | jq

The response includes MasterKey, Account, and Index for that address.

Native asset CAIP-19 (gas)

For EVM contract deploy, mint, and burn, set CAIP19 to the chain's native asset (slip44), not the ERC-20 token. Example for Ethereum Sepolia:

eip155:11155111/slip44:60

Fund the signing address with enough native token on that chain to pay gas before you deploy, mint, or burn (for example, send Sepolia ETH to the address from a faucet or another wallet). Deploy and mint transactions fail if the address has insufficient balance for fees.

API reference:


Step 4: Deploy the Token Contract

Contract deployment uses POST /api/cwp/operations/start/makeTransaction with a pre-built unsigned transaction in RawTransaction. The vault performs MPC signing only; you broadcast the returned SignedTransaction to the network.

4a. Compile the contract

Use Foundry or Hardhat to compile your ERC-20 contract and extract the bytecode:

Bash

curl -L https://foundry.paradigm.xyz | bash
foundryup

forge init my-token && cd my-token
forge install OpenZeppelin/openzeppelin-contracts --no-commit
forge build

Extract the bytecode field from the compiler output JSON.

4b. Build an unsigned deploy transaction

Use your EVM library to construct an unsigned EIP-1559 transaction whose data field is the contract bytecode (no to address). Example with viem:

TypeScript

import { createPublicClient, http, serializeTransaction } from 'viem';
import { sepolia } from 'viem/chains';

const client = createPublicClient({ chain: sepolia, transport: http('<rpc-url>') });
const fromAddress = '<your-wallet-address>' as `0x${string}`;
const bytecode = '0x6080604052...' as `0x${string}`;

const nonce = await client.getTransactionCount({ address: fromAddress });
const gas = await client.estimateGas({ account: fromAddress, data: bytecode, value: 0n });
const { maxFeePerGas, maxPriorityFeePerGas } = await client.estimateFeesPerGas();

const rawTransaction = serializeTransaction({
  chainId: sepolia.id,
  nonce,
  value: 0n,
  gas,
  maxFeePerGas: maxFeePerGas!,
  maxPriorityFeePerGas: maxPriorityFeePerGas!,
  accessList: [],
  type: 'eip1559',
  data: bytecode,
});

4c. Start make transaction (sign only)

Bash

curl -s -X POST https://<sandbox>.api.blockdaemon-wallet.com/api/cwp/operations/start/makeTransaction \
  -H "authorization: <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "CAIP19": "eip155:11155111/slip44:60",
    "Source": { "Address": "<your-wallet-address>" },
    "RawTransaction": "0x02f8..."
  }' | jq

Optional InitiatorID: set to the Vault user's email when your policies require an identified initiator.

The response includes OperationID. Approve the operation on the BD Approver app when prompted.

4d. Poll status and broadcast

Bash

curl -s https://<sandbox>.api.blockdaemon-wallet.com/api/cwp/operations/id/<operationID>/status \
  -H "authorization: <your-api-key>" | jq

When Status is Succeeded, read Result.Transaction.SignedTransaction and publish it to your RPC:

Bash

cast publish --rpc-url <rpc-url> "0x<signed-transaction-hex>"

Find the deployed contract address from the transaction receipt on the block explorer (e.g. Sepolia Etherscan) using the broadcast tx hash.

API reference: Start Make Transaction


Step 5: Register the Token in the Wallet

Register the deployed ERC-20 so the vault tracks balances and policies can reference the token. To require approvals on deploy, mint, burn, or other on-chain operations, configure Transaction Restrictions.

Bash

curl -s -X POST https://<sandbox>.api.blockdaemon-wallet.com/api/cwp/operations/start/registerToken \
  -H "authorization: <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "CAIP19": "eip155:11155111/erc20:0x<deployed-contract-address>",
    "Symbol": "MYTKN",
    "Decimals": 18
  }' | jq

Poll GET /api/cwp/operations/id/{operationID}/status until Succeeded or Failed. Approve on the BD Approver app when required.

You can also register through the UI: Settings > Assets > Add Assets.

Confirm registration:

Bash

curl -s https://<sandbox>.api.blockdaemon-wallet.com/api/cwp/tokens/list \
  -H "authorization: <your-api-key>" | jq

Docs: Add ERC-20 Assets

API reference: Start Register Token


Step 6: Mint Tokens

Option A: Structured contract call (vault builds, signs, and broadcasts)

Use makeTransaction with EVM.Data set to ABI-encoded mint(address,uint256) calldata. Point Destination at the token contract with Amount: "0" (native-value transfer to the contract). Use the chain's native CAIP19 for gas, same as deploy.

Encode calldata (TypeScript with viem):

TypeScript

import { encodeFunctionData, parseAbi, parseEther } from 'viem';

const calldata = encodeFunctionData({
  abi: parseAbi(['function mint(address to, uint256 amount)']),
  functionName: 'mint',
  args: ['<recipient-address>' as `0x${string}`, parseEther('100')],
});

Submit:

Bash

curl -s -X POST https://<sandbox>.api.blockdaemon-wallet.com/api/cwp/operations/start/makeTransaction \
  -H "authorization: <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "CAIP19": "eip155:11155111/slip44:60",
    "Source": { "Address": "<your-wallet-address>" },
    "Destination": [\
      { "Address": "<token-contract-address>", "Amount": "0" }\
    ],
    "EVM": { "Data": "0x40c10f19..." }
  }' | jq

Poll operation status until Succeeded. On success, Result.Transaction.ID contains the on-chain transaction hash.

API reference: Start Make Transaction

Option B: Raw transaction (client builds unsigned tx, vault signs, you broadcast)

Build and serialize an unsigned EIP-1559 transaction to the token contract with mint calldata in data, then pass the hex string as RawTransaction (same pattern as Step 4). Do not set Destination or EVM when using RawTransaction.

After Status is Succeeded, broadcast Result.Transaction.SignedTransaction with your RPC client or cast publish.


Step 7: Burn Tokens

Burn uses the same makeTransaction patterns as mint; only the calldata changes.

Calldata encoding

Encode burn(uint256) (burns from the signer's balance):

TypeScript

import { encodeFunctionData, parseAbi, parseEther } from 'viem';

const calldata = encodeFunctionData({
  abi: parseAbi(['function burn(uint256 amount)']),
  functionName: 'burn',
  args: [parseEther('50')],
});

Or with Foundry cast:

Bash

cast calldata "burn(uint256)" 50000000000000000000

Structured contract call

Bash

curl -s -X POST https://<sandbox>.api.blockdaemon-wallet.com/api/cwp/operations/start/makeTransaction \
  -H "authorization: <your-api-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "CAIP19": "eip155:11155111/slip44:60",
    "Source": { "Address": "<your-wallet-address>" },
    "Destination": [\
      { "Address": "<token-contract-address>", "Amount": "0" }\
    ],
    "EVM": { "Data": "0x42966c68..." }
  }' | jq

Raw transaction

Follow Step 6, Option B, substituting burn calldata. The signing address must hold at least the burn amount of tokens.


Step 8: Monitor Operations

All CWP operations (deploy, register token, mint, burn) follow the same asynchronous pipeline: policy evaluation, approvals, MPC signing, and (for structured makeTransaction calls) broadcasting.

Poll operation status

Bash

curl -s https://<sandbox>.blockdaemon-wallet.com/api/cwp/operations/id/<operationID>/status \
  -H "authorization: <your-api-key>" | jq

Terminal Status values:

Status Meaning
Succeeded Completed successfully. For structured makeTransaction, Result.Transaction.ID is the tx hash. For RawTransaction flows, use Result.Transaction.SignedTransaction and broadcast locally.
Failed Rejected by policy, approver, or an execution error. See ErrorDetails.
Pending approval Waiting for approver action on the BD Approver app.
Pending confirmation Waiting for initiator confirmation when required by policy.
Executing / Pending signature In progress.

Approval details

Bash

curl -s https://<sandbox>.api.blockdaemon-wallet.com/api/cwp/operations/id/<operationID>/approvalstatus \
  -H "authorization: <your-api-key>" | jq

Inspect the stored intent

Bash

curl -s https://<sandbox>.api.blockdaemon-wallet.com/api/cwp/operations/id/<operationID>/intent \
  -H "authorization: <your-api-key>" | jq

Reference Links

Topic URL
Getting Started https://vault.docs.blockdaemon.com/docs/getting-started
Sandbox Enrollment https://vault.docs.blockdaemon.com/docs/blockdaemon-wallet-sandbox-enrollment-process
Onboarding / Approver App https://vault.docs.blockdaemon.com/docs/onboarding-the-wallet
Quick Start / API Key https://vault.docs.blockdaemon.com/reference/quick-start-blockdaemon-wallet
Generate SDKs https://vault.docs.blockdaemon.com/reference/generate-client-sdks
Add ERC-20 Assets https://vault.docs.blockdaemon.com/docs/add-erc-20-assets
Start Make Transaction https://vault.docs.blockdaemon.com/reference/cwpstartmaketransaction
Start Register Token https://vault.docs.blockdaemon.com/reference/cwpstartregistertoken
Start Create Account https://vault.docs.blockdaemon.com/reference/cwpstartcreateaccount
Get address https://vault.docs.blockdaemon.com/reference/cwpgetaddresscaip2
Get Operation Status https://vault.docs.blockdaemon.com/reference/cwpgetoperationstatus
Transaction Flows https://vault.docs.blockdaemon.com/docs/transaction-flows
User Types & Privileges https://vault.docs.blockdaemon.com/docs/user-types-and-privileges
OpenAPI Spec (Swagger UI) https://<sandbox>.blockdaemon-wallet.com/swagger.html