Solidity code and scripts for deploying and interacting with MintContract and BurnContract on Sepolia network.
The first things you need to do are cloning this repository and installing its dependencies (you may need to nvm use version > 16):
npm install
Compile smartcontracts
npx hardhat compile
Now you can run script in the scripts folder using:
npx hardhat run scripts/script-name.js --network sepolia
Contract | Address |
---|---|
sBTC | 0xa32e5903815476Aff6E784F5644b1E0e3eE2081B |
AxelarGateway | 0xd70943944567979d99800DD14b441B1D3A601A1D |
GasService | 0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6 |
AxelarAuthWeighted | 0x71b7B290B14D7A8EB8071e35e3457b192b4a7fB6 |
MintContract | 0x768E8De8cf0c7747D41f75F83C914a19C5921Cf3 |
BurnContract | 0x44dD1420Af56740ACeb697538227A9A787067786 |
Scripts to deploy smart contract usually begin with this pattern:
const gatewayAddress = "0x1811AE0B97479b77258CF8aAda7768aB74e21aE9"; // Params passed to constructor of BurnContract
const gasServiceAddress = "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6"; // Params passed to constructor of BurnContract
const sbtcAddress = "0xa32e5903815476Aff6E784F5644b1E0e3eE2081B"; // Params passed to constructor of BurnContract
const BurnContract = await ethers.getContractFactory("BurnContract");
const burnContract = await BurnContract.deploy(
gatewayAddress,
gasServiceAddress,
sbtcAddress
);
await burnContract.deployed();
At the end of the deployment script, the contract abi and address will be saved to the abis
folder.
saveABI([
{
name: "BurnContract",
address: burnContract.address,
},
]);
Scripts to test or interact with smart contract usually begin with this pattern:
const contractName = "BurnContract";
const contractArtifact = require(`../artifacts/contracts/${contractName}.sol/${contractName}.json`);
const contractABI = contractArtifact.abi;
const burnContract = new ethers.Contract(
"0x6F111e169710C6c3a33948c867aE425A74cDa1a3", // TODO: update BurnContract address
contractABI,
signer
);
This code is used to get the contract instance by contract ABI and address.
When running any scripts, check the contract address and update it if necessary.
When calling get methods of a contract (public attributes or view functions), you just need to:
const result = await burnContract.getSomeValue();
However, when calling set methods of a contract (functions that change the state of the contract), you need to sign the transaction before sending it:
const tx = await burnContract.setSomeValue(newValue);
await tx.wait();
In order to make the ethereum-side of the bridge work, we need to deploy these main contracts:
AxelarGateway
: This contract is the main contract that will be used to interact with the Axelar network. The constructor of this contract take the address of theAxelarAuthWeighted
contract as a parameter, so that we need to deploy theAxelarAuthWeighted
contract first.AxelarAuthWeighted
: This contract is used to manage the operatorship of the Axelar network. It also define logic to validate the signatures of messages receiving from the Axelar network.MintContract
: This contract implement theAxelarExecutable
interface. The constructor of this contract take the address of theAxelarGateway
,GasServices
andsBTC
contracts as parameters. However, theGasServices
contract is not used in the our implementation, so basically we can pass the address of contract deployed by Axelar team (e.g.0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6
on Sepolia).BurnContract
: Deployment of this contract is similar toMintContract
.
-
BTC -> Axelar stuff
-
Relayer listen and handle the ContractCallApproved event from Axelar network. In this step, relayer will generate
executeData
from batch commands and callexecute()
function ofAxelarGateway
contract.const executeData = await vxClient.getExecuteDataFromBatchCommands( event.args.destinationChain, batchedCommandId ); logger.info( `[handleCosmosToEvmApprovedEvent] BatchCommands: ${JSON.stringify( executeData )}` ); const tx = await evmClient.gatewayExecute(executeData);
-
In this
execute()
function, theinput
first will be decoded todata
part andproof
part. These values then will be validated by thevalidateProof()
function ofAxelarAuthWeighted
contract. -
If the proof is valid, the gateway contract will process commands in the
data
part. In this case, it will call itsapproveContractCall()
function and emitContractCallApproved
event, indicating that the Minting call is approved to be executed. -
Relayer will listen to this event and call the
execute()
function of theMintContract
contract.- The
execute()
function is actually theexecute()
function of theAxelarExecutable
interface. It first check if the Minting call is approved and then call the_execute()
function ofMintContract
. - This
_execute()
will then callsBTC
contract to mint the token.
- The
-
To unbonding Bitcoin transaction, the staker first need to burn their sBTC token on Ethereum network.
-
First, the staker need to approve the
BurnContract
to burn their sBTC token. This is done by calling theapprove()
function of thesBTC
contract.const tx = await sBTC.approve(burnContract.address, amountToBurn); await tx.wait();
-
Then, the staker can call the
callBurn()
function of theBurnContract
contract with necessary parameters.const txCallBurn = await burnContract.callBurn( destinationChain, destinationAddress, amountToBurn, btcPsbtB64 ); await txCallBurn.wait();
-
This
callBurn()
function will then callsBTC
contract to burn the token and call thecallContract()
function of theAxelarGateway
contract to emitContractCall
event for relayer to listen and handle.
- If you want to create another contract for GMP in Axelar network, you can follow the same pattern as
MintContract
andBurnContract
. The contract need to implement theAxelarExecutable
interface with the same constructor. - If your contract execute logic in the BTC -> EVM direction, write your logic in the
_execute()
function such asMintContract
. - If your contract execute logic in the EVM -> BTC direction, write your logic in the
callContract()
function such asBurnContract
. - For more details, please refer to this example.
Currently, the ownership of the sBTC
contract is set to the MintContract
. This is to ensure that only the MintContract
can mint the token. If you want to change the ownership, you can call the transferOwnership()
function of the sBTC
contract (using the deployer account). Details can be found in the scripts/setOwnership.js
.
The operatorship of the Axelar network is managed by the AxelarAuthWeighted
contract. The operator can be added by calling the transferOperatorship()
function of this contract. Details can be found in the scripts/transferOperatorShip.js
.
To deploy all contract in once time.
node scripts/deployAll.js deploy <target> --network <string> --newSbtc <bool> --newAxelarGateway <bool>
- target: choice "All, AxelarGateway, MintContract, BurnContract"
- network: network name < ethereum-sepolia, ethereum-local>
- --newSbtc: a parameter to specify whether to deploy a new contract sBtc
- --newAxelarGateway: a parameter to specify whether to deploy a new contract AxelarGateway when deploying Mint/Burn Contract
- --AxelarGatewayAddress: input AxelarGateway address when newAxelarGateway is false