Building Event-Driven Workers
Now that you’ve created your first worker, let’s build something more powerful: workers that automatically respond to blockchain events.
What You’ll Build
Section titled “What You’ll Build”A worker that:
- Listens for token transfers on Ethereum
- Automatically logs the transaction details
- Can trigger actions on other blockchains
Prerequisites
Section titled “Prerequisites”- Complete the Getting Started guide
- nXCC node running locally (
./run.sh) - Basic understanding of blockchain events
Setting Up Event Listening
Section titled “Setting Up Event Listening”1. Review the Generated Event Handler
Section titled “1. Review the Generated Event Handler”Open workers/my-worker.ts in the project you scaffolded during Getting Started. The template already wires up HTTP, timer, and transfer handlers so you can jump straight to reacting to events. Focus on the generated handleTransfer implementation:
async handleTransfer(eventPayload: Record<string, unknown>, { userdata }: WorkerContext) { try { const { args: { from, to, value }, } = decodeEventLog({ abi: [transferEvent], topics: eventPayload.topics as [signature: Hex, ...args: Hex[]], data: eventPayload.data as Hex, });
const transactionHash = eventPayload.transaction_hash as Hex; const blockNumber = eventPayload.block_number as number;
console.log(`➡️ Transfer detected in block ${blockNumber}:`); console.log(` From: ${from}`); console.log(` To: ${to}`); console.log(` Amount: ${formatUnits(value, 6)} USDC`); console.log(` Tx: ${transactionHash}`); } catch (error) { console.error("Failed to decode transfer event", error, eventPayload); } },handleTransfer receives a decoded payload (including args, the transaction hash, and block number), so you can log details or trigger downstream actions immediately.
2. Start Anvil and Deploy the Contract
Section titled “2. Start Anvil and Deploy the Contract”If you don’t already have Foundry installed, set it up first:
curl -L https://foundry.paradigm.xyz | bash && foundryupRun a local Ethereum chain in a separate terminal:
anvilThen create and deploy a simple ERC-20 token that emits Transfer events:
cat > TestToken.sol <<'EOF'// SPDX-License-Identifier: MITpragma solidity ^0.8.0;
contract TestToken { mapping(address => uint256) public balanceOf;
event Transfer(address indexed from, address indexed to, uint256 value);
constructor() { balanceOf[msg.sender] = 1000000 * 10**18; }
function transfer(address to, uint256 amount) public { require(balanceOf[msg.sender] >= amount, "Insufficient balance"); balanceOf[msg.sender] -= amount; balanceOf[to] += amount; emit Transfer(msg.sender, to, amount); }}EOF
forge create TestToken.sol:TestToken \ --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \ --broadcastThe output should be similar to the following. The deployed contract address is in the highlighted line.
Deployer: 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266Deployed to: 0x5FbDB2315678afecb367f032d93F642f64180aa3Transaction hash: 0x2f241f20b76090179012a16f9060a223d793fd97939a31d03bbfd19d144534fcRecord the deployed contract address and replace YOUR_TOKEN_CONTRACT_ADDRESS in workers/manifest.template.json.
3. Configure the Event Subscription
Section titled “3. Configure the Event Subscription”Update workers/manifest.template.json to watch your local development chain. We override the gateways so that the nXCC node running in docker can access the anvil node running on the host. Replace the contract address 0x5FbDB2315678afecb367f032d93F642f64180aa3 with the one you just got, if it differs.
{ "handler": "handleTransfer", "kind": "web3_event", "chain": 31337, "address": ["0x5FbDB2315678afecb367f032d93F642f64180aa3"], "gateways": ["ws://host.docker.internal:8545"], "topics": [ ["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"] ]}4. Build and Deploy Locally
Section titled “4. Build and Deploy Locally”With Anvil running and the manifest pointing at your test token, build and deploy the worker. Choose whichever method you prefer to capture the worker ID so you can reference it later without copy/paste hunting:
npm run buildexport WORKER_ID=$(nxcc worker deploy --bundle workers/manifest.template.json | jq -r '.workOrderId')echo "Worker ID: $WORKER_ID"npm run buildDEPLOY_OUTPUT=$(nxcc worker deploy --bundle workers/manifest.template.json)echo "$DEPLOY_OUTPUT"After the command runs, copy the value from the workOrderId field and set it manually:
export WORKER_ID="paste-work-order-id-here"Trigger a transfer (swap in the address you recorded, if different from 0x5FbDB2315678afecb367f032d93F642f64180aa3) to watch the worker run:
cast send 0x5FbDB2315678afecb367f032d93F642f64180aa3 \ "transfer(address,uint256)" \ 0x70997970C51812dc3A010C7d01b50e0d17dc79C8 \ 1000000000000000000 \ --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80Then inspect the worker’s logs directly from the CLI:
nxcc worker logs "$WORKER_ID" --tail 50Tip: Use --follow to stream logs.
5. Try on Ethereum
Section titled “5. Try on Ethereum”When you’re ready to monitor mainnet activity, point the manifest at a contract such as USDC on Ethereum and redeploy your nXCC worker:
"chain": 31337,"address": ["0x5FbDB2315678afecb367f032d93F642f64180aa3"],"gateways": ["ws://host.docker.internal:8545"],"chain": 1,"address": ["0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"],Rebuild and run the deploy command again and watch mainnet transfers stream into the same handler.
Next Steps
Section titled “Next Steps”- Identities and Policies - Learn secure credential management
- Events Manifest Reference - Explore every event type and configuration option
Appendix: Multi-Chain Event Handling
Section titled “Appendix: Multi-Chain Event Handling”One of nXCC’s superpowers is handling events from multiple blockchains in a single worker:
{ "events": [ { "handler": "handleTransfer", "kind": "web3_event", "chain": 1, "address": ["0x..."], "topics": [["0x..."]] }, { "handler": "handleTransfer", "kind": "web3_event", "chain": 137, "address": ["0x..."], "topics": [["0x..."]] }, { "handler": "handleTransfer", "kind": "web3_event", "chain": 42161, "address": ["0x..."], "topics": [["0x..."]] } ]}Your worker will receive events from Ethereum, Polygon, and Arbitrum in the same handleTransfer function!
You’re now ready to build sophisticated event-driven applications that span multiple blockchains! 🚀