Skip to main content
Follow the token template file by file, from the contract to the web app, so you know what to change and what to leave alone.
Ready. This template is published in uzolabs/templates and has been deployed and verified on testnet. It’s a learning template and hasn’t been audited. Use test funds only.

Project layout

Project layout

The contract

contracts/UzoToken.sol combines three OpenZeppelin contracts: The constructor takes (name, symbol, initialSupply, owner) and mints the initial supply to the owner. The deploy scripts pass the deployer as the owner.

Tests

test/UzoToken.t.sol holds 18 Solidity tests. forge test and hardhat test solidity both run the same file, so you write each test once.
Terminal

Two toolchains, one project

foundry.toml and hardhat.config.ts both point at contracts/ and test/, and both compile with Solidity 0.8.28 for the cancun EVM. Foundry finds imports through remappings.txt. Hardhat finds them in node_modules. OpenZeppelin and forge-std come from npm, so there are no git submodules to update.

Deploying

scripts/deploy.ts does the same work for either toolchain:
  1. Reads the network from @uzolabs/sdk/chains and checks that the RPC reports the chain ID you asked for.
  2. Checks that your wallet has gas. On mainnet it also asks you to type MAINNET.
  3. Runs forge script script/Deploy.s.sol, or hardhat ignition deploy with --tool hardhat.
  4. Confirms there is code at the new address.
  5. Writes deployments/968.json, frontend/.env and frontend/src/abi.ts.
BOT Chain blocks have a base fee of 0. Hardhat Ignition reads that as free gas and would send a fee of 0, which the node rejects with “transaction underpriced”. scripts/lib/ignition-fees.ts is a Hardhat network hook, registered as the uzo-ignition-fees plugin, that fills in the fee the RPC asks for (20 gwei on testnet). Foundry gets this right on its own.

Verifying

scripts/verify.ts reads the address and constructor arguments from deployments/968.json and prints the command it runs. With Foundry:
Terminal
With Hardhat:
Terminal
BOTScan runs Blockscout, so neither command needs an API key. See Verify contracts.

Sending from the command line

Terminal
scripts/transfer.ts sends 25 tokens from your .env wallet and prints both balances before and after.

The web app

Each transaction shows “confirm in wallet”, then “pending” with a BOTScan link, then “confirmed” or a readable error. Status lines sit in an aria-live region so screen readers announce them. The app doesn’t subscribe to events, because BOT Chain’s public RPCs don’t serve eth_getLogs. See Events without getLogs.

Using a local node

Set VITE_RPC_URL in frontend/.env to point the web app at another RPC, such as anvil --chain-id 968. The batched reads go through Multicall3, which a fresh anvil doesn’t have. Copy its code from testnet with cast code and set it with anvil_setCode.

Next steps

Customize

Change the supply rules, add burning or a cap.

USDT and decimals

Why decimals matter when you work with amounts.
Last modified on October 2, 2026