Using Gnosis SAFE Multisig
You can use Ethereum-based Gnosis SAFE to assign SKALE Chain ownership and use it to cross-chain configure the SKALE Chain using the IMA bridge. administration functions.
Preparation to SKALE chain creation
No custom steps are required to create a SKALE chain that Gnosis SAFE controls. Ensure that you know the address of your Gnosis SAFE deployed on Ethereum mainnet; if you don’t have one follow https://help.gnosis-safe.io/en/articles/3876461-create-a-safe to create it.
Once you have deployed a Gnosis SAFE, you can use its address as the SKALE chain owner. If you assign ownership to a SAFE or any other contract, you must also provide a backup address which is assigned as the owner of the backup (predeployed) Multisig on the SKALE Chain.
SKALE chain owner address can’t change after deployment. |
When assigning SKALE Chain ownership to a SAFE, the ADMIN Roles for all predeployed contracts on the SKALE Chain are assigned to Marionette AND to the Backup multisig. |
Calling functions on a SKALE chain
You can use the multisigwallet-cli to simplify this process.
Installation
Clone and install multisigwallet-cli
.
git clone https://github.com/skalenetwork/multisigwallet-cli.git
yarn install
Setup
Change directory into multisigwallet-cli`
folder and create a .env
file:
cd multisigwallet-cli
touch .env
Add the following environment variables to the .env
file:
ENDPOINT=http://localhost:8545
PRIVATE_KEY_1={private key}
You can set any values. See this issue |
Prepare transaction data
Call encodeData command of multisigwallet-cli
.
For example, to prepare a call of grantRole
function of Etherbase
smart contract on SKALE chain example-chain
with parameters grantRole(0xe0ba7b49edc651b7ad93b374c67f1e9a0d37370168bbb86b81c569ebfa15f046, 0xd2D2D2D2d2d2d2d2D2d2d2d2D2d2D2d2D2d2D2d2)
(where 0xe0ba7b49edc651b7ad93b374c67f1e9a0d37370168bbb86b81c569ebfa15f046
is an id of ETHER_MANAGER_ROLE
) execute the following:
npx msig encodeData example-chain Etherbase grantRole 0xe0ba7b49edc651b7ad93b374c67f1e9a0d37370168bbb86b81c569ebfa15f046 0xd2D2D2D2d2d2d2d2D2d2d2d2D2d2D2d2D2d2D2d2
It will return a long hex string.
Submitting transaction to Gnosis Safe
-
Go to Gnosis Safe UI and press the New transaction →
Contract interaction
. -
Enable the checkbox
Use custom data (hex encoded)
. -
Put the IMA address for
message_proxy_mainnet_address
to theContract address
field.This MessageProxy Mainnet address on Ethereum is 0x8629703a9903515818C2FeB45a6f6fA5df8Da404
. This address probably won’t change, but it’s better to ensure visiting Releases repo. In this repo, you can find the addresses of IMA on different Ethereum testnets. -
Enter
0
in theValue
field. -
Copy the hex string obtained in the previous step from
multisigwallet-cli
to theData
field. -
Review and submit the transaction.
Execution of the transaction
After signing, execute the transaction. Be patient: wait for IMA to pick up the transaction and execute the call. This process can take up to several minutes.
Deeper explanation
The following section isn’t necessary to control a SKALE chain with Gnosis SAFE via multisigwallet-cli
but may help integrate with other products. This section describes how to encode a SKALE chain smart contract call into an Ethereum mainnet transaction for sending through IMA.
Marionette
There is a Marionette smart contract that’s predeployed on SKALE chains at address 0xD2c0DeFACe000000000000000000000000000000
.
It’s granted all administration rights to all ADMIN roles of SKALE Chain predeployed contracts and serves as a proxy to forward calls sent via IMA.
Marionette has a function postMessage(bytes32 sourceChain, address sender,bytes calldata encodedCall)
that IMA calls. It checks that a sender is a SKALE chain owner and performs a call encoded in encodedCall
parameter.
encodedCall
is a triplet (address receiver, uint value, bytes calldata data)
encoded to bytes as arguments according to Contract ABI Specification (See encodeFunctionCall function of Marionette
).
Here:
-
receiver
is a target contract -
value
is the amount of sFuel transferred in the transaction -
data
is a call data
In the example above, grantRole
of Etherbase
smart contract is called. In this case:
-
receiver
is0xd2bA3e0000000000000000000000000000000000
(the address ofEtherbase
) -
value is equal to
0
because sFUEL isn’t needed -
data is equal to
0x2f2ff15de0ba7b49edc651b7ad93b374c67f1e9a0d37370168bbb86b81c569ebfa15f046000000000000000000000000d2D2D2D2d2d2d2d2D2d2d2d2D2d2D2d2D2d2D2d2
(grantRole
function selector0x2f2ff15d
+ETHER_MANAGER_ROLE
id0xe0ba7b49edc651b7ad93b374c67f1e9a0d37370168bbb86b81c569ebfa15f046
+ padded address parameter0x000000000000000000000000d2D2D2D2d2d2d2d2D2d2d2d2D2d2D2d2D2d2D2d2
).
Accordingly, encodedCall
is abi.encode(receiver, value, data)
and equals:
000000000000000000000000d2ba3e0000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000060
0000000000000000000000000000000000000000000000000000000000000044
2f2ff15de0ba7b49edc651b7ad93b374c67f1e9a0d37370168bbb86b81c569eb
fa15f046000000000000000000000000d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2
d2d2d2d200000000000000000000000000000000000000000000000000000000
IMA
Omitting details, there is a MessageProxyForMainnet smart contract deployed on Ethereum with the function postOutgoingMessage(bytes32 targetChainHash, address targetContract, bytes memory data)
. Call of this function causes execution of function postMessage
of a smart contract with address targetContract
on SKALE chain where the hash of its name is targetChainHash
.
In this example, postOutgoingMessage
receives the following parameters:
-
targetChainHash -
0x7e67eb6444a60ce618f250a380d5b7b32e7b5dbb96b0d43506047b1f15c8f23c
- keccak256 hash of SKALE chain nameexample-chain
-
targetContract -
0xD2c0DeFACe000000000000000000000000000000
address ofMarionette
smart contract -
data - encoded call to
grantRole
function ofEtherbase
smart contract (see previous section)
Summary
Sending a transaction with data
94489202
7e67eb6444a60ce618f250a380d5b7b32e7b5dbb96b0d43506047b1f15c8f23c
000000000000000000000000d2c0deface000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000060
00000000000000000000000000000000000000000000000000000000000000e0
000000000000000000000000d2ba3e0000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000060
0000000000000000000000000000000000000000000000000000000000000044
2f2ff15de0ba7b49edc651b7ad93b374c67f1e9a0d37370168bbb86b81c569eb
fa15f046000000000000000000000000d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2
d2d2d2d200000000000000000000000000000000000000000000000000000000
from Gnosis SAFE to MessageProxyForMainnet
calls
postOutgoingMessage(
"0x7e67eb6444a60ce618f250a380d5b7b32e7b5dbb96b0d43506047b1f15c8f23c", // SKALE chain name hash
"0xD2c0DeFACe000000000000000000000000000000" // Marionette address,
"0x0000000000000000000000000000000000000000000000000000000000000060" +
"00000000000000000000000000000000000000000000000000000000000000e0" +
"000000000000000000000000d2ba3e0000000000000000000000000000000000" +
"0000000000000000000000000000000000000000000000000000000000000000" +
"0000000000000000000000000000000000000000000000000000000000000060" +
"0000000000000000000000000000000000000000000000000000000000000044" +
"2f2ff15de0ba7b49edc651b7ad93b374c67f1e9a0d37370168bbb86b81c569eb" +
"fa15f046000000000000000000000000d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2" +
"d2d2d2d200000000000000000000000000000000000000000000000000000000" // encoded call to grantRole of Etherbase
)
In the next step, IMA securely transfers the message to example-chain
and triggers execution of the Marionette
function:
postMessage(
{mainnet id}, // source chain
{Gnosis Safe address}, // message sender address,
"0x0000000000000000000000000000000000000000000000000000000000000060" +
"00000000000000000000000000000000000000000000000000000000000000e0" +
"000000000000000000000000d2ba3e0000000000000000000000000000000000" +
"0000000000000000000000000000000000000000000000000000000000000000" +
"0000000000000000000000000000000000000000000000000000000000000060" +
"0000000000000000000000000000000000000000000000000000000000000044" +
"2f2ff15de0ba7b49edc651b7ad93b374c67f1e9a0d37370168bbb86b81c569eb" +
"fa15f046000000000000000000000000d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2" +
"d2d2d2d200000000000000000000000000000000000000000000000000000000" // encoded call to grantRole of Etherbase
)
Then Marionette
checks permissions, decodes the call and executes it. In this case, it calls Etherbase
:
grantRole(
"0xe0ba7b49edc651b7ad93b374c67f1e9a0d37370168bbb86b81c569ebfa15f046", // id of ETHER_MANAGER_ROLE
"0xd2D2D2D2d2d2d2d2D2d2d2d2D2d2D2d2D2d2D2d2" // target address
)