paint-brush
Multisig - the RSK wayby@rootstock_io
220 reads

Multisig - the RSK way

by RootstockJuly 27th, 2021
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

The term "multisig" stands for "multi-signature" and it's used to describe accounts associated with more than one private key. The account is created with a list of owners and a policym-out-of-n                ; when transactions are approved by at least m owners, they can be executed. The multisig account eliminates the single point of failure in case of attacks and it allows the implementation of a variety of use cases where one or many parties are involved.

Companies Mentioned

Mention Thumbnail
Mention Thumbnail

Coin Mentioned

Mention Thumbnail
featured image - Multisig - the RSK way
Rootstock HackerNoon profile picture

What does multisig mean?

The term "multisig" stands for "multi-signature" and it's used to describe accounts associated with more than one private key. The account is created with a list of owners and a policy

m-out-of-n
; when transactions are approved by at least m owners, they can be executed.

In blockchain each transaction involves the usage of public-key signature; each blockchain participant owns a private/public key pair. The participants use the private keys to sign a transaction and all other participants can verify that signature using the signer's public key.

With a multisig account, each transaction requires the signature of many accounts before being executed. One participant (who is among the multisig account owners) can start the transaction, but only when the other owners approve that transaction, it will be executed.

Why?

The multisig account eliminates the single point of failure in case of attacks and it allows the implementation of a variety of use cases where one or many parties are involved. Here below some examples.

Redundancy: if a user loses the key, the funds are not lost because the other keys can still be used.

2FA (two-factor authentication): a single user can have both a web wallet and a mobile wallet associated with a multisig account, policy 2-of-2.

Departments approval: two departments must approve each transaction before being published, policy 2-of-2.

Parents' saving account: a kid can spend money only with the approval of either parent.

What we are going to do

  1. Create a multisig account
  2. Transfer funds to the multisig account
  3. Create multisig transactions
  4. Approve multisig transactions
  5. Execute multisig transactions
  6. Reject multisig transactions
  7. Update multisig account (owners and threshold)

On the repository you can find an example with some of the features listed above, you can clone it and run it locally.

  1. clone the node sample app repository:
    git clone [email protected]:rsksmart/rif-multisig-node-sample.git
  2. install dependencies:
    npm install
  3. start the ganache network:
    npm run network:local
  4. npm run start
    to execute the main script

RSK solution architecture

Libraries:

Smart contracts:

Rest API:

  • Safe transaction service: it collects all the multisig information using
    trace_transaction
    and
    trace_blocks
    . Furthermore, it allows collecting off-chain signatures and retrieving multisig pending transactions.

Repository:

Let’s start

Create a multisig account

Initialize the signer:

const provider = new providers.JsonRpcProvider()
const signer = await provider.getSigner(0)


Before creating a multisig account, it requires that the contracts have been deployed to the network. Using one of the RSK networks (mainnet or testnet), you can use the addresses of the pre-deployed contracts.

import { EthersSafeFactory } from '@rsksmart/safe-factory-sdk'

const safeFactory = new EthersSafeFactory(
 signer,
 proxyFactoryAddress,
 safeSingletonAddress
)

const safeSdk = await safeFactory.createSafe({
 owners: [signer.getAddress()],
 threshold: 1
})

Transfer funds to the multisig account

It is important to understand that, to receive funds, we have to use the multisig account address (instead of the owner's address) as a receiver.

// previously created safe
const safeSdk: Safe;

// address to use as fund receiver
const multisigAccountAddress = safeSdk.getAddress()

Create multisig transactions

There are several ways to create multisig transactions according to our needs.

If we need to create raw transactions we could use the createTransaction method on the Safe instance:

const partialTx: SafeTransactionDataPartial = {
 to: '0x<address>',
 data: '0x<data>',
 value: '<eth_value_in_wei>'
}
const safeTransaction = await safeSdk.createTransaction(partialTx)

or we could use the

RawTransactionBuilder
:

import { RawTransactionBuilder } from '@rsksmart/safe-transactions-sdk'

const rawTransactionBuilder = new RawTransactionBuilder(safe)
const safeTransaction = await rawTransactionBuilder.rawTransaction(to, value, data)

ERC20 transactions

Similarly, we can create ERC20-related transactions, including RIF Token.

import { ERC20TransactionBuilder } from '@rsksmart/safe-transactions-sdk'

const erc20TransactionBuilder = ERC20TransactionBuilder.create(safe, ERC20Token)

// create a `transfer` transaction
const transferTx = await erc20TransactionBuilder.transfer(
 to,
 transfer
)

// create a `transferFrom` transaction
const transferFromTx = await erc20TransactionBuilder.transferFrom(
 from,
 to,
 value
)

ERC721 transactions

The package @rsksmart/safe-transactions-sdk provides also an easy way to create ERC721 transactions.

import { ERC721TransactionBuilder } from "@rsksmart/safe-transactions-sdk";

const erc721TransactionBuilder = ERC721TransactionBuilder.create(
 safe,
 ERC721Token
);

// create a `transferFrom` transaction
const transferFromTx = await erc721TransactionBuilder.transferFrom(
 from,
 to,
 tokenId
);

// create a `safeTransferFrom` transaction
const safeTransferFromTx = await erc721TransactionBuilder.safeTransferFrom(
 from,
 to,
 tokenId
);

Approve multisig transactions

Transaction approval can take place both on-chain and off-chain. With the on-chain approval, the signature is added interacting with the safe account, hence the signature is available in the blockchain. Off-chain approval requires the user to publish the signature through the Safe Transaction Service to make it available to other participants.

On-chain approval

const txHash = await safeSdk.getTransactionHash(safeTransaction)
const approveTxResponse = await safeSdk.approveTransactionHash(txHash)
await approveTxResponse.wait()

Once the transaction is approved on-chain, user can retrieve the list of owners with the

getOwnersWhoApprovedTx
method.

const ownersWhoApproved = await safeSdk.getOwnersWhoApprovedTx(txHash)

Off-chain approval

The user needs to create the signature first.

const signature = await safeSdk.signTransaction(safeTransaction)

Once the signature is generated, the user must publish the signature using the Safe Transaction Service.

const safeService = new SafeServiceClient(SAFE_TRANSACTION_SERVICE_URL)

const safeTxHash = await safeCoreSdk.getTransactionHash(transaction)
await safeServiceClient.confirmTransaction(safeTxHash, signature.data)

Execute multisig transactions

Once a transaction is approved by a number of owners that must be equal or greater than the threshold set, it can be executed.

const txResponse = await safeSDk.executeTransaction(safeTransaction)
await txResponse.wait()

Reject multisig transactions

Transaction rejection implies the creation and execution of a new transaction with the same nonce like the one we want to reject. Once the rejection transaction is created, it must be approved and executed like any other multisig transaction.

For instance, if the current threshold is set to 2, rejecting a transaction requires the following steps:

  1. the creation of a rejection transaction
  2. the approval of the rejection transaction by at least 2 owners
  3. the execution of the rejection transaction.
import { rejectTx } from '@rsksmart/safe-transactions-sdk'

// creation of the rejection transaction
const rejectionTx = await rejectTx(safe, transaction)

Update multisig account (owners and threshold)

As per any other transaction, changing the main properties of a multisig account requires the creation of a new transaction and its approval, before being executed.

// Create a transaction to add a new owner
const ownerTx = await safe.getAddOwnerTx(newOwner, newThreshold)
// Create a transaction to remove an owner
const ownerTx = await safe.getRemoveOwnerTx(existingOwner, newThreshold)
// Create a transaction to replace an owner
const ownerTx = await safe.getSwapOwnerTx(oldOwner, newOwner)

It is important to keep in mind that

newThreshold
must be set according to the current owners. If the current owners are three, and we add a new owner, the
newThreshold
cannot be set to four, but it can be at most three, as the current number of owners.

const thresholdTx = await safe.getChangeThresholdTx(newThreshold)

Eventually, it’s also possible to change the

threshold
without changing the owners.

The operations listed above won’t change the safe account, they create the related transactions. The changes will be applied only after the transaction is executed.

Glossary

  • multisig: account or transaction requiring multiple signatures before being executed
  • threshold: the number of signatures required to execute a transaction
  • owner: address allowed to execute operations on a multisig account

References

Do you have further questions?

Please contact us through our open Slack for technical questions and support.

You can also reach out with any feedback you would like to share with us through our social media channels and forums: