Advanced Solidity examples for developing on Bool Network.
- Clone this repository
git clone https://github.com/boolnetwork/advanced-solidity-tutorials.git cd advanced-solidity-tutorials
- Add dependencies
yarn install
/contracts
: smart contracts./tasks
: extra Hardhat tasks to build omni-chain applications on Bool Network.hardhat.config.ts
: configuration file.
In order to prevent users to leak private keys, this project includes the dotenv package which is used to load environment variables. It's used to load the wallet private key, required to run the deploy script.
To use it, rename .env.example
to .env
and enter your private key. And do remember to fund your DEPLOYER_ADDRESS
in order to build!
TESTNET_DEPLOYER_PRIVATE_KEY=123cde574ccff....
TESTNET_DEPLOYER_ADDRESS=0x123cde574ccff....
In addition, set the RPC URLs of the networks you want to test with.
ETHEREUM_GOERLI_RPC_URL =
ZKSYNC_GOERLI_RPC_URL =
OPTIMISM_GOERLI_RPC_URL =
ARBITRUM_GOERLI_RPC_URL =
yarn hardhat compile
will compile the contracts.yarn hardhat compile --network zksync_goerli
will specifically compile the contracts for the zkSync Goerli network.
Arbitrary Message Transmission (AMT) bridges are a type of bridge defined by Bool Network that allows for the transfer of arbitrary data across multiple chains. This is in contrast to a token bridge, which only allows for the transfer of a specific token.
Each AMT bridge is defined by a group of Anchor
contracts, one on each chain. Each Anchor
contract is responsible for helping its uniquely binding Consumer
contract to interact with Bool Network by sending and receiving cross-chain messages.
The security of each Anchor
is preserved by a distinctive Dynamic Hidden Committee in Bool Network.
The diagram below visually depicts the complete lifecycle of a cross-chain transaction processed by Bool Network.
Consumer
in the diagram is instantiated by a liquidity pool for USDT, which also effectively illustrates the process of a cross-chain asset transfer conducted by our SWAP application, BoolSwap. The detailed process is as follows:
Source Step1
: When the sourcePool
receives a specific amount of the designated token, it generates a message including the informaiton of the amount of token and the address of the destination recipient, and sends it in the form of apayload
to its bindingAnchor
on the source chain.Source Step2
: The sourceAnchor
then sends a standardized message to the sourceMessenger
and triggers the standard cross-chain event that can be monitored by Bool NetworkOff-chain Step1
: TheCommitteee
binding to the destinationAnchor
will verify the finality of the source chain transaction and the validity of the message. If the verification is successful, theCommittee
will sign the message content with its private key.Off-chain Step2
: TheDeliverer
service (previously known asRelayer
) will collect thesignature
and the correspondingmessage
, and send them to the destinationMessenger
.Destination Step1
: The destinationMessenger
will verify the validity of the message versus the signature based on the public key stored on the destinationAnchor
, and then send the message to the destinationAnchor
.Destination Step2
: The destinationAnchor
then sends the message to the destinationPool
, which will release the designated amount of token to the destination recipient.
In this section, we provide an outline for developers to build an Arbitrary Message Transmission (AMT) bridge on Bool Network.
WARNING: You must build an AMT bridge before deploying an omni-chain application on Bool Network.
- Get tBOOL tokens from Bool Network Testnet Faucet.
- Open Bool Network Explorer (Testnet).
- Connect your MetaMask to Bool Network Testnet (configuration here).
- Create two
ECDSA
-type committees, one for each chain (link here). - Build an AMT bridge by deploying two
Anchor
contracts to two chains respectively (link here). - You should see your newly created AMT bridge under the
Dashboard/Bridge
tab (link here). An example is given as follows:
After completing the creation of an AMT bridge, there are still the following necessary steps to complete the development of an omni-chain dApp:
- Design your
Consumer
contract that should comply with our standards where theAnchor
address is cofigured as one of the constructor parameters (link here). - Deploy your
Consumer
contracts on each chain you intend to support. - Bind each
Consumer
contract to its correspondingAnchor
contract by calling theupdateConsumer
function on theAnchor
contract. - Update the remote
Anchor
addresses with remotechainIds
on eachAnchor
contract by calling thebatchUpdateRemoteAnchors
function on theAnchor
contract. - Send transactions by calling the cross-chain functions in your
Consumer
contract, e.g.deposit()
in the TokenBridge.sol, and track the lifecycle of the transaction via BoolScan.
Suggestion: For steps 3 and 4, you can use the updateConsumer
and updateRemoteAnchor
tasks provided by this repository.
In this section, we will introduce dApp developers on how to design a Consumer
contract that complies with the Bool Network standards.
Your Consumer
contract must inherit from ./contracts/bool/BoolConsumerBase.sol
Send logic
: an external payable function to call_sendAnchor
in order to send a cross-chain message to theAnchor
contract on the source chain.Receive logic
: implement thereceiveFromAnchor
function to receive cross-chain message. WARNING: you must use theonlyAnchor
modifier to restrict the access of this function to the bindingAnchor
contract.
Encode & Decode logic
: implement theencodePayload
anddecodePayload
functions to encode and decode the message content. Suggestions: we recommend set the visibility of these two functions aspublic
.Calculate fee
: implement thecalculateFee
function to calculate the fee for sending a cross-chain message based on your payload. This functin can provide convenience for the front-end by directly calculating the fee from yourConsumer
contract before initiating a cross-chain transaction.
WARNING: TokenBridge is designed for testing purposes only.
This section provides an outline for deploying a burn & mint ERC20 token bridge on Bool Network.
Deploy an AMT Bridge (Build an AMT Bridge) and get two Anchor
addresses through BoolScan/Dashboard/Bridge/<your bridge>
. For example:
0xe59a9ab6a5732d5f7a89d9c7f31964c459456f70
on Arbitrum Goerli0x7eb25a4ab45e29c9306a1987c664111bf7ebd002
on zkSync Goerli
WARNING: You must deploy an AMT bridge through BoolScan before building a TokenBridge.
- Deploy two TokenBridge contracts.
Format: yarn hardhat deployTokenBridge --anchor <anchor address> --decimals <decimals> --name <token name> --symbol <token symbol> --network <network name>
yarn hardhat deployTokenBridge --anchor 0xe59a9ab6a5732d5f7a89d9c7f31964c459456f70 --decimals 9 --name BoolTestToken --symbol BTT --network arbitrum_goerli Output: Deploying TokenBridge... Deploying a new TokenBridge contract on chain 421613... TokenBridge deployed at 0x565D09b0cd1c8B7Ca4846c06cc9Ec4a92a01012d
yarn hardhat deployTokenBridge --anchor 0x7eb25a4ab45e29c9306a1987c664111bf7ebd002 --decimals 9 --name BoolTestToken --symbol BTT --network zksync_goerli Output: Deploying TokenBridge... Deploying a new TokenBridge contract on chain 280... TokenBridge deployed at 0x281b5702012654065733A0b763e2F3494663968b
- Binding each TokenBridge to an Anchor.
Format: yarn hardhat updateConsumer --anchor <anchor address> --consumer <tokenBridge address> --network <network name>
yarn hardhat updateConsumer --anchor 0xe59a9ab6a5732d5f7a89d9c7f31964c459456f70 --consumer 0x565D09b0cd1c8B7Ca4846c06cc9Ec4a92a01012d --network arbitrum_goerli Output: The current consumer: 0x0000000000000000000000000000000000000000 Updating the consumer... Transaction hash: 0x9bb73e827f490d7863bb23696ee55c8f0ce9395a64c1b67edc1b7fb1127446a5 The new consumer: 0x565D09b0cd1c8B7Ca4846c06cc9Ec4a92a01012d
yarn hardhat updateConsumer --anchor 0x7eb25a4ab45e29c9306a1987c664111bf7ebd002 --consumer 0x281b5702012654065733A0b763e2F3494663968b --network zksync_goerli Output: The current consumer: 0x0000000000000000000000000000000000000000 Updating the consumer... Transaction hash: 0x139d4589783d96a36d7a40748a7e090e844feb131b007850a4a3ed9ca06bb673 The new consumer: 0x281b5702012654065733A0b763e2F3494663968bb
- Configure the "remote anchors" so each of them can receive messages from one another, and
only
one another on a specific chain.Format: yarn hardhat updateRemoteAnchor --anchor <anchor address> --id <remote chain id> --remoteanchor <remote anchor address in bytes32> --network <network name>
yarn hardhat updateRemoteAnchor --anchor 0xe59a9ab6a5732d5f7a89d9c7f31964c459456f70 --id 280 --remoteanchor 0x0000000000000000000000007eb25a4ab45e29c9306a1987c664111bf7ebd002 --network arbitrum_goerli Output: Updating the remote anchors... Transaction hash: 0x3ef9850d957763db1eb9a0fc3526ba6a70f67e64b547a7569e87102314629854
yarn hardhat updateRemoteAnchor --anchor 0x7eb25a4ab45e29c9306a1987c664111bf7ebd002 --id 421613 --remoteanchor 0x000000000000000000000000e59a9ab6a5732d5f7a89d9c7f31964c459456f70 --network zksync_goerli Output: Updating the remote anchors... Transaction hash: 0x46660f869763dedb4a3697cb08fbb51b666bef6204e3c7eabd2c66481ae34e56
- Deposit tokens on Arbitrum Goerli and receive them on zkSync Goerli.
Format: yarn hardhat tokenBridgeDeposit --amount <deposit amount> --bridge <tokenBridge address> --id <destination chain id> --network <network name>
yarn hardhat tokenBridgeDeposit --amount 1000000000 --bridge 0x565D09b0cd1c8B7Ca4846c06cc9Ec4a92a01012d --id 280 --network arbitrum_goerli Output: Depositing 1.0 BTT to chain 280 Transaction hash: 0x4de93d3f6ae3d301e0ceffbee3e87203db98aa8017e2f66321f547d4431a5d32
- Track the lifecycle of the transaction on BoolScan.