Skip to main content
Point the gasless app template at your own contract, tune what the relayer will pay for, and host the relayer for real users.
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.

What you’ll build

A relayer that pays gas for the functions you choose on your own contract, with limits that fit your budget. Start with the settings, then change the contract and the relayer’s allowlist together.

Prerequisites

Rate limits and gas cap

These need no code changes. Set them in .env and restart the relayer:
.env
The limiter keeps counts in memory. If you run more than one relayer instance, replace the RateLimiter in relayer/src/rate-limit.ts with one backed by a shared store, or each instance counts separately.

Relay your own contract

1

Make the contract trust the forwarder

Extend ERC2771Context, pass the forwarder’s address to the constructor, and use _msgSender() wherever you’d use msg.sender:
contracts/Counter.sol
A single msg.sender left in your contract would record the relayer instead of the user.
2

Deploy it with the forwarder

Update script/Deploy.s.sol, ignition/modules/GuestBook.ts and scripts/deploy.ts to deploy your contract after UzoForwarder, passing the forwarder’s address. Keep writing both addresses to deployments/968.json.
3

Change the relayer's allowlist

In relayer/src/forward.ts, the relayer refuses everything except one target and one function:
relayer/src/forward.ts
Change the selector to your function, such as toFunctionSelector("increment()"), and point the target check at your contract. Replace the message length checks below it with checks that suit your function’s arguments.Allow only the functions you’re willing to pay for. Every allowed function is one anyone can call at your expense, up to the rate limits.
4

Test, refresh the ABI and redeploy

Terminal
Restart the relayer and the web app after the deploy, because both read the addresses when they start.

Message length

To change the 280 byte limit, change all three together: The contract is the final check. The other two only give a clearer error sooner.

Who may use it

To limit who can use your relayer, add a check in relayer/src/app.ts before it sends, such as an allowlist of signer addresses or a login.

Host the relayer

  1. Run npm run relayer on a server with its own .env and a relayer wallet funded with only what you’re willing to spend.
  2. Put it behind HTTPS.
  3. Set RELAYER_ALLOWED_ORIGINS to your site’s address.
  4. Set VITE_RELAYER_URL in frontend/.env to the relayer’s public URL, then rebuild the web app.
Set RELAYER_TRUST_PROXY=true only if the relayer runs behind your own reverse proxy.
On mainnet the relayer spends real BOT on every message, so anyone who can reach it can cost you money, up to the rate limits. The template hasn’t been audited. Deploy with npm run deploy -- --mainnet only after a review, and use separate mainnet wallets.

Change the look

Edit the variables at the top of frontend/src/theme.css for colours and fonts, and frontend/src/styles.css for this app’s own styles.

Verify

With the relayer running, sign from a wallet holding 0 tBOT. On BOTScan, the transaction’s sender is the relayer, and your contract records the signer. For the counter above, counts(<signer>) on the Read contract tab should go up by one.

Troubleshooting

The target check still points at the guest book, or frontend/.env and deployments/968.json point at different deploys. Update the check, redeploy, and restart the relayer and the web app.
Somewhere it uses msg.sender instead of _msgSender(), or it was deployed with a different forwarder address.
The request was already used, signed for an old nonce, or is more than 10 minutes old. Sign again. After a new deploy, restart the relayer so it uses the new forwarder.
Check that it’s running and that VITE_RELAYER_URL matches its port. A CORS error in the browser console means the page’s address is missing from RELAYER_ALLOWED_ORIGINS.
The signer or IP hit the rate limit. Wait, or raise RELAYER_RATE_LIMIT and restart the relayer.
Its balance is below RELAYER_LOW_BALANCE. Send it tBOT from the faucet.

Next steps

Run a relayer

Operating a relayer safely.

EOA paymaster

BOT Chain’s paymaster flow.
Last modified on October 3, 2026