Skip to content
This repository has been archived by the owner on Jul 12, 2022. It is now read-only.

Commit

Permalink
Add cb-sol-cli (#4)
Browse files Browse the repository at this point in the history
Copies cb-sol-cli in, updates the install process to clone chainbridge-solidity and build the ABI files.
  • Loading branch information
ansermino authored Jun 15, 2020
1 parent f8f13d0 commit 3b853f4
Show file tree
Hide file tree
Showing 23 changed files with 1,303 additions and 3 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
.idea/
.idea/
node_modules/
chainbridge-solidity/
13 changes: 12 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Copyright 2020 ChainSafe Systems
# SPDX-License-Identifier: LGPL-3.0-only

cache: npm

branches:
only:
- master
Expand All @@ -17,4 +19,13 @@ jobs:
script:
- cd cfgBuilder
- make lint
- go test ./...
- go test ./...
- name: "cb-sol-cli Tests"
language: node_js
node_js: 12
env: CI=true
script:
- pushd cb-sol-cli
- make install
- cd chainbridge-solidity && SILENT=true make start-ganache
- popd && ./ci/ci_cli.sh
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ Please see the [README](/cfgBuilder/README.md).

## cb-sol-cli

[WIP]
Please see the [README](/cb-sol-cli/README.md).
16 changes: 16 additions & 0 deletions cb-sol-cli/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
SOL_URL=https://github.com/ChainSafe/chainbridge-solidity


fetch-contracts:
@echo " > \033[32mFetching chainbridge-solidity contracts... \033[0m "
git clone ${SOL_URL} && cd chainbridge-solidity && git checkout ${GIT_COMMIT}

compile:
cd chainbridge-solidity && npm install && npx truffle compile

install: fetch-contracts compile
@echo " > \033[32mInstalling cb-sol-cli... \033[0m "
npm link .

clean:
rm -rf chainbridge-solidity/
39 changes: 39 additions & 0 deletions cb-sol-cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# cb-sol-cli Documentation

This CLI supports on-chain interactions with components of ChainBridge.

## Installation

Installation requires the ABI files from the contracts which will be fetched and built from the chainbridge-solidity repo.
```
$ make install
```

## Usage

The root command (`cb-sol-cli`) has some options:
```
--url <value> URL to connect to
--gasLimit <value> Gas limit for transactions
--gasPrice <value> Gas limit for transactions
```
\
The keypair used for interactions can be configured with:
```
--privateKey <value> Private key to use
```
or
```
--jsonWallet <path> Encrypted JSON wallet
--jsonWalletPassword <value> Password for encrypted JSON wallet
```

There are multiple subcommands provided:

- [`deploy`](docs/deploy.md): Deploys contracts via RPC
- [`bridge`](docs/bridge.md): Interactions with the bridge contract such as registering resource IDs and handler addresses
- [`admin`](docs/admin.md): Interactions with the bridge contract for administering relayer set, relayer threshold, fees and more.
- [`erc20`](docs/erc20.md): Interactions with ERC20 contracts and handlers
- [`erc721`](docs/erc721.md): Interactions with ERC721 contracts and handler


115 changes: 115 additions & 0 deletions cb-sol-cli/cmd/admin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
const ethers = require('ethers');
const constants = require('../constants');

const {Command} = require('commander');
const {setupParentArgs, waitForTx, log} = require("./utils")

const isRelayerCmd = new Command("is-relayer")
.description("Check if address is relayer")
.option('--relayer <value>', 'Address to check', constants.relayerAddresses[0])
.option('--bridge <address>', 'Bridge contract address', constants.BRIDGE_ADDRESS)
.action(async function (args) {
await setupParentArgs(args, args.parent.parent)
const bridgeInstance = new ethers.Contract(args.bridge, constants.ContractABIs.Bridge.abi, args.wallet);

let res = await bridgeInstance.isRelayer(args.relayer)
console.log(`[${args._name}] Address ${args.relayer} ${res ? "is" : "is not"} a relayer.`)
})

const addRelayerCmd = new Command("add-relayer")
.description("Add a relayer")
.option('--relayer <address>', 'Address of relayer', constants.relayerAddresses[0])
.option('--bridge <address>', 'Bridge contract address', constants.BRIDGE_ADDRESS)
.action(async function (args) {
await setupParentArgs(args, args.parent.parent)
const bridgeInstance = new ethers.Contract(args.bridge, constants.ContractABIs.Bridge.abi, args.wallet);
log(args, `Adding ${args.relayer} as a relayer.`)
let tx = await bridgeInstance.adminAddRelayer(args.relayer)
await waitForTx(args.provider, tx.hash)
})

const removeRelayerCmd = new Command("remove-relayer")
.description("Remove a relayer")
.option('--relayer <address>', 'Address of relayer', constants.relayerAddresses[0])
.option('--bridge <address>', 'Bridge contract address', constants.BRIDGE_ADDRESS)
.action(async function (args) {
await setupParentArgs(args, args.parent.parent)
const bridgeInstance = new ethers.Contract(args.bridge, constants.ContractABIs.Bridge.abi, args.wallet);
log(args, `Removing relayer ${args.relayer}.`)
let tx = await bridgeInstance.adminRemoveRelayer(args.relayer)
await waitForTx(args.provider, tx.hash)
})

const setThresholdCmd = new Command("set-threshold")
.description("Set relayer threshold")
.option('--bridge <address>', 'Bridge contract address', constants.BRIDGE_ADDRESS)
.option('--threshold <value>', 'New relayer threshold', 3)
.action(async function (args) {
await setupParentArgs(args, args.parent.parent)
const bridgeInstance = new ethers.Contract(args.bridge, constants.ContractABIs.Bridge.abi, args.wallet);
log(args, `Setting relayer threshold to ${args.threshold}`)
let tx = await bridgeInstance.adminChangeRelayerThreshold(args.threshold)
await waitForTx(args.provider, tx.hash)
})

const pauseTransfersCmd = new Command("pause")
.description("Pause deposits and proposal on the bridge")
.option('--bridge <address>', 'Bridge contract address', constants.BRIDGE_ADDRESS)
.action(async function (args) {
await setupParentArgs(args, args.parent.parent)
const bridgeInstance = new ethers.Contract(args.bridge, constants.ContractABIs.Bridge.abi, args.wallet);
log(args, `Pausing deposits and proposals`)
let tx = await bridgeInstance.adminPauseTransfers()
await waitForTx(args.provider, tx.hash)
})

const unpauseTransfersCmd = new Command("unpause")
.description("Unpause deposits and proposals on the bridge")
.option('--bridge <address>', 'Bridge contract address', constants.BRIDGE_ADDRESS)
.action(async function (args) {
await setupParentArgs(args, args.parent.parent)
const bridgeInstance = new ethers.Contract(args.bridge, constants.ContractABIs.Bridge.abi, args.wallet);
log(args, `Unpausing deposits and proposals`)
let tx = await bridgeInstance.adminUnpauseTransfers()
await waitForTx(args.provider, tx.hash)
})

const changeFeeCmd = new Command("set-fee")
.description("Set a new fee for deposits")
.option('--bridge <address>', 'Bridge contract address', constants.BRIDGE_ADDRESS)
.option('--fee <value>', 'New fee (in wei)', 0)
.action(async function (args) {
await setupParentArgs(args, args.parent.parent)
const bridgeInstance = new ethers.Contract(args.bridge, constants.ContractABIs.Bridge.abi, args.wallet);
log(args, `Setting fee to ${args.fee} wei`)
let tx = await bridgeInstance.adminChangeFee(args.fee)
await waitForTx(args.provider, tx.hash)
})

const withdrawCmd = new Command("withdraw")
.description("Withdraw funds collected from fees")
.option('--bridge <address>', 'Bridge contract address', constants.BRIDGE_ADDRESS)
.option('--handler <address>', 'Handler contract address', constants.ERC20_HANDLER_ADDRESS)
.option('--tokenContract <address>', 'ERC20 or ERC721 token contract address', constants.ERC20_ADDRESS)
.option('--recipient <address>', 'Address to withdraw to', constants.relayerAddresses[0])
.option('--amountOrId <value>', 'Token ID or amount to withdraw', 1)
.action(async function (args) {
await setupParentArgs(args, args.parent.parent)
const bridgeInstance = new ethers.Contract(args.bridge, constants.ContractABIs.Bridge.abi, args.wallet);
log(args, `Withdrawing tokens (${args.amountOrId}) in contract ${args.tokenContract} to recipient ${args.recipient}`)
let tx = await bridgeInstance.adminWithdraw(args.handler, args.tokenContract, args.recipient, args.amountOrId)
await waitForTx(args.provider, tx.hash)
})

const adminCmd = new Command("admin")

adminCmd.addCommand(isRelayerCmd)
adminCmd.addCommand(addRelayerCmd)
adminCmd.addCommand(removeRelayerCmd)
adminCmd.addCommand(setThresholdCmd)
adminCmd.addCommand(pauseTransfersCmd)
adminCmd.addCommand(unpauseTransfersCmd)
adminCmd.addCommand(changeFeeCmd)
adminCmd.addCommand(withdrawCmd)

module.exports = adminCmd
101 changes: 101 additions & 0 deletions cb-sol-cli/cmd/bridge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
const ethers = require('ethers');
const constants = require('../constants');

const {Command} = require('commander');
const {setupParentArgs, getFunctionBytes, waitForTx, log} = require("./utils")

const EMPTY_SIG = "0x00000000"

const registerResourceCmd = new Command("register-resource")
.description("Register a resource ID with a contract address for a handler")
.option('--bridge <address>', 'Bridge contract address', constants.BRIDGE_ADDRESS)
.option('--handler <address>', 'Handler address', constants.ERC20_HANDLER_ADDRESS)
.option('--targetContract <address>', `Contract address to be registered`, constants.ERC20_ADDRESS)
.option('--resourceId <address>', `Resource ID to be registered`, constants.ERC20_RESOURCEID)
.action(async function (args) {
await setupParentArgs(args, args.parent.parent)

const bridgeInstance = new ethers.Contract(args.bridge, constants.ContractABIs.Bridge.abi, args.wallet);
log(args,`Registering contract ${args.targetContract} with resource ID ${args.resourceId} on handler ${args.handler}`);
const tx = await bridgeInstance.adminSetResource(args.handler, args.resourceId, args.targetContract, { gasPrice: args.gasPrice, gasLimit: args.gasLimit});
await waitForTx(args.provider, tx.hash)
})

const registerGenericResourceCmd = new Command("register-generic-resource")
.description("Register a resource ID with a generic handler")
.option('--bridge <address>', 'Bridge contract address', constants.BRIDGE_ADDRESS)
.option('--handler <address>', 'Handler contract address', constants.GENERIC_HANDLER_ADDRESS)
.option('--targetContract <address>', `Contract address to be registered`, constants.CENTRIFUGE_ASSET_STORE_ADDRESS)
.option('--resourceId <address>', `ResourceID to be registered`, constants.GENERIC_RESOURCEID)
.option('--deposit <string>', "Deposit function signature", EMPTY_SIG)
.option('--execute <string>', "Execute proposal function signature", EMPTY_SIG)
.option('--hash', "Treat signature inputs as function prototype strings, hash and take the first 4 bytes", false)
.action(async function(args) {
await setupParentArgs(args, args.parent.parent)

const bridgeInstance = new ethers.Contract(args.bridge, constants.ContractABIs.Bridge.abi, args.wallet);

if (args.hash) {
args.deposit = getFunctionBytes(args.deposit)
args.execute = getFunctionBytes(args.execute)
}

log(args,`Registering generic resource ID ${args.resourceId} with contract ${args.targetContract} on handler ${args.handler}`)
const tx = await bridgeInstance.adminSetGenericResource(args.handler, args.resourceId, args.targetContract, args.deposit, args.execute, { gasPrice: args.gasPrice, gasLimit: args.gasLimit})
await waitForTx(args.provider, tx.hash)
})

const setBurnCmd = new Command("set-burn")
.description("Set a token contract as burnable in a handler")
.option('--bridge <address>', 'Bridge contract address', constants.BRIDGE_ADDRESS)
.option('--handler <address>', 'ERC20 handler contract address', constants.ERC20_HANDLER_ADDRESS)
.option('--tokenContract <address>', `Token contract to be registered`, constants.ERC20_ADDRESS)
.action(async function (args) {
await setupParentArgs(args, args.parent.parent)
const bridgeInstance = new ethers.Contract(args.bridge, constants.ContractABIs.Bridge.abi, args.wallet);

log(args,`Setting contract ${args.tokenContract} as burnable on handler ${args.handler}`);
const tx = await bridgeInstance.adminSetBurnable(args.handler, args.tokenContract, { gasPrice: args.gasPrice, gasLimit: args.gasLimit});
await waitForTx(args.provider, tx.hash)
})

const queryProposalCmd = new Command("query-proposal")
.description("Query a proposal on-chain")
.option('--bridge <address>', 'Bridge contract address', constants.BRIDGE_ADDRESS)
.option('--depositNonce <address>', 'Nonce of proposal', 0)
.option('--chainId <id>', '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 <address>', 'Bridge contract address', constants.BRIDGE_ADDRESS)
.option('--chainId <id>', 'Chain ID of proposal to cancel', 0)
.option('--depositNonce <value>', 'Deposit nonce of proposal to cancel', 0)
.action(async function (args) {
await setupParentArgs(args, args.parent.parent)
const bridgeInstance = new ethers.Contract(args.bridge, constants.ContractABIs.Bridge.abi, args.wallet);

log(args, `Setting proposal with chain ID ${args.chainId} and deposit nonce ${args.depositNonce} status to 'Cancelled`);
const tx = await bridgeInstance.adminCancelProposal(args.chainId, args.depositNonce);
await waitForTx(args.provider, tx.hash)
})

const bridgeCmd = new Command("bridge")

bridgeCmd.addCommand(registerResourceCmd)
bridgeCmd.addCommand(registerGenericResourceCmd)
bridgeCmd.addCommand(setBurnCmd)
bridgeCmd.addCommand(queryProposalCmd)
bridgeCmd.addCommand(cancelProposalCmd)

module.exports = bridgeCmd
23 changes: 23 additions & 0 deletions cb-sol-cli/cmd/centrifuge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const ethers = require('ethers');
const {Command} = require('commander');

const {setupParentArgs, log} = require("./utils")

const constants = require('../constants');

const getHashCmd = new Command('getHash')
.description('Returns if a the given hash exists')
.requiredOption('--hash <value>', 'A hash to lookup', ethers.utils.hexZeroPad("0x", 32))
.option('--address <value>', 'Centrifuge asset store contract address', constants.CENTRIFUGE_ASSET_STORE_ADDRESS)
.action(async function (args) {
await setupParentArgs(args, args.parent.parent);
const assetStore = new ethers.Contract(args.address, constants.ContractABIs.CentrifugeAssetStore.abi, args.wallet);
const res = await assetStore._assetsStored(ethers.utils.hexZeroPad(args.hash, 32));
log(args, `The hash ${args.hash} was ${res ? "found!" : "NOT found!"}`);
})

const centCmd = new Command("cent")

centCmd.addCommand(getHashCmd)

module.exports = centCmd
Loading

0 comments on commit 3b853f4

Please sign in to comment.