Skip to main content
Issue a bond with an ISIN, a face value and a maturity date through Hedera’s Asset Tokenization Studio (ATS), approve the investors allowed to hold it, and distribute it to them. ATS deploys the bond for you from audited, upgradeable contracts already on testnet: you deploy nothing.

Get the code

npx create-scaffold-hbar@latest --template hedera-dev/hedera-finance-templates#templates/ats-bond

Overview

A tokenised bond is a debt security recorded as a token: it has an ISIN, a face (nominal) value in a currency, a start date and a maturity date, and only investors who are allowed to hold it can receive it. Asset Tokenization Studio is Hedera’s open-source platform for digital securities. Its contracts are already deployed on testnet:
  • A factory creates each bond as a new Diamond (EIP-2535) proxy. One call to deployBond gives you a complete security: roles, control lists, maturity, nominal value, corporate-action support.
  • A Business Logic Resolver holds the shared, audited logic every bond points to, by configuration and version. Bonds get upgrades without being redeployed.
  • Each bond implements ERC-1400, the security-token standard: ERC-20 compatible, with issuance controlled by roles and transfers checked against compliance rules.
You deploy no contracts of your own. The learner’s only on-chain step is the factory call, and everything after it is a call on the bond. The workflow has three actors:
  • Issuer: the operator account the app runs with. Issues the bond through the factory and holds its admin, issuer and control-list roles
  • Investors: two demo investors the app creates, one you approve and one you leave unapproved
  • Wallet investor (optional): your own MetaMask account on Hedera testnet
The lifecycle:
  1. The Issuer asks the resolver for the bond configuration’s latest version
  2. The Issuer calls the factory’s deployBond with the terms, the allowlist switched on, and its own roles
  3. The Issuer approves investors by adding them to the bond’s control list
  4. The Issuer issues bonds to approved investors; issuing to an unapproved one is refused
  5. A wallet investor can hold bonds and transfer them, under the same allowlist
The app explains each step as it goes, shows what the network holds in a second column, and logs every action with its transaction hash.
The ATS SDK signs only through wallets (MetaMask, WalletConnect) or custody providers (Dfns, Fireblocks, AWS KMS). A server holding a plain key, as this template’s Issuer does, calls the ATS contracts directly over JSON-RPC instead.

Prerequisites

  • Node.js 20.18 or later, and Yarn
  • A Hedera testnet account from the Hedera Portal with an ECDSA key. It becomes the Issuer. JSON-RPC transactions are signed with ECDSA, so an ED25519 key cannot be used; the app says so at start.
  • Testnet HBAR on that account. Each Issue bond costs about 9.7 HBAR: about 7.4 for the bond deployment and about 1.1 for each of the two demo investors. Approving and issuing cost a fraction of an HBAR each.
  • Optional, for Hold and transfer with MetaMask: MetaMask with Hedera testnet (chain id 296) and an ECDSA testnet account holding a little HBAR for gas

Project structure

Configuration

Create packages/nextjs/.env.local:
Then start the app:
The ATS addresses live in services/ats/constants.ts: factory 0.0.9213391 and resolver 0.0.9212226, the v8 deployment on testnet. The app reads their EVM addresses from the mirror node at runtime.

Issue the bond

Fill in the issue form (name, symbol, ISIN, face value in USD, months to maturity, maximum supply) and press Issue bond. Every field starts with a value that works. The ISIN’s check digit is validated before anything is signed. The page shows each step as it runs: a spinner on the running step, then a check with what it produced.

Step 1: Resolve the bond configuration version

Every bond points to a configuration on the resolver, by id and version. The bond configuration’s id is 0x00…02. Its version must be looked up: passing 0 reverts with VersionZero.

Step 2: Call the factory

deployBond takes the security’s configuration and the bond’s terms. The Issuer grants itself three roles at deployment, so no extra transactions are needed later, and switches the control list to allowlist mode:
The deployment uses about 7.2 million gas. Hedera charges for at least 80% of a transaction’s gas limit, so keep limits close to what a call needs.
Do not keep the address simulateContract returns. A CREATE address is computed against the state at simulation time, and this factory is shared: another deployment landing in between changes it, and an app that trusts the prediction rejects its own successful deployment — abandoning a bond it has already paid 7.4 HBAR for. BondDeployed indexes only the deployer, so you cannot filter logs by the bond’s address, but bondAddress is in the event’s data, along with the bondData the bond was built with.
The published @hashgraph/asset-tokenization-contracts package does not include the factory’s ABI. The template writes the deployBond fragment by hand from the ATS Solidity source, in services/ats/abi.ts.
You should see the bond’s EVM address and contract id, both linked to HashScan.

Step 3: Read the bond back

Every term on the bond card is read from the contract, never echoed from the form: You should see the terms you entered, with the face value and maturity as stored on chain. The same step creates the two demo investors: a fresh ECDSA key each, receiving 0.5 HBAR from the Issuer, which makes Hedera auto-create the account. It stays hollow until it first signs, which a demo investor never does. Auto-creation is most of what an issue costs after the deployment itself: 607,858 gas against a plain transfer’s 21,000. Hence the template’s funding limit of 1,000,000, and about 1.1 HBAR per investor.
A failed read is shown as a failed read. Approval reads Unknown, balances and the issued total unavailable, and the bond keeps the terms read at issue. Never Not approved or 0: those are claims about the chain that a failed read has not made. Both Approve and Revoke stay enabled while approval is unknown, because the contract decides which applies.

Approve investors and distribute

Every write is simulated first: a plain eth_call with no signature and no HBAR spent. A refusal is caught and decoded to its ATS error name before a transaction is ever sent.

Step 1: Approve an investor

Press Approve on Investor A. The Issuer adds the investor’s EVM address to the bond’s control list. Because the list is in allowlist mode, being listed means being allowed to hold.
Revoke calls removeFromControlList the same way. You should see the investor marked Approved, and the action in the activity log with its transaction hash.

Step 2: Issue bonds

Enter an amount and press Issue. The Issuer mints that many bonds to the investor, up to the bond’s maximum supply:
You should see the investor’s balance and the bond’s total supply both rise by the amount, read from the contract.

Step 3: Try an unapproved investor

Issue to Investor B without approving it. The bond refuses with AccountIsBlocked(<address>). The app decodes the custom error from the revert and shows it by name:
You should see the refusal on the page — the decoded error in #action-error — and in the activity log, Investor B’s balance still 0, and the total supply unchanged. Approve Investor B and the same issue succeeds; revoke it and it is refused again.

Hold and transfer with MetaMask

ATS bonds are EVM contracts, so any EVM wallet can hold and transfer them. The header’s Connect Wallet offers MetaMask through RainbowKit (and WalletConnect).
  1. Connect MetaMask and switch it to Hedera testnet. The page says what to do next at every point.
  2. Press Add my wallet as an investor. Your wallet appears in the investors list as Your wallet.
  3. Approve it and issue bonds to it from its row, as for the demo investors.
  4. Transfer: enter a recipient EVM address and an amount. MetaMask signs the bond’s transfer call:
The allowlist applies to transfers too: to an approved recipient the transfer succeeds, and to an unapproved one the bond refuses it and the page says why.

Going to production

  • Real terms: a real ISIN, legal documentation, and a starting date that matches the offering.
  • KYC instead of a bare allowlist: ATS supports internal KYC with verifiable credentials, external KYC lists, and ERC-3643 identity, where investors carry reusable on-chain identities with claims from trusted issuers.
  • Custody for the Issuer’s roles: the ATS SDK signs through Dfns, Fireblocks or AWS KMS; hold the admin, issuer and control-list roles there rather than in an environment variable.
  • Separate roles: give issuing and approving to different accounts, so no single key both approves and mints.
  • Coupons and maturity: pay interest over the bond’s life and redeem it at maturity. Coupons are the next Finance Workflows template.

Next steps

Asset Tokenization Studio

What ATS is and what it supports.

ATS web UI

Issue and manage securities without code.

ATS FAQ

Standards, compliance and deployment questions.

Scaffold HBAR

The CLI and the other templates.