Get the code
npx create-scaffold-hbar@latest --template hedera-dev/hedera-finance-templates#templates/ats-bondOverview
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
deployBondgives 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.
- 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 Issuer asks the resolver for the bond configuration’s latest version
- The Issuer calls the factory’s
deployBondwith the terms, the allowlist switched on, and its own roles - The Issuer approves investors by adding them to the bond’s control list
- The Issuer issues bonds to approved investors; issuing to an unapproved one is refused
- A wallet investor can hold bonds and transfer them, under the same allowlist
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
Createpackages/nextjs/.env.local:
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 is0x00…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:
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 plaineth_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.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:Step 3: Try an unapproved investor
Issue to Investor B without approving it. The bond refuses withAccountIsBlocked(<address>). The app decodes the custom error from the revert and shows it by name:
#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).- Connect MetaMask and switch it to Hedera testnet. The page says what to do next at every point.
- Press Add my wallet as an investor. Your wallet appears in the investors list as Your wallet.
- Approve it and issue bonds to it from its row, as for the demo investors.
-
Transfer: enter a recipient EVM address and an amount. MetaMask signs the bond’s
transfercall:
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.