The project includes 4 types of contracts, which are:
- multisig contract: owner account of map light client contract and mcs contract to avoid centralization risk
- mcs factory contract: factory contract to create multisig contract and mcs contract
- mcs contract: MAP cross chain service contract
- mcs token contract: NEP-141 token created by mcs contract
1. rust
Follow these instructions for setting up Rust. Then, add the wasm32-unknown-unknown toolchain which enables compiling Rust to Web Assembly (wasm), the low-level language used by the NEAR platform.
# Get Rust in linux and MacOS
curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
source $HOME/.cargo/env
# Add the wasm toolchain
rustup target add wasm32-unknown-unknown
2. near-cli
The NEAR Command Line Interface (CLI) is a tool that enables to interact with the NEAR network directly from the shell. Follow here for installing near-cli. Then, select the network and login with your master account.
# Install near-cli in linux and McsOS
npm install -g near-cli
# The default network for near-cli is testnet, change the network by setting NEAR_ENV
# export NEAR_ENV=mainnet
# login with your master account
near login
3. jq
Jq is a lightweight and flexible command-line JSON processor. Follow here to install it.
Run below script to build:
./scripts/build.sh
5 wasm files will be generated in directory ./script/res, which are: (the first 2 files are copied from mapclients project)
- mcs.wasm: MAP cross chain service contract
- mcs_factory.wasm: factory contract to deploy and initialize the MCS contract and make MCS contract account in locked state.
- mcs_token.wasm: NEP-141 token contract deployed by MCS contract
- mock_map_client.wasm: mocked MAP light client contract which is for testing
- multisig.wasm: multisig contract
1. Configure below parameters in ./scripts/config.sh
MASTER_ACCOUNT="map002.testnet" # make sure the account is already created on NEAR blockchain
# factory contract
FACTORY_NAME=mfac # the name of mcs factory contract to be created, the account ID will be $MFACTORY_NAME.$MASTER_ACCOUNT
# multisig contract
MULTISIG_NAME="multisig" # the name of multisig contract to be created, the account ID will be $MULTISIG_NAME.$MFACTORY_NAME.$MASTER_ACCOUNT
MEMBERS=(member0.map002.testnet member1.map002.testnet member2.map002.testnet) # the multisig members list, make sure
# these accounts have been created on NEAR blockchain
CONFIRMS=2 # the multisig confirmation number to trigger the execution of the request
REQUEST_LOCK=5 # request cooldown period in seconds (time before a request can be executed)
# mcs contract
MCS_NAME="mcs" # the name of mcs contract to be created, the account ID will be $MCS_NAME.$MFACTORY_NAME.$MASTER_ACCOUNT
MAP_MCS_ADDRESS="F579c89C22DAc92E9816C0b39856cA9281Bd7BE0" # the mcs contract address on MAP relay chain
WNEAR_ACCOUNT="wrap.testnet" # wrapped near contract account on NEAR blockchain
NEAR_CHAIN_ID=5566818579631833089 # NEAR testnet blockchain id, mainnet is 5566818579631833088
MAP_CHAIN_ID=22776 # MAP blockchain ID
CLIENT_ACCOUNT="client.fac.map002.testnet" # the account ID of the map light client contract which has already been deployed
2. Deploy factory contract, multisig contract and mcs contract, and initialize them with below command:
./scripts/deploy.sh
We can use the shell scripts in directory ./script to simplify the steps. First run below command to set environment variables:
source ./scripts/config.sh
NOTE: in the following examples we are using 2 out of 3 multisig schema.
1. Support new NEP-141 mcs token to cross chain through MCS service
MCS_TOKEN_NAME="mcs_token_0" # the mcs token name, the token account will be $MCS_TOKEN_NAME.$MCS_ACCOUNT
MCS_TOKEN=$MCS_TOKEN_NAME.$MCS_ACCOUNT # mcs token account ID
DECIMALS=24
USER_ACCOUNT="map002.testnet" # the account to deploy new mcs token contract, make sure it is created on NEAR blockchain
# deploy mcs token contract
./scripts/manage_mcs_token.sh deploy $MCS_TOKEN_NAME $USER_ACCOUNT
# request to set metadata by multisig member
./scripts/manage_multisig.sh request_and_confirm metadata $MCS_TOKEN $DECIMALS ${MEMBERS[1]}
# the request ID can be obtained from the last line of last command's output
REQUEST_ID=
# confirm the request by another member
./scripts/manage_multisig.sh confirm $REQUEST_ID ${MEMBERS[2]}
# if the request is not executed because of the time lock, anyone can execute it after REQUEST_LOCK time
# ./scripts/manage_multisig.sh execute $REQUEST_ID $USER_ACCOUNT
# list mcs tokes
./scripts/manage_mcs_token.sh list
2. Allow the mcs/ft/native token to transfer to a specified target blockchain
First, we should set the chain type of target blockchain. Currently only EvmChain type is supported.
TO_CHAIN=212 # to chain ID
CHAIN_TYPE="EvmChain" # to chain type
# request to set chain type by multisig member
./scripts/manage_multisig.sh request_and_confirm chain_type $TO_CHAIN $CHAIN_TYPE ${MEMBERS[1]}
# the request ID can be obtained from the last line of last command's output
REQUEST_ID=
# confirm the request by another member
./scripts/manage_multisig.sh confirm $REQUEST_ID ${MEMBERS[2]}
# if the request is not executed because of the time lock, anyone can execute it after REQUEST_LOCK time
# ./scripts/manage_multisig.sh execute $REQUEST_ID $MASTER_ACCOUNT
If you want to add target chain ID to mcs token, run below commands:
TO_CHAIN=212 # to chain ID
MCS_TOKEN_NAME="mcs_token_0"
MCS_TOKEN=$MCS_TOKEN_NAME.$MCS_ACCOUNT # mcs token account ID
# request to add target chain ID to mcs token by multisig member
./scripts/manage_multisig.sh request_and_confirm add_mcs $MCS_TOKEN $TO_CHAIN ${MEMBERS[1]}
# the request ID can be obtained from the last line of last command's output
REQUEST_ID=
# confirm the request by another member
./scripts/manage_multisig.sh confirm $REQUEST_ID ${MEMBERS[2]}
# if the request is not executed because of the time lock, anyone can execute it after REQUEST_LOCK time
# ./scripts/manage_multisig.sh execute $REQUEST_ID $MASTER_ACCOUNT
# view the token list to check if the chain ID is added successfully
./scripts/manage_mcs_token.sh list
If you want to add target chain ID to ft token, run below commands:
TO_CHAIN=212 # to chain ID
FT_TOKEN="wrap.testnet" # ft token account ID
# request to add target chain ID to ft token by multisig member
./scripts/manage_multisig.sh request_and_confirm add_ft $FT_TOKEN $TO_CHAIN ${MEMBERS[1]}
# the request ID can be obtained from the last line of last command's output
REQUEST_ID=
# confirm the request by another member
./scripts/manage_multisig.sh confirm $REQUEST_ID ${MEMBERS[2]}
# if the request is not executed because of the time lock, anyone can execute it after REQUEST_LOCK time
# ./scripts/manage_multisig.sh execute $REQUEST_ID $MASTER_ACCOUNT
# view the token list to check if the chain ID is added successfully
./scripts/manage_ft_token.sh list
If you want to add target chain ID to native token, run below commands:
TO_CHAIN=212 # to chain ID
# request to add target chain ID to native token by multisig member
./scripts/manage_multisig.sh request_and_confirm add_native $TO_CHAIN ${MEMBERS[1]}
# the request ID can be obtained from the last line of last command's output
REQUEST_ID=
# confirm the request by another member
./scripts/manage_multisig.sh confirm $REQUEST_ID ${MEMBERS[2]}
# if the request is not executed because of the time lock, anyone can execute it after REQUEST_LOCK time
# ./scripts/manage_multisig.sh execute $REQUEST_ID $MASTER_ACCOUNT
# view the token list to check if the chain ID is added successfully
./scripts/manage_native_token.sh list
3. Transfer mcs/ft/native token to another blockchain through MCS service
Transfer mcs token to another blockchain:
FROM="map001.testnet" # sender account ID on NEAR blockchain
TO="[46,120,72,116,221,179,44,215,151,93,104,86,91,80,148,18,165,181,25,244]" # address 0x2E784874ddB32cD7975D68565b509412A5B519F4
# on target blockchain
TO_CHAIN=212 # to chain ID
AMOUNT=100000000000000000000000
MCS_TOKEN="mcs_token_0".$MCS_ACCOUNT # mcs token account ID
# get the token balance of the sender
./scripts/manage_mcs_token.sh balance $MCS_TOKEN $FROM
# transfer mcs token to receiver on target chain, make sure sender has enough token
./scripts/manage_mcs_token.sh transfer $MCS_TOKEN $TO_CHAIN $FROM $TO $AMOUNT
# get the token balance of the sender to check if the token was transferred out successfully
./scripts/manage_mcs_token.sh balance $MCS_TOKEN $FROM
Transfer ft token to another blockchain:
FROM="map001.testnet"
TO="[46,120,72,116,221,179,44,215,151,93,104,86,91,80,148,18,165,181,25,244]"
TO_CHAIN=212
AMOUNT=100000000000000000000000
FT_TOKEN="wrap.testnet" # ft token account ID
# get the token balance of the sender
./scripts/manage_ft_token.sh balance $FT_TOKEN $FROM
# transfer ft token to receiver on target chain, make sure sender has enough token
./scripts/manage_ft_token.sh transfer $FT_TOKEN $TO_CHAIN $FROM $TO $AMOUNT
# get the token balance of the sender to check if the token was transferred out successfully
./scripts/manage_ft_token.sh balance $FT_TOKEN $FROM
Transfer native token to another blockchain:
FROM="map001.testnet"
TO="[46,120,72,116,221,179,44,215,151,93,104,86,91,80,148,18,165,181,25,244]"
TO_CHAIN=212
AMOUNT=100000000000000000000000
# get the token balance of the sender
./scripts/manage_native_token.sh balance $FROM
# transfer native token to receiver on target chain, make sure sender has enough token
./scripts/manage_native_token.sh transfer $TO_CHAIN $FROM $TO $AMOUNT
# get the token balance of the sender to check if the token was transferred out successfully
./scripts/manage_native_token.sh balance $FROM
The mcs contract and mcs token contract can be upgraded through multisig contract.
Before upgrading mcs contract, everything (transfer in, transfer out, deposit out...) should be paused.
PAUSED_MASK=63 # pause everything
# request to pause everything by multisig member
./scripts/manage_multisig.sh request_and_confirm set_paused $PAUSED_MASK ${MEMBERS[1]}
# the request ID can be obtained from the last line of last command's output
REQUEST_ID=
# confirm the request by another member
./scripts/manage_multisig.sh confirm $REQUEST_ID ${MEMBERS[2]}
# if the request is not executed because of the time lock, anyone can execute it after REQUEST_LOCK time
# ./scripts/manage_multisig.sh execute $REQUEST_ID $MASTER_ACCOUNT
Then upgrade the mcs contract code.
The first multisig member should use mcs upgrade tool to add request and confirm.
The tool output contains a link to the transaction detail. You can get the request ID from the NEAR explorer.
Other multisig member can confirm and execute the request using below command:
# the request ID can be obtained from the transaction detail in NEAR explorer
REQUEST_ID=
# confirm the request by another member
./scripts/manage_multisig.sh confirm $REQUEST_ID ${MEMBERS[2]}
# if the request is not executed because of the time lock, anyone can execute it after REQUEST_LOCK time
# ./scripts/manage_multisig.sh execute $REQUEST_ID $MASTER_ACCOUNT
Set the mcs contract state if new state is added to the contract struct.
E.g, if "map_chain_id" is added, set it using below command:
MAP_CHAIN_ID="22776" # MAP chain ID
# request to set new map light client account by multisig member
./scripts/manage_multisig.sh request_and_confirm map_chain_id $MAP_CHAIN_ID ${MEMBERS[1]}
# the request ID can be obtained from the last line of last command's output
REQUEST_ID=
# confirm the request by another member
./scripts/manage_multisig.sh confirm $REQUEST_ID ${MEMBERS[2]}
# if the request is not executed because of the time lock, anyone can execute it after REQUEST_LOCK time
#./scripts/manage_multisig.sh execute $REQUEST_ID $MASTER_ACCOUNT
Finally, unpause everything.
PAUSED_MASK=0 # unpause everything
# request to unpause everything by multisig member
./scripts/manage_multisig.sh request_and_confirm set_paused $PAUSED_MASK ${MEMBERS[1]}
# the request ID can be obtained from the last line of last command's output
REQUEST_ID=
# confirm the request by another member
./scripts/manage_multisig.sh confirm $REQUEST_ID ${MEMBERS[2]}
# if the request is not executed because of the time lock, anyone can execute it after REQUEST_LOCK time
# ./scripts/manage_multisig.sh execute $REQUEST_ID $MASTER_ACCOUNT
NOTE: currently the script works on MacOS only.
MCS_TOKEN_WASM_FILE=/path/to/mcs/token/contract # new mcs token contract wasm file
MCS_TOKEN="mcs_token_0".$MCS_ACCOUNT
# request to upgrade mcs token contract by multisig member
./scripts/manage_multisig.sh request_and_confirm upgrade_mcs_token $MCS_TOKEN $MCS_TOKEN_WASM_FILE ${MEMBERS[1]}
# the request ID can be obtained from the last line of last command's output
REQUEST_ID=
# confirm the request by another member
./scripts/manage_multisig.sh confirm $REQUEST_ID ${MEMBERS[2]}
# if the request is not executed because of the time lock, anyone can execute it after REQUEST_LOCK time
# ./scripts/manage_multisig.sh execute $REQUEST_ID $MASTER_ACCOUNT
The MCS contract supports updating the MAP light client contract account to a new one if the old one is deprecated.
Before setting new client, the transfer in function should be paused.
PAUSED_MASK=2 # pause transfer in
# request to pause transfer in by multisig member
./scripts/manage_multisig.sh request_and_confirm set_paused $PAUSED_MASK ${MEMBERS[1]}
# the request ID can be obtained from the last line of last command's output
REQUEST_ID=
# confirm the request by another member
./scripts/manage_multisig.sh confirm $REQUEST_ID ${MEMBERS[2]}
# if the request is not executed because of the time lock, anyone can execute it after REQUEST_LOCK time
# ./scripts/manage_multisig.sh execute $REQUEST_ID $MASTER_ACCOUNT
Then set the new client account.
NEW_CLIENT_ACCOUNT="new_client1.testnet" # new MAP light client account ID
# request to set new map light client account by multisig member
./scripts/manage_multisig.sh request_and_confirm set_client $NEW_CLIENT_ACCOUNT ${MEMBERS[1]}
# the request ID can be obtained from the last line of last command's output
REQUEST_ID=
# confirm the request by another member
./scripts/manage_multisig.sh confirm $REQUEST_ID ${MEMBERS[2]}
# if the request is not executed because of the time lock, anyone can execute it after REQUEST_LOCK time
#./scripts/manage_multisig.sh execute $REQUEST_ID $MASTER_ACCOUNT
Finally, unpause transfer in function.
PAUSED_MASK=0 # unpause everything
# request to unpause everything by multisig member
./scripts/manage_multisig.sh request_and_confirm set_paused $PAUSED_MASK ${MEMBERS[1]}
# the request ID can be obtained from the last line of last command's output
REQUEST_ID=
# confirm the request by another member
./scripts/manage_multisig.sh confirm $REQUEST_ID ${MEMBERS[2]}
# if the request is not executed because of the time lock, anyone can execute it after REQUEST_LOCK time
# ./scripts/manage_multisig.sh execute $REQUEST_ID $MASTER_ACCOUNT
NOTE: currently the script works on MacOS only.
MULTISIG_WASM_FILE=/path/to/multisig/contract # new multisig contract wasm file
# request to upgrade multisig contract by multisig member
./scripts/manage_multisig.sh request_and_confirm upgrade_multisig $MULTISIG_WASM_FILE ${MEMBERS[1]}
# the request ID can be obtained from the last line of last command's output
REQUEST_ID=
# confirm the request by another member
./scripts/manage_multisig.sh confirm $REQUEST_ID ${MEMBERS[2]}
# if the request is not executed because of the time lock, anyone can execute it after REQUEST_LOCK time
# ./scripts/manage_multisig.sh execute $REQUEST_ID $MASTER_ACCOUNT
- How to run unit testing?
cargo test --workspace --lib
- How to run integration testing?
NOTE: Before run the integration testing, make sure near sandbox exists on your computer. If not, please clone the nearcore project and run "make sandbox" to build it.
# set below environment before run tests
export NEAR_SANDBOX_BIN_PATH="/path/to/near/sandbox/bin"
cargo test