IMA Access Control
Introduction
The SKALE Chain owner has special permissions to configure SKALE Chain access control. The owner may choose a "permission-by-owner" policy, selectively restricting access to only certain users and/or tokens, or may choose a permissionless policy, enabling any user and/or tokens. This access control includes several features:
-
controlling how users access SKALE Chain FUEL (sFUEL) to perform transactions.
-
controlling which tokens transfer between Mainnet and SKALE Chains.
This document describes how SKALE Chain owners can set permissions for the IMA Bridge. There are two general categories:
-
Owner permissions for IMA Mainnet
-
Owner permissions for IMA SKALE Chain
There are several IMA SKALE Chain roles that can be granted by the owner, these include:
-
CHAIN_CONNECTOR_ROLE
-
EXTRA_CONTRACT_REGISTRAR_ROLE
-
CONSTANT_SETTER_ROLE (MessageProxy)
-
CONSTANT_SETTER_ROLE (CommunityLocker)
-
REGISTRAR_ROLE
-
AUTOMATIC_DEPLOY_ROLE
-
TOKEN_REGISTRAR_ROLE
Owner IMA Mainnet Permissions
Contracts | Functions | Description |
---|---|---|
DepositBoxERC20 DepositBoxERC721 DepositBoxERC1155 |
Enable/disable whitelist (default: enabled). Whitelist allows only specific tokens to be used with the IMA Bridge. |
|
addERC20TokenByOwner addERC721TokenByOwner addERC1155TokenByOwner |
Allows owner to manually map tokens to the bridge. |
|
DepositBoxEth DepositBoxERC20 DepositBoxERC721 DepositBoxERC1155 |
getFunds |
If the |
MessageProxyForMainnet |
registerExtraContract removeExtraContract |
Allows owner to add/remove extra contracts to/from the IMA Bridge. |
Linker |
allowInterchainConnections |
Allows owner to connect other chains (that they own) to the IMA bridge. |
kill |
Allows owner and contract deployer to kill the SKALE Chain IMA Bridge and allow owner to execute getFunds(). Both roles must call this function to execute. Reserved only for extraordinary situations. |
IMA SKALE Chain Permissions
Contracts | Functions | Description |
---|---|---|
ProxyAdmin |
Allows owner to upgrade contracts. |
|
MessageProxyForSchain |
grantRole |
Allows owner to grant |
addConnectedChain removeConnectedChain |
Allows owner or |
|
registerExtraContract removeExtraContract |
Allows owner or |
|
setNewGasLimit |
Allows owner or |
|
TokenManagerEth |
setEthErc20Address |
Allows owner to set a new address for ETHERC20 (wrapped ETH) on SKALE. |
TokenManagerERC20 TokenManagerERC721 TokenManagerERC1155 |
grantRole |
Allows owner to grant |
addTokenManager removeTokenManager |
Allows owner to add/remove other TokenManagers to the IMA Bridge. |
|
changeDepositBoxAddress |
Allows owner to set a new DepositBox address. |
|
enableAutomaticDeploy disableAutomaticDeploy |
Allows owner or |
|
addERC20TokenByOwner addERC721TokenByOwner addERC1155TokenByOwner |
Allows owner or |
|
TokenManagerLinker |
registerTokenManager removeTokenManager |
Allows owner or |
connectSchain disconnectSchain |
Allows owner or |
|
CommunityLocker |
setTimeLimitPerMessage |
Allows owner or |
Enable or Disable the Whitelist
The token whitelist allows the SKALE chain owner to allow token exchange through IMA and a SKALE chain. For example, a decentralized exchange may prefer to disable the whitelist to allow any token transfer. A dApp using only one token may enable the whitelist to allow only a single token exchange.
To disable the whitelist, the owner executes disableWhitelist
in DepositBoxERC20/721/1155 contracts, and pass in the schainName as the only argument. For example:
deposit_box_erc20 = self._get_contract_on_mainnet('deposit_box_erc20')
disable = deposit_box_erc20.encodeABI(fn_name="disableWhitelist", args=[schainName])
signed_txn = self.web3_mainnet.eth.account.signTransaction(dict(
nonce=self.web3_mainnet.eth.getTransactionCount(sender_address),
gasPrice=self.web3_mainnet.eth.gasPrice,
gas=200000,
to=deposit_box_erc20.address,
value=0,
data = disable
),
from_key)
self.web3_mainnet.eth.sendRawTransaction(signed_txn.rawTransaction)
const Web3 = require('web3');
const Tx = require('ethereumjs-tx');
let rinkebyABIs = require("[YOUR_SKALE_ABIs_ON_RINKEBY]");
let privateKey = new Buffer('[YOUR_PRIVATE_KEY]', 'hex')
let account = "[YOUR_ACCOUNT_ADDRESS]";
let rinkeby = "[RINKEBY_ENDPOINT]";
let schainName = "[YOUR_SKALE_CHAIN_NAME]";
let chainID = "[ETHEREUM_CHAIN_ID]";
const depositBoxERC20Address = rinkebyABIs.deposit_box_erc20_address;
const depositBoxERC20ABI = rinkebyABIs.deposit_box_erc20_abi;
const web3 = new Web3(rinkeby);
let contract = new web3.eth.Contract(depositBoxERC20ABI, depositBoxERC20Address);
/*
* prepare the smart contract function
* disableWhitelist(string chainName)
*/
let disableWhitelist = contract.methods
.disableWhitelist(
schainName
)
.encodeABI();
//get nonce
web3.eth.getTransactionCount(account).then(nonce => {
//create raw transaction to send 1 ETH
const rawTx = {
chainId: chainId,
from: account,
nonce: "0x" + nonce.toString(16),
data: disableWhitelist,
to: depositBoxERC20Address,
gas: 200000,
gasPrice: 10000000000
};
//sign transaction
const tx = new Tx(rawTx);
tx.sign(privateKey);
//serialize transaction
const serializedTx = tx.serialize();
//send signed transaction
web3.eth
.sendSignedTransaction("0x" + serializedTx.toString("hex"))
.on("receipt", receipt => {
//record receipt to console
console.log(receipt);
})
.catch(console.error);
});
const Web3 = require('web3');
const Tx = require('ethereumjs-tx');
let rinkebyABIs = require("[YOUR_SKALE_ABIs_ON_RINKEBY]");
let privateKey = new Buffer('[YOUR_PRIVATE_KEY]', 'hex')
let account = "[YOUR_ACCOUNT_ADDRESS]";
let rinkeby = "[RINKEBY_ENDPOINT]";
let skaleChainName = "[YOUR_SKALE_CHAIN_NAME]";
let chainID = "[ETHEREUM_CHAIN_ID]";
const depositBoxERC20Address = rinkebyABIs.deposit_box_erc20_address;
const depositBoxERC20ABI = rinkebyABIs.deposit_box_erc20_abi;
const web3 = new Web3(rinkeby);
let depositBoxERC20 = new web3.eth.Contract(depositBoxERC20ABI, depositBoxERC20Address);
async function disableWhitelist(contract, chainName) {
const disableWhitelistMethod = contract.methods.disableWhitelist(chainName).encodeABI();
const nonce = await web3.eth.getTransactionCount(account);
const rawTX = {
chainId: chainId,
from: account,
nonce: "0x" + nonce.toString(16),
data: disableWhitelistMethod,
to: contract.address,
gas: 200000,
gasPrice: 100000000000
};
const tx = new Tx(rawTx);
tx.sign(privateKey);
const serializedTx = tx.serialize();
const txReceipt = await web3.eth.sendSignedTransaction("0x" + serializedTx.toString("hex"));
console.log("Receipt:", txReceipt);
}
// run function
disableWhitelist(depositBoxERC20, skaleChainName);
// add account and network to the hardhat config file
// run from the console:
// npx hardhat run PATH_TO_SCRIPT --network rinkeby
import { ethers } from "hardhat";
import { promises as fs } from "fs";
const abiFilename = "[YOUR_SKALE_ABIs_ON_RINKEBY]"
const skaleChainName = "[YOUR_SKALE_CHAIN_NAME]";
async function disableWhitelist(schainName: string) {
const abi = JSON.parse(await fs.readFile(abiFilename, "utf-8"));
const [ deployer ] = await ethers.getSigners();
const depositBoxERC20Address = abi["deposit_box_erc20_address"];
const depositBoxERC20Abi = abi["deposit_box_erc20_abi"];
const depositBoxERC20Factory = await ethers.Contract(depositBoxERC20Address, depositBoxERC20Abi, deployer);
const txReceipt = await( await(depositBoxERC20.disableWhitelist(schainName))).wait();
console.log("Receipt:", txReceipt);
}
if (require.main === module) {
disableWhitelist(skaleChainName)
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
}
To re-enable the whitelist:
deposit_box_erc20 = self._get_contract_on_mainnet('deposit_box_erc20')
enable = deposit_box_erc20.encodeABI(fn_name="enableWhitelist", args=[schainName])
signed_txn = self.web3_mainnet.eth.account.signTransaction(dict(
nonce=self.web3_mainnet.eth.getTransactionCount(sender_address),
gasPrice=self.web3_mainnet.eth.gasPrice,
gas=200000,
to=deposit_box_erc20.address,
value=0,
data = enable
),
from_key)
self.web3_mainnet.eth.sendRawTransaction(signed_txn.rawTransaction)
const Web3 = require('web3');
const Tx = require('ethereumjs-tx');
let rinkebyABIs = require("[YOUR_SKALE_ABIs_ON_RINKEBY]");
let privateKey = new Buffer('[YOUR_PRIVATE_KEY]', 'hex')
let account = "[YOUR_ACCOUNT_ADDRESS]";
let rinkeby = "[RINKEBY_ENDPOINT]";
let schainName = "[YOUR_SKALE_CHAIN_NAME]";
let chainID = "[ETHEREUM_CHAIN_ID]";
const depositBoxERC20Address = rinkebyABIs.deposit_box_erc20_address;
const depositBoxERC20ABI = rinkebyABIs.deposit_box_erc20_abi;
const web3 = new Web3(rinkeby);
let contract = new web3.eth.Contract(depositBoxERC20ABI, depositBoxERC20Address);
/*
* prepare the smart contract function
* enableWhitelist(string chainName)
*/
let enableWhitelist = contract.methods
.enableWhitelist(
schainName
)
.encodeABI();
//get nonce
web3.eth.getTransactionCount(account).then(nonce => {
//create raw transaction to send 1 ETH
const rawTx = {
chainId: chainId,
from: account,
nonce: "0x" + nonce.toString(16),
data: enableWhitelist,
to: depositBoxERC20Address,
gas: 200000,
gasPrice: 100000000000
};
//sign transaction
const tx = new Tx(rawTx);
tx.sign(privateKey);
//serialize transaction
const serializedTx = tx.serialize();
//send signed transaction
web3.eth
.sendSignedTransaction("0x" + serializedTx.toString("hex"))
.on("receipt", receipt => {
//record receipt to console
console.log(receipt);
})
.catch(console.error);
});
const Web3 = require('web3');
const Tx = require('ethereumjs-tx');
let rinkebyABIs = require("[YOUR_SKALE_ABIs_ON_RINKEBY]");
let privateKey = new Buffer('[YOUR_PRIVATE_KEY]', 'hex')
let account = "[YOUR_ACCOUNT_ADDRESS]";
let rinkeby = "[RINKEBY_ENDPOINT]";
let skaleChainName = "[YOUR_SKALE_CHAIN_NAME]";
let chainID = "[ETHEREUM_CHAIN_ID]";
const depositBoxERC20Address = rinkebyABIs.deposit_box_erc20_address;
const depositBoxERC20ABI = rinkebyABIs.deposit_box_erc20_abi;
const web3 = new Web3(rinkeby);
let depositBoxERC20 = new web3.eth.Contract(depositBoxERC20ABI, depositBoxERC20Address);
async function enableWhitelist(contract, chainName) {
const enableWhitelistMethod = contract.methods.enableWhitelist(chainName).encodeABI();
const nonce = await web3.eth.getTransactionCount(account);
const rawTX = {
chainId: chainId,
from: account,
nonce: "0x" + nonce.toString(16),
data: enableWhitelistMethod,
to: contract.address,
gas: 200000,
gasPrice: 100000000000
};
const tx = new Tx(rawTx);
tx.sign(privateKey);
const serializedTx = tx.serialize();
const txReceipt = await web3.eth.sendSignedTransaction("0x" + serializedTx.toString("hex"));
console.log("Receipt:", txReceipt);
}
// run function
enableWhitelist(depositBoxERC20, skaleChainName);
// add account and network to the hardhat config file
// run from the console:
// npx hardhat run PATH_TO_SCRIPT --network rinkeby
import { ethers } from "hardhat";
import { promises as fs } from "fs";
const abiFilename = "[YOUR_SKALE_ABIs_ON_RINKEBY]"
const skaleChainName = "[YOUR_SKALE_CHAIN_NAME]";
async function enableWhitelist(schainName: string) {
const abi = JSON.parse(await fs.readFile(abiFilename, "utf-8"));
const [ deployer ] = await ethers.getSigners();
const depositBoxERC20Address = abi["deposit_box_erc20_address"];
const depositBoxERC20Abi = abi["deposit_box_erc20_abi"];
const depositBoxERC20Factory = await ethers.Contract(depositBoxERC20Address, depositBoxERC20Abi, deployer);
const txReceipt = await( await(depositBoxERC20.enableWhitelist(schainName))).wait();
console.log("Receipt:", txReceipt);
}
if (require.main === module) {
enableWhitelist(skaleChainName)
.then(() => process.exit(0))
.catch(error => {
console.error(error);
process.exit(1);
});
}
Adding tokens with Enabled Whitelist
If the whitelist is enabled, the SKALE chain owner must authorize each token for the schain.
You can use addERC20TokenByOwner
, addERC721TokenByOwner
, and addERC1155TokenByOwner
functions available in DepositBoxERC20, DepositBoxERC721, DepositBox1155, and TokenManagerERC20, TokenManagerERC721, TokenManagerERC1155 contracts on both Mainnet and each SKALE chain.
The recommended process is
-
Deploy the ERC20/ERC721/ERC1155 token contract on the SKALE chain.
-
Add the token to TokenManagerERC20/TokenManagerERC721/TokenManager1155 on the SKALE chain.
-
Grant the token’s minter role to TokenManagerERC20/TokenManagerERC721/TokenManager1155 on the SKALE chain.
-
Add the token to DepositBoxERC20/DepositBoxERC721/DepositBox1155 on Mainnet.
Adding tokens to TokenManager
Adding ERC20
token_manager_erc20 = self._get_contract_on_schain('token_manager_erc20')
addERC20Schain = token_manager_erc20.encodeABI(fn_name="addERC20TokenByOwner", args=[erc20MainnetAddress, erc20SchainAddress])
signed_txn = self.web3_schain.eth.account.signTransaction(dict(
nonce=self.web3_schain.eth.getTransactionCount(sender_address),
gasPrice=self.web3_schain.eth.gasPrice,
gas=200000,
to=token_manager_erc20.address,
value=0,
data = addERC20Schain
),
from_key)
self.web3_schain.eth.sendRawTransaction(signed_txn.rawTransaction)
Adding ERC721
token_manager_erc721 = self._get_contract_on_schain('token_manager_erc721')
addERC721Schain = token_manager_erc721.encodeABI(fn_name="addERC721TokenByOwner", args=[erc721MainnetAddress, erc721SchainAddress])
signed_txn = self.web3_schain.eth.account.signTransaction(dict(
nonce=self.web3_schain.eth.getTransactionCount(sender_address),
gasPrice=self.web3_schain.eth.gasPrice,
gas=200000,
to=token_manager_erc721.address,
value=0,
data = addERC721Schain
),
from_key)
self.web3_schain.eth.sendRawTransaction(signed_txn.rawTransaction)
Assign Schain TokenManager as Minter and Burner role
You need to assign TokenManagerERC20/TokenManagerERC721/TokenManagerERC1155 as the minter and burner for the deployed token on the schain. For AccessControl supported ERC20/ERC721/ERC1155, you can use apply the following pseudocode:
Adding tokens to DepositBox
addERC20TokenByOwner , addERC721TokenByOwner , addERC1155TokenByOwner on the DepositBox takes 2 arguments: schainName and erc20Mainnet/erc721Mainnet/erc1155Mainnet address.
|
Adding ERC20
deposit_box_erc20 = self._get_contract_on_mainnet('deposit_box_erc20')
addERC20Mainnet = deposit_box_erc20.encodeABI(fn_name="addERC20TokenByOwner", args=[schainName, erc20MainnetAddress])
signed_txn = self.web3_mainnet.eth.account.signTransaction(dict(
nonce=self.web3_mainnet.eth.getTransactionCount(sender_address),
gasPrice=self.web3_mainnet.eth.gasPrice,
gas=200000,
to=deposit_box_erc20.address,
value=0,
data = addERC20Mainnet
),
from_key)
self.web3_mainnet.eth.sendRawTransaction(signed_txn.rawTransaction)
Adding ERC721
addERC721TokenByOwner on the DepositBox takes 2 arguments: schainName and erc721Mainnet address.
|
deposit_box_erc721 = self._get_contract_on_mainnet('deposit_box_erc721')
addERC721Mainnet = deposit_box_erc721.encodeABI(fn_name="addERC721TokenByOwner", args=[schainName, erc721MainnetAddress])
signed_txn = self.web3_mainnet.eth.account.signTransaction(dict(
nonce=self.web3_mainnet.eth.getTransactionCount(sender_address),
gasPrice=self.web3_mainnet.eth.gasPrice,
gas=200000,
to=deposit_box_erc721.address,
value=0,
data = addERC20Mainnet
),
from_key)
self.web3_mainnet.eth.sendRawTransaction(signed_txn.rawTransaction)
Automatic deployment
Automatic deployment is disabled by default and requires that the SKALE Chain owner deploys the token contract on the SKALE Chain.
If enabled, then tokens are automatically deployed on the schain by the TokenFactory contract after a token is received through DepositBox on mainnet.
There are several limits to using automatic deployment. When using this method:
|