Bridge Offchain Worker
September 16, 2025 ยท View on GitHub
Tip
This tutorial builds on the previous Custom Contract Bridging example -make sure to check it out before moving on!
So far we have seen how to initiate a trustless bridge transaction and how to customize our trustless bridging logic. In this tutorial, we will be seeing how to automate our interaction with the Creditcoin oracle so that end users only have to submit a single transaction on the Sepolia source chain.
What is an Offchain Worker
An Offchain Worker is an script responsible for watching the state of a source chain (in this case, Sepolia) as well as the Creditcoin execution chain. It submits oracle queries and interacts with our Universal Smart Contract on Creditcoin in response to specific events on each chain. With an offchain worker, all the end user needs to do is sign a single transaction on the source chain kicking off cross-chain interaction.
Our offchain worker will listen to events from the following sources:
-
burntransfer events which are emitted by our testERC20contract on Sepolia. When our worker detects this, they create an oracle query to request a proof of the token burn from the Creditcoin Decentralized Oracle. -
queryProofVerifiedevents which are emitted by the prover contract on Creditcoin. Each of these events signals that a query has finished being proven by the Creditcoin Decentralized Oracle. When our worker detects this, it submits a request to our bridge proxy contract to finalize the bridging process by minting the tokens we burned on Sepolia onto Creditcoin. -
mintevents which are emitted by our bridge proxy contract. This signals that all bridged tokens have finished minting and we can drop any local records related to the bridged transaction.
External dependencies
To continue with this tutorial, you will first need to have the following dependencies available locally:
Tip
This project provides a flake.nix you can use to download all the dependencies you will need for
this tutorial inside of a sandboxed environment. Just keep in mind you will have to
enable flakes for this to work. To start you development environment, simply run:
nix develop
Start by heading to the bridge-offchain-worker folder:
cd bridge-offchain-worker
You will need to set up the right version of foundry with foundryup:
foundryup --version v1.2.3 # Skip this command if you are using nix!
And download the required packages with yarn:
yarn
1. Setup
This is the same as in Hello Bridge. If you have not already done so, follow the installation steps in the setup section there.
Once that is done, you will need to set up some additional configuration for the offchain worker.
Save and edit the following to a .env file inside of bridge-offchain-worker/:
# ============================================================================ #
# Source Chain Configuration #
# ============================================================================ #
# The starting block number to begin listening for burn events. If not provided,
# the worker will get the latest source chain block number
# SOURCE_CHAIN_INITIAL_START_BLOCK=<block_number>
# Number of blocks to wait before processing events
SOURCE_CHAIN_BLOCK_LAG=3
# Address of the ERC20 token contract on source chain
SOURCE_CHAIN_CONTRACT_ADDRESS=<test_erc20_contract_address_from_custom_contracts_bridging>
# RPC endpoint for the source chain. Following our previous example, this will
# be the Sepolia urls
SOURCE_CHAIN_RPC_URL=https://sepolia.infura.io/v3/<your_infura_api_key>
# ============================================================================ #
# Creditcoin USC Chain Configuration #
# ============================================================================ #
# The starting block number to begin listening for prover events. If not
# provided, the worker will get the latest USC Testnet block number
#USC_TESTNET_INITIAL_START_BLOCK=<block_number>
# Number of blocks to wait before processing
USC_TESTNET_BLOCK_LAG=3
# RPC endpoint for the Creditcoin USC chain
USC_TESTNET_RPC_URL=https://rpc.usc-testnet.creditcoin.network
# Address of the mintable ERC20 token on Creditcoin USC chain
USC_TESTNET_ERC20_MINTABLE_ADDRESS=<erc20_mintable_address_from_custom_contracts_bridging>
# Private key of the wallet that will submit mint requests
USC_TESTNET_WALLET_PRIVATE_KEY=<your_private_key>
# ============================================================================ #
# Contract Addresses #
# ============================================================================ #
# Address of the prover contract on Creditcoin USC chain
PROVER_CONTRACT_ADDRESS=0xc43402c66e88f38a5aa6e35113b310e1c19571d4
# Address of the proxy bridge contract on Creditcoin USC chain
USC_BRIDGE_CONTRACT_ADDRESS=<universal_bridge_proxy_address_from_custom_contracts_bridging>
# ============================================================================ #
# Block Processing Configuration #
# ============================================================================ #
# Maximum number of blocks to process in each worker run
MAX_BLOCK_RANGE=2000
Caution
If you configure SOURCE_CHAIN_INITIAL_START_BLOCK and USC_TESTNET_INITIAL_START_BLOCK to point
to blocks in the past, chances are you're going to re-query transactions and events that have
already been processed. The issue with the example worker we are using is that it will always
build and submit queries as if this was the first time they are being submitted. This will fail
on the prover since it doesn't allow query resubmissions. The bridge proxy contract acts
similarly, in that it will revert mint calls for any query id that has been already been
submitted.
2. Start the Offchain Worker
Once you have your worker configured, it's time to start automating some queries!
Run the following command to start the worker:
yarn start_worker
Once it's up and running, you start to see the following logs:
Starting...
Worker job run 1
Source chain listener is listening from block 8827111 to 8827396
Creditcoin chain listener is listening from block 1298785 to 1299490
Worker job run 2
...
Tip
The prover can take a bit of time to get started. Sit back and wait until you get the full log output as shown above โ
3. Burning the tokens you want to bridge
Like we did in the previous tutorials, we start the bridging process by burning the funds we want to bridge on Sepolia. This time however this will be the only transaction we need to submit! The rest will be handled automatically by the worker ๐ค
Run the following command to initiate the burn:
cast send --rpc-url https://sepolia.infura.io/v3/<your_infura_api_key> \
<test_erc20_contract_address_from_custom_contracts_bridging> \
"burn(uint256)" 50000000000000000000 \
--private-key <your_private_key>
Tip
It can take some time for the worker to pick up your transaction. Pay attention to your
transaction's blockNumber: the prover will pick it up when that block number falls into the
range of blocks it is listening to on the source chain โ
4. Monitor the Offchain Worker
At this point, you should see the worker start to process some events and requesting a proof of your token burn from the Creditcoin Decentralized Oracle:
...
Source chain listener is listening from block 9159981 to 9159985
Found 2 new burn transaction events
Skipping non-burn transfer: tx=0xa6d0aa852fb1cead707be7fae0c6c3678dd5dcb33ec09327606d53079a8e3039, from=0x0000000000000000000000000000000000000000, to=0x475CFf3D6728B0BaEdDd65d863DD7E82a43367ee, value=50000
Query cost: 10000000000000000000 for query 0x45778684817c53de254036b8bfe975dd16b93a36f320c7de3f88ae107bf8a2b0
Query submitted to the Creditcoin oracle: 0x3aa3b0e4f99524f087bb0257b339f3ed1ff7d8ca9ea0c03660577468911e26ae
...
The worker then waits for the prover contract to emit a QueryProofVerified event notifying it that
the query has been processed. It then queries the bridging proxy contract on Creditcoin to initate
the minting process:
...
Creditcoin USC chain listener is listening from block 69063 to 69066
Found 1 new prover contract result events
Caught the query proof verified event: 0x45778684817c53de254036b8bfe975dd16b93a36f320c7de3f88ae107bf8a2b0
Value return in event: {"eventName":"QueryProofVerified","args":{"queryId":"0x45778684817c53de254036b8bfe975dd16b93a36f320c7de3f88ae107bf8a2b0","resultSegments":[{"offset":"448","abiBytes":"0x0000000000000000000000000000000000000000000000000000000000000001"},{"offset":"192","abiBytes":"0x000000000000000000000000475cff3d6728b0baeddd65d863dd7e82a43367ee"},{"offset":"224","abiBytes":"0x00000000000000000000000015166ba9d24abfa477c0c88dd1e6321297214ec8"},{"offset":"800","abiBytes":"0x00000000000000000000000015166ba9d24abfa477c0c88dd1e6321297214ec8"},{"offset":"928","abiBytes":"0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"},{"offset":"960","abiBytes":"0x000000000000000000000000475cff3d6728b0baeddd65d863dd7e82a43367ee"},{"offset":"992","abiBytes":"0x0000000000000000000000000000000000000000000000000000000000000001"},{"offset":"1056","abiBytes":"0x0000000000000000000000000000000000000000000000000000000000000032"}],"state":2}}
Transaction submitted to the Creditcoin bridge USC: 0x30a57a9f51a36d954e4ae4939589ad8298bdd54d1d69a02485918bfc54d4828e
...
Finally, the worker listens for the mint event notifying it that the tokens have been minted to
our account on Creditcoin.
...
Found 1 new bridge USC events
Caught the tokens minted event: 0x45778684817c53de254036b8bfe975dd16b93a36f320c7de3f88ae107bf8a2b0
Value return in event: {"eventName":"TokensMinted","args":{"token":"0xb0fb0b182f774266b1c7183535A41D69255937a3","recipient":"0x475CFf3D6728B0BaEdDd65d863DD7E82a43367ee","queryId":"0x45778684817c53de254036b8bfe975dd16b93a36f320c7de3f88ae107bf8a2b0","amount":"50"}}
Congratulations! You've successfully bridged tokens from your source chain to your Creditcoin chain!
...
That's it! All it took was a single transaction on your end to initiate the bridging process, providing for a truly native UX.
5. Check Balance in USC Testnet ERC20 Contract
As a final check, we can take a look at the balance of your account on Creditcoin to confirm that the bridging process was successful.
Run the following command to check your funds:
yarn check_balance \
<erc20_mintable_address_from_custom_contracts_bridging> \
<your_wallet_address>
If you've been going through the previous tutorials, your balance should now be:
๐ฆ Token: Mintable (TEST)
๐งพ Raw Balance: 200000000000000000000
๐ฐ Formatted Balance: 200 TEST
Conclusion
Congratulations! You've completed the Creditcoin Universal Smart Contracts tutorial series! You've learned:
- How to interact with the Creditcoin Oracle
- How to deploy your own custom Universal Smart Contracts
- How to run an offchain worker to support smooth cross-chain user experience
If you haven't already, take a look at the USC Gitbook for more information.