From 53b7c4d2da4ba6896387e694708cf87c28c8ea38 Mon Sep 17 00:00:00 2001 From: David Ansermino Date: Fri, 26 Jun 2020 13:33:34 -0400 Subject: [PATCH] CLI Additions (#8) --- cb-sol-cli/Makefile | 4 +-- cb-sol-cli/cmd/bridge.js | 48 ++++++++++++++++++------------ cb-sol-cli/cmd/erc20.js | 44 ++++++++++++++++++++++++++-- cb-sol-cli/cmd/erc721.js | 45 +++++++++++++++++++++++++---- cb-sol-cli/constants.js | 4 ++- cb-sol-cli/docs/bridge.md | 31 +++++++++++++------- cb-sol-cli/docs/erc20.md | 20 +++++++++++++ cb-sol-cli/docs/erc721.md | 11 +++++++ ci/ci_cli.sh | 61 ++++++++++++++++++++++----------------- 9 files changed, 201 insertions(+), 67 deletions(-) diff --git a/cb-sol-cli/Makefile b/cb-sol-cli/Makefile index 822d1d2..9cbbb77 100644 --- a/cb-sol-cli/Makefile +++ b/cb-sol-cli/Makefile @@ -1,9 +1,9 @@ SOL_URL=https://github.com/ChainSafe/chainbridge-solidity - +SOL_VERSION="v0.0.2-alpha" fetch-contracts: @echo " > \033[32mFetching chainbridge-solidity contracts... \033[0m " - git clone ${SOL_URL} && cd chainbridge-solidity && git checkout ${GIT_COMMIT} + git clone ${SOL_URL} && cd chainbridge-solidity && git checkout ${SOL_VERSION} compile: cd chainbridge-solidity && npm install && npx truffle compile diff --git a/cb-sol-cli/cmd/bridge.js b/cb-sol-cli/cmd/bridge.js index 1465139..acf3547 100644 --- a/cb-sol-cli/cmd/bridge.js +++ b/cb-sol-cli/cmd/bridge.js @@ -59,23 +59,6 @@ const setBurnCmd = new Command("set-burn") await waitForTx(args.provider, tx.hash) }) -const queryProposalCmd = new Command("query-proposal") - .description("Query a proposal on-chain") - .option('--bridge
', 'Bridge contract address', constants.BRIDGE_ADDRESS) - .option('--depositNonce
', 'Nonce of proposal', 0) - .option('--chainId ', 'Source chain ID of proposal', constants.DEFAULT_SOURCE_ID) - .action(async function (args) { - await setupParentArgs(args, args.parent.parent) - - // Instances - const bridgeInstance = new ethers.Contract(args.bridge, constants.ContractABIs.Bridge.abi, args.wallet); - - const prop = await bridgeInstance.getProposal(args.chainId, args.depositNonce) - log(args, `Source: ${args.chainId} Nonce: ${args.depositNonce}`) - log(args, `Votes: ${prop._yesVotes} Status: ${prop._status}`) - }) - - const cancelProposalCmd = new Command("cancel-proposal") .description("Cancel a proposal that has passed the expiry threshold") .option('--bridge
', 'Bridge contract address', constants.BRIDGE_ADDRESS) @@ -90,12 +73,41 @@ const cancelProposalCmd = new Command("cancel-proposal") await waitForTx(args.provider, tx.hash) }) +const queryProposalCmd = new Command("query-proposal") + .description("Queries a proposal") + .option('--bridge
', 'Bridge contract address', constants.BRIDGE_ADDRESS) + .option('--chainId ', 'Source chain ID of proposal', 0) + .option('--depositNonce ', 'Deposit nonce of proposal', 0) + .option('--dataHash ', 'Hash of proposal metadata', constants.ERC20_PROPOSAL_HASH) + .action(async function (args) { + await setupParentArgs(args, args.parent.parent) + const bridgeInstance = new ethers.Contract(args.bridge, constants.ContractABIs.Bridge.abi, args.wallet); + + const prop = await bridgeInstance.getProposal(args.chainId, args.depositNonce, args.dataHash) + + console.log(prop) + }) + +const queryResourceId = new Command("query-resource") + .description("Query the contract address associated with a resource ID") + .option('--handler
', 'Handler contract address', constants.ERC20_HANDLER_ADDRESS) + .option('--resourceId
', `ResourceID to query`, constants.ERC20_RESOURCEID) + .action(async function(args) { + await setupParentArgs(args, args.parent.parent) + + const handlerInstance = new ethers.Contract(args.handler, constants.ContractABIs.HandlerHelpers.abi, args.wallet) + const address = await handlerInstance._resourceIDToTokenContractAddress(args.resourceId) + log(args, `Resource ID ${args.resourceId} is mapped to contract ${address}`) + }) + + const bridgeCmd = new Command("bridge") bridgeCmd.addCommand(registerResourceCmd) bridgeCmd.addCommand(registerGenericResourceCmd) bridgeCmd.addCommand(setBurnCmd) -bridgeCmd.addCommand(queryProposalCmd) bridgeCmd.addCommand(cancelProposalCmd) +bridgeCmd.addCommand(queryProposalCmd) +bridgeCmd.addCommand(queryResourceId) module.exports = bridgeCmd diff --git a/cb-sol-cli/cmd/erc20.js b/cb-sol-cli/cmd/erc20.js index c241aae..21fedc4 100644 --- a/cb-sol-cli/cmd/erc20.js +++ b/cb-sol-cli/cmd/erc20.js @@ -57,14 +57,13 @@ const depositCmd = new Command("deposit") const bridgeInstance = new ethers.Contract(args.bridge, constants.ContractABIs.Bridge.abi, args.wallet); const data = '0x' + - args.resourceId.substr(2) + // OriginHandlerAddress (32 bytes) - ethers.utils.hexZeroPad(ethers.utils.hexlify(Number(args.amount)), 32).substr(2) + // Deposit Amount (32 bytes) + ethers.utils.hexZeroPad(ethers.utils.bigNumberify(args.amount).toHexString(), 32).substr(2) + // Deposit Amount (32 bytes) ethers.utils.hexZeroPad(ethers.utils.hexlify((args.recipient.length - 2)/2), 32).substr(2) + // len(recipientAddress) (32 bytes) args.recipient.substr(2); // recipientAddress (?? bytes) log(args, `Constructed deposit:`) log(args, ` Resource Id: ${args.resourceId}`) - log(args, ` Amount: ${args.amount}`) + log(args, ` Amount: ${ethers.utils.bigNumberify(args.amount).toHexString()}`) log(args, ` len(recipient): ${(args.recipient.length - 2)/ 2}`) log(args, ` Recipient: ${args.recipient}`) log(args, ` Raw: ${data}`) @@ -93,6 +92,43 @@ const balanceCmd = new Command("balance") log(args, `Account ${args.address} has a balance of ${balance}` ) }) +const allowanceCmd = new Command("allowance") + .description("Get the allowance of a spender for an address") + .option('--spender
', 'Address of spender', constants.ERC20_HANDLER_ADDRESS) + .option('--owner
', 'Address of token owner', constants.deployerAddress) + .option('--erc20Address
', 'ERC20 contract address', constants.ERC20_ADDRESS) + .action(async function(args) { + await setupParentArgs(args, args.parent.parent) + + const erc20Instance = new ethers.Contract(args.erc20Address, constants.ContractABIs.Erc20Mintable.abi, args.wallet); + const allowance = await erc20Instance.allowance(args.owner, args.spender) + + log(args, `Spender ${args.spender} is allowed to spend ${allowance} tokens on behalf of ${args.owner}`) + }) + +const createErc20ProposalData = (amount, recipient) => { + if (recipient.substr(0, 2) === "0x") { + recipient = recipient.substr(2) + } + return '0x' + + ethers.utils.hexZeroPad(ethers.utils.bigNumberify(amount).toHexString(), 32).substr(2) + + ethers.utils.hexZeroPad(ethers.utils.hexlify(recipient.length / 2 + recipient.length % 2), 32).substr(2) + + recipient; +} + +const proposalDataHashCmd = new Command("data-hash") + .description("Hash the proposal data for an erc20 proposal") + .option('--amount ', "Amount to transfer", 1) + .option('--recipient
', 'Destination recipient address', constants.relayerAddresses[4]) + .option('--handler
', 'ERC20 handler address', constants.ERC20_HANDLER_ADDRESS) + .action(async function(args) { + + const data = createErc20ProposalData(args.amount, args.recipient) + const hash = ethers.utils.solidityKeccak256(["address", "bytes"], [args.handler, data]) + + log(args, `Hash: ${hash} Data: ${data}`) + }) + const erc20Cmd = new Command("erc20") erc20Cmd.addCommand(mintCmd) @@ -100,5 +136,7 @@ erc20Cmd.addCommand(addMinterCmd) erc20Cmd.addCommand(approveCmd) erc20Cmd.addCommand(depositCmd) erc20Cmd.addCommand(balanceCmd) +erc20Cmd.addCommand(allowanceCmd) +erc20Cmd.addCommand(proposalDataHashCmd) module.exports = erc20Cmd \ No newline at end of file diff --git a/cb-sol-cli/cmd/erc721.js b/cb-sol-cli/cmd/erc721.js index 765a9f7..c33ccb8 100644 --- a/cb-sol-cli/cmd/erc721.js +++ b/cb-sol-cli/cmd/erc721.js @@ -33,7 +33,7 @@ const addMinterCmd = new Command("add-minter") .description("Add a new minter to the contract") .option('--erc721Address
', 'ERC721 contract address', constants.ERC721_ADDRESS) .option('--minter
', 'Minter address', constants.relayerAddresses[1]) - .action(async function(args) { + .action(async function (args) { await setupParentArgs(args, args.parent.parent) const erc721Instance = new ethers.Contract(args.erc721Address, constants.ContractABIs.Erc721Mintable.abi, args.wallet); const MINTER_ROLE = await erc721Instance.MINTER_ROLE() @@ -52,7 +52,10 @@ const approveCmd = new Command("approve") const erc721Instance = new ethers.Contract(args.erc721Address, constants.ContractABIs.Erc721Mintable.abi, args.wallet); log(args, `Approving ${args.recipient} to spend token ${args.id} from ${args.wallet.address} on contract ${args.erc721Address}!`); - const tx = await erc721Instance.approve(args.recipient, ethers.utils.hexlify(args.id), { gasPrice: args.gasPrice, gasLimit: args.gasLimit}); + const tx = await erc721Instance.approve(args.recipient, ethers.utils.hexlify(args.id), { + gasPrice: args.gasPrice, + gasLimit: args.gasLimit + }); await waitForTx(args.provider, tx.hash) }) @@ -70,15 +73,14 @@ const depositCmd = new Command("deposit") const bridgeInstance = new ethers.Contract(args.bridge, constants.ContractABIs.Bridge.abi, args.wallet); const data = '0x' + - args.resourceId.substr(2) + // resourceID (32 bytes) for now ethers.utils.hexZeroPad(ethers.utils.hexlify(args.id), 32).substr(2) + // Deposit Amount (32 bytes) - ethers.utils.hexZeroPad(ethers.utils.hexlify((args.recipient.length - 2)/2), 32).substr(2) + // len(recipientAddress) (32 bytes) + ethers.utils.hexZeroPad(ethers.utils.hexlify((args.recipient.length - 2) / 2), 32).substr(2) + // len(recipientAddress) (32 bytes) ethers.utils.hexlify(args.recipient).substr(2) // recipientAddress (?? bytes) log(args, `Constructed deposit:`) log(args, ` Resource Id: ${args.resourceId}`) log(args, ` Token Id: ${args.id}`) - log(args, ` len(recipient): ${(args.recipient.length - 2)/2}`) + log(args, ` len(recipient): ${(args.recipient.length - 2) / 2}`) log(args, ` Recipient: ${args.recipient}`) log(args, ` Raw: ${data}`) log(args, "Creating deposit to initiate transfer!") @@ -88,10 +90,40 @@ const depositCmd = new Command("deposit") args.dest, // destination chain id args.resourceId, data, - { gasPrice: args.gasPrice, gasLimit: args.gasLimit}); + {gasPrice: args.gasPrice, gasLimit: args.gasLimit}); await waitForTx(args.provider, tx.hash) }) +const createErc721ProposalData = (id, recipient, metadata) => { + if (recipient.substr(0, 2) === "0x") { + recipient = recipient.substr(2) + } + if (metadata.substr(0, 2) === "0x") { + metadata = metadata.substr(2) + } + console.log(metadata) + return '0x' + + ethers.utils.hexZeroPad(ethers.utils.bigNumberify(id).toHexString(), 32).substr(2) + + ethers.utils.hexZeroPad(ethers.utils.hexlify(recipient.length / 2 + recipient.length % 2), 32).substr(2) + + recipient + + ethers.utils.hexZeroPad(ethers.utils.hexlify(metadata.length / 2 + metadata.length % 2), 32).substr(2) + + metadata; +} + +const proposalDataHashCmd = new Command("data-hash") + .description("Hash the proposal data for an erc721 proposal") + .option('--id ', "Token ID", 1) + .option('--recipient
', 'Destination recipient address', constants.relayerAddresses[4]) + .option('--metadata ', 'Token metadata', "") + .option('--handler
', 'ERC721 handler address', constants.ERC20_HANDLER_ADDRESS) + .action(async function (args) { + console.log(args.metadata) + const data = createErc721ProposalData(args.id, args.recipient, args.metadata) + const hash = ethers.utils.solidityKeccak256(["address", "bytes"], [args.handler, data]) + + log(args, `Hash: ${hash} Data: ${data}`) + }) + const erc721Cmd = new Command("erc721") erc721Cmd.addCommand(mintCmd) @@ -99,5 +131,6 @@ erc721Cmd.addCommand(ownerCmd) erc721Cmd.addCommand(addMinterCmd) erc721Cmd.addCommand(approveCmd) erc721Cmd.addCommand(depositCmd) +erc721Cmd.addCommand(proposalDataHashCmd) module.exports = erc721Cmd diff --git a/cb-sol-cli/constants.js b/cb-sol-cli/constants.js index deaafcf..c27df6a 100644 --- a/cb-sol-cli/constants.js +++ b/cb-sol-cli/constants.js @@ -12,7 +12,8 @@ const ContractABIs = { Erc721Handler: require(CONTRACT_PATH + "/ERC721Handler.json"), Erc721Mintable: require(CONTRACT_PATH + "/ERC721MinterBurnerPauser.json"), GenericHandler: require(CONTRACT_PATH + "/GenericHandler.json"), - CentrifugeAssetStore: require(CONTRACT_PATH + "/CentrifugeAsset.json") + CentrifugeAssetStore: require(CONTRACT_PATH + "/CentrifugeAsset.json"), + HandlerHelpers: require(CONTRACT_PATH + "/HandlerHelpers.json") } module.exports.ContractABIs = ContractABIs @@ -53,3 +54,4 @@ module.exports.ERC20_RESOURCEID = ethers.utils.hexZeroPad((this.ERC20_ADDRESS + module.exports.ERC721_RESOURCEID = ethers.utils.hexZeroPad((this.ERC721_ADDRESS + ethers.utils.hexlify(this.DEFAULT_SOURCE_ID).substr(2)), 32); module.exports.GENERIC_RESOURCEID = ethers.utils.hexZeroPad((this.CENTRIFUGE_ASSET_STORE_ADDRESS + ethers.utils.hexlify(this.DEFAULT_SOURCE_ID).substr(2)), 32); +module.exports.ERC20_PROPOSAL_HASH = "0x19b14d095647bb784f237072e14df1133fbd2008c5039c469321d77099a7b6da" \ No newline at end of file diff --git a/cb-sol-cli/docs/bridge.md b/cb-sol-cli/docs/bridge.md index 592f526..0712499 100644 --- a/cb-sol-cli/docs/bridge.md +++ b/cb-sol-cli/docs/bridge.md @@ -3,8 +3,9 @@ - [`register-resource`](#register-resource) - [`register-generic-resource`](#register-generic-resource) - [`set-burn`](#set-burn) -- [`query-proposal`](#query-proposal) - [`cancel-proposal`](#cancel-proposal) +- [`query-proposal`](#query-proposal) +- [`query-resource`](#query-resouce) ## `register-resource` @@ -41,15 +42,6 @@ Set a token contract as mintable/burnable in a handler. --tokenContract
Token contract to be registered ``` -## `query-proposal` -Query a proposal on-chain. - -``` - --bridge
Bridge contract address - --depositNonce
Nonce of proposal - --chainId Source chain ID of proposal -``` - ## `cancel-proposal` Cancels an expired proposal. @@ -58,3 +50,22 @@ Cancels an expired proposal. --chainId Chain ID of proposal to cancel --depositNonce Deposit nonce of proposal to cancel ``` + +## `query-proposal` +Queries an inbound proposal. + +``` + --bridge
Bridge contract address + --chainId Source chain ID of proposal + --depositNonce Deposit nonce of proposal + --dataHash Hash of proposal metadata +``` + +## `query-resouce` +Queries the contract address associated with the provided resource ID for a specific handler contract. + +``` + --handler
Handler contract address + --resourceId
ResourceID to query + +``` \ No newline at end of file diff --git a/cb-sol-cli/docs/erc20.md b/cb-sol-cli/docs/erc20.md index 350446e..37d9ba6 100644 --- a/cb-sol-cli/docs/erc20.md +++ b/cb-sol-cli/docs/erc20.md @@ -5,6 +5,8 @@ - [`approve`](#approve) - [`deposit`](#deposit) - [`balance`](#balance) +- [`allowance`](#allowance) +- [`data-hash`](#data-hash) ## `mint` Mint tokens on an ERC20 mintable contract. @@ -46,3 +48,21 @@ Query balance for an account in an ERC20 contract. --address
Address to query --erc20Address
ERC20 contract address ``` + +## `allowance` +Get the allowance of a spender for an address + +``` + --spender
Address of spender + --owner
Address of token owner + --erc20Address
ERC20 contract address +``` + +## `data-hash` +Constructs proposal data and returns the hash required for on-chain queries. + +``` + --amount Amount to transfer + --recipient
Destination recipient address + --handler
ERC20 handler address +``` \ No newline at end of file diff --git a/cb-sol-cli/docs/erc721.md b/cb-sol-cli/docs/erc721.md index 4affb9e..f548e07 100644 --- a/cb-sol-cli/docs/erc721.md +++ b/cb-sol-cli/docs/erc721.md @@ -5,6 +5,7 @@ - [`add-minter`](#add-minter) - [`approve`](#approve) - [`deposit`](#deposit) +- [`data-hash`](#data-hash) ## `mint` Mint tokens on an ERC721 mintable contract. @@ -50,3 +51,13 @@ Initiate a transfer of ERC721 tokens. --resourceId Resource ID for transfer --bridge
Bridge contract address ``` + +## `data-hash` +Construct erc721 proposal data and provide hash for on-chain queries. + +``` + --id Token ID + --recipient
Destination recipient address + --metadata Token metadata + --handler
ERC721 handler address +``` \ No newline at end of file diff --git a/ci/ci_cli.sh b/ci/ci_cli.sh index 788c4f4..7bb3b0e 100755 --- a/ci/ci_cli.sh +++ b/ci/ci_cli.sh @@ -2,6 +2,8 @@ # Copyright 2020 ChainSafe Systems # SPDX-License-Identifier: LGPL-3.0-only +CMD=cb-sol-cli + ERC721_HANDLER="0x3f709398808af36ADBA86ACC617FeB7F5B7B193E" ERC721_RESOURCE_ID="0x0000000000000000000000d7E33e1bbf65dC001A0Eb1552613106CD7e40C3100" ERC721_CONTRACT="0xd7E33e1bbf65dC001A0Eb1552613106CD7e40C31" @@ -13,31 +15,36 @@ NEW_RELAYER="0x8cED5ad0d8dA4Ec211C17355Ed3DBFEC4Cf0E5b9" set -eux -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE deploy --all - -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE erc20 mint -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE erc20 add-minter -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE bridge register-resource -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE bridge set-burn -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE erc20 approve -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE erc20 deposit -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE erc20 balance - -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE erc721 mint --id 0x1 -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE erc721 add-minter -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE bridge register-resource --handler $ERC721_HANDLER --resourceId $ERC721_RESOURCE_ID --targetContract $ERC721_CONTRACT -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE erc721 approve --id 0x1 -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE bridge set-burn --handler $ERC721_HANDLER --tokenContract $ERC721_CONTRACT -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE erc721 deposit --id 0x1 - -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE bridge register-generic-resource --execute "store(bytes32)" --hash -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE cent getHash - -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE admin is-relayer -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE admin add-relayer --relayer $NEW_RELAYER -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE admin remove-relayer --relayer $NEW_RELAYER -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE admin set-threshold --threshold 3 -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE admin pause -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE admin unpause -cb-sol-cli --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE admin set-fee --fee 1 +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE deploy --all + +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE erc20 mint --amount 100 +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE erc20 add-minter +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE bridge register-resource +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE bridge query-resource +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE bridge set-burn +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE erc20 approve --amount 100 +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE erc20 allowance +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE erc20 deposit --amount 100 +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE erc20 balance +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE erc20 data-hash --amount 100 +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE bridge query-proposal + +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE erc721 mint --id 0x1 +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE erc721 add-minter +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE bridge register-resource --handler $ERC721_HANDLER --resourceId $ERC721_RESOURCE_ID --targetContract $ERC721_CONTRACT +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE erc721 approve --id 0x1 +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE bridge set-burn --handler $ERC721_HANDLER --tokenContract $ERC721_CONTRACT +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE erc721 deposit --id 0x1 +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE erc721 data-hash --id 0x1 --metadata "0x1234" + +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE bridge register-generic-resource --execute "store(bytes32)" --hash +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE cent getHash + +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE admin is-relayer +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE admin add-relayer --relayer $NEW_RELAYER +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE admin remove-relayer --relayer $NEW_RELAYER +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE admin set-threshold --threshold 3 +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE admin pause +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE admin unpause +$CMD --gasLimit $GAS_LIMIT --gasPrice $GAS_PRICE admin set-fee --fee 1