Counter contract you deployed in the quickstart, first from a Node.js script and then from a web page where anyone with a wallet can increment it.
What you’ll build
- A script that reads the counter and sends an
incrementtransaction with viem. - A React page built with wagmi that connects a browser wallet, shows the counter and has an Increment button.
defineChain. That way you see every value your app depends on. In your own projects you can import botChainTestnet from the Uzo SDK instead.
Prerequisites
- A deployed
Countercontract and its address, from the quickstart. - The
uzo-devwallet from the quickstart, with some tBOT in it. - Node.js 20.6 or later. This guide was tested with Node.js 24, viem 2.57 and wagmi 3.7.
- For part 2, a browser wallet such as MetaMask with BOT Chain testnet added. See Connect a wallet.
Part 1: a viem script
Create the project
Define the chain
multicall3 address is the one BOT Chain deploys on both networks. Viem uses it to batch reads. The usual Multicall3 address used on other chains is not deployed on testnet, so don’t skip this field.Add the contract ABI
out/Counter.sol/Counter.json in your quickstart project. For this guide you only need these three functions:Set your environment variables
.env file, so they never appear in your code.Export the private key of your uzo-dev testnet wallet. Cast asks for the keystore password:.env and paste in your values:Read the counter
--env-file flag loads your .env file:n means the value is a JavaScript BigInt.Send a transaction
Part 2: a wagmi page
Now give the contract a user interface. Visitors connect their own wallet, so this app needs no private key.Create a React app
Configure wagmi
chain.js) and creates the wagmi config. The injected connector works with MetaMask and other browser wallets.Add the contract
VITE_ to your frontend. A contract address is public, so it’s safe here.as const lets TypeScript check your function names and argument types.Wrap the app in providers
src/main.tsx:Build the page
src/App.tsx:injected connector from wagmi.ts only shows, as Connect wallet, when no wallet announces itself.
src/App.css to style the page:Run it
http://localhost:5173.Verify it worked

- The page shows the same counter value that
read.jsprinted. - Click Connect with your wallet’s name, then approve in your wallet.
- Click Increment and confirm. After about a second the value goes up by one.
- The View transaction on BOTScan link opens your transaction.
Troubleshooting
InvalidAddressError or Address "undefined" is invalid
InvalidAddressError or Address "undefined" is invalid
COUNTER_ADDRESS. Run it with node --env-file=.env, and check that .env is in the folder you’re running from. In the React app, restart npm run dev after creating .env.local, and check that the name starts with VITE_.The contract function "number" returned no data ("0x")
The contract function "number" returned no data ("0x")
Counter contract at that address on testnet. Check that you copied the Deployed to address from the quickstart, not your wallet address or the transaction hash.invalid private key, expected hex or 32 bytes
invalid private key, expected hex or 32 bytes
PRIVATE_KEY must be 64 hex characters with a 0x prefix. Copy it again from cast wallet decrypt-keystore uzo-dev.Clicking Connect wallet does nothing
Clicking Connect wallet does nothing
injected connector, which needs a browser wallet extension. Without one, the button still appears but nothing happens when you click it. Install MetaMask or another EVM wallet and reload the page.The wallet shows the wrong network or a chain mismatch warning
The wallet shows the wrong network or a chain mismatch warning