1.0.21 • Published 2 years ago

@parrotfi/msig v1.0.21

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

Parrot Multisig Toolkit

Parrot's multisig workflow

  • makes it easier to audit instruction data and account inputs using a whitelist
  • makes it easy to verify and approve multiple multisig transactions at once
  • extends the multisig workflow with application specific instructions

Tutorial

  • Generate three wallets to become the signers of a multisig
  • Create a 2/3 multisig
  • Send 1 USDC to the multisig
  • Send 1 USDC from the multisig to a receiving wallet
    • Use key1 to create the multisig transaction
    • Use key2 to approve and execute the multisig transaction

Create Wallets

If you have existing wallets you can use those, or you can generate new ones by using the genkey command.

Let's generate a keyfile called id1.json:

msig genkey id1.json
Public key:
FGeVJVMUBX4GmYghZR9e7FqCa8FVZmqJsqczxxauKTjb
Private key (hex):
c40a398d04d4c7bce56b5593c844ad4ce0eb2dc9316c67ea18add4ff57e41637d405b7850880cc49cc109f276cec448cc0859d6656dc59eaa77515dc30d3073e
Private key (json):
[
  196,  10,  57, 141,   4, 212, 199, 188, 229, 107,  85,
  147, 200,  68, 173,  76, 224, 235,  45, 201,  49, 108,
  103, 234,  24, 173, 212, 255,  87, 228,  22,  55, 212,
    5, 183, 133,   8, 128, 204,  73, 204,  16, 159,  39,
  108, 236,  68, 140, 192, 133, 157, 102,  86, 220,  89,
  234, 167, 117,  21, 220,  48, 211,   7,  62
]

You would get a different address and private key. Keep the keyfile and private key secure!

Use the same command to generate two more keys:

msig genkey id2.json
msig genkey id3.json

For testing purposes, you could generate all three keys locally. For a production multisig, each participant should generate their own key.

Create A Multisig

Let's setup a 2/3 wallet with the following keys:

id1: 9GFcTi42f7fdCFhSoA7Ycqr64PbUAPnot77fqwoq3MGn
id2: ABvMxYveW9V8CXa5thSzGENEGbrktC652WxPfiX9HQ8o
id3: Cq3tX7P3vThPG8Rk39toQfgAUzpHY5xZMt9UT7gTVBc7

Use id1.json as the wallet to create a 2/3 multisig, with the pubkeys that you'd want to use:

# replace the pubkeys with your own

msig setup --wallet id1.json --threshold 2 --owners \
  9GFcTi42f7fdCFhSoA7Ycqr64PbUAPnot77fqwoq3MGn \
  ABvMxYveW9V8CXa5thSzGENEGbrktC652WxPfiX9HQ8o \
  Cq3tX7P3vThPG8Rk39toQfgAUzpHY5xZMt9UT7gTVBc7

Once the transaction is confirmed, you'd get a multisig address, and a multisig PDA:

msig wallet address (don't send tokens to this address):
9he5FHBkLtEFGQeBA6xBubWbbYoAdAX3DrjAmEiUvi7S

msig wallet PDA (send tokens here):
D6D7ADuNvKgAJkai9AURfGHjKBK4iMZxxvcAYMhiuDE1

The multisig PDA is the address where you can send tokens, or use as the authority signer for various instructions.

Inspect the multisig address with the info command:

multisig: 9he5FHBkLtEFGQeBA6xBubWbbYoAdAX3DrjAmEiUvi7S (don't send tokens to this address)
threshold: 2
ownerSetSeqno: 0
owners: 9GFcTi42f7fdCFhSoA7Ycqr64PbUAPnot77fqwoq3MGn,ABvMxYveW9V8CXa5thSzGENEGbrktC652WxPfiX9HQ8o,Cq3tX7P3vThPG8Rk39toQfgAUzpHY5xZMt9UT7gTVBc7
PDA: D6D7ADuNvKgAJkai9AURfGHjKBK4iMZxxvcAYMhiuDE1

And it should display the same info as you have specified.

Send 1 USDC To The Multisig

The "multisig PDA" is the address that acts as the signer of the multisig. You can treat it like a wallet public address, and send tokens to it.

Here is an example transaction of sending 1 USDC to the multisig PDA:

https://solscan.io/tx/5jqCRbSd8PbBU5cJgdL1bw3YvqXHpGPeAwmFYaJVzGxDu7swZBwxH2frt6bbC6fAwGZhsuxjFdDT4tfhvJyDR2ag

Create Proposals

Let's create proposals to transfer USDC tokens out of the multisig to the id1 wallet. This multisig tool is designed to make it easy to create, verify, and approve multiple transactions at once. So let's try to create 2 separate transactions to send out USDC:

const { u64 } = require("@solana/spl-token")
const { PublicKey } = require("@solana/web3.js")
const { TransferTokenToOwner } = require("@parrotfi/msig")

// Maintain known/verified addresses, to make it easier to review proposed
// transactions.
const known = {
  mints: {
    usdc: new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"),
  },

  wallets: {
    parrot: new PublicKey("9GFcTi42f7fdCFhSoA7Ycqr64PbUAPnot77fqwoq3MGn"),
  },
}

module.exports = {
  multisig: new PublicKey("9he5FHBkLtEFGQeBA6xBubWbbYoAdAX3DrjAmEiUvi7S"),

  transactions: [
    new TransferTokenToOwner(
      // The memo string must be unique for a multisig. Use a timestamp as
      // prefix (by convention) to ensure its uniqueness.
      "2021-11-08T20:02:28+08:00 transfer 0.3",
      {
        mint: known.mints.usdc,
        toOwner: known.wallets.parrot,
      },
      new u64(0.3 * 1e6), // 0.3 USDC
    ),
    new TransferTokenToOwner(
      // The memo string must be unique for a multisig. Use a timestamp as
      // prefix (by convention) to ensure its uniqueness.
      "2021-11-08T20:02:28+08:00 transfer 0.7",
      {
        mint: known.mints.usdc,
        toOwner: known.wallets.parrot,
      },
      new u64(0.7 * 1e6), // 0.7 USDC
    ),
  ],
}

Then use the create command to create these multisig transactions:

msig --wallet id1.json create proposals.js

You should see that the multisig transactions have been created, and are pending approval:

create multisig tx:  3UG14FnQqkovUnBmqVxV1J6VUBJTMJB3yoFniKkN5Q2u {
  "memo": "2021-11-08T20:02:28+08:00 transfer 0.3",
  "accounts": {
    "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    "toOwner": "9GFcTi42f7fdCFhSoA7Ycqr64PbUAPnot77fqwoq3MGn"
  },
  "amount": "0493e0"
}
0  Cddq98QAr8yokd9dfMA6eTnFducVSrMd7RRSAiYrmkb   w: y s: n
1  A7ZKwPkya1h4PsJdjD9PjaYR1gtPVt4DnnVtULYxKspq  w: y s: n
2  D6D7ADuNvKgAJkai9AURfGHjKBK4iMZxxvcAYMhiuDE1  w: n s: y

create multisig tx:  FKZQ3hNg1Uq85yMNGHjQxJi2LQFRdBgygxoZs4Y36fdM {
  "memo": "2021-11-08T20:02:28+08:00 transfer 0.7",
  "accounts": {
    "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    "toOwner": "9GFcTi42f7fdCFhSoA7Ycqr64PbUAPnot77fqwoq3MGn"
  },
  "amount": "0aae60"
}
0  Cddq98QAr8yokd9dfMA6eTnFducVSrMd7RRSAiYrmkb   w: y s: n
1  A7ZKwPkya1h4PsJdjD9PjaYR1gtPVt4DnnVtULYxKspq  w: y s: n
2  D6D7ADuNvKgAJkai9AURfGHjKBK4iMZxxvcAYMhiuDE1  w: n s: y

Verify Proposals

The first wallet has created the transaction, now another owner of the multisig needs to verify and approve the transactions.

Given the same proposals.js, the second wallet can should verify that the multisig transactions created on-chain indeed have the right input accounts, and the correct instruction data:

msig --wallet id2.json verify proposals.js

=======>> verify 2021-11-08T20:02:28+08:00 transfer 0.3 3UG14FnQqkovUnBmqVxV1J6VUBJTMJB3yoFniKkN5Q2u
 PASSED
=======>> verify 2021-11-08T20:02:28+08:00 transfer 0.7 FKZQ3hNg1Uq85yMNGHjQxJi2LQFRdBgygxoZs4Y36fdM
 PASSED

We see that the verifications have passed.

Approve Proposals

Once verified, the second signer can choose to approve the transaction. Note that if the treshold is reached, this command would also execute the transactions:

msig --wallet id2.json approve proposals.js
======>> approve/execute 2021-11-08T20:02:28+08:00 transfer 0.3 3UG14FnQqkovUnBmqVxV1J6VUBJTMJB3yoFniKkN5Q2u
=======>> verify 2021-11-08T20:02:28+08:00 transfer 0.3 3UG14FnQqkovUnBmqVxV1J6VUBJTMJB3yoFniKkN5Q2u
 PASSED
execute txid: Xfm347vyzChLFSThkEqXPnSzdjc6Z3QBBvZoDkN9tQm3yoxn4ZQd5kskb3fRZ2hSEnZKtynLeNW1h1fQWsrWR1K
======>> approve/execute 2021-11-08T20:02:28+08:00 transfer 0.7 FKZQ3hNg1Uq85yMNGHjQxJi2LQFRdBgygxoZs4Y36fdM
=======>> verify 2021-11-08T20:02:28+08:00 transfer 0.7 FKZQ3hNg1Uq85yMNGHjQxJi2LQFRdBgygxoZs4Y36fdM
 PASSED
execute txid: 2yGrmQHV2KCSjSmJjymekG226fWL3TcEShiy7VhEe3RbRv3RHqDWtEDEF5FPs7XSTe1VeZQL3n7CXJvGca1hZW8Z

If you dig into the actual transactions, you would see that the multisig PDA is used to authorize the USDC transfer.

Also, since the transactions have already been executed, they would be skipped if encountered again:

msig --wallet id2.json verify proposals.js

2021-11-08T20:02:28+08:00 transfer 0.3 already executed, skip verify
2021-11-08T20:02:28+08:00 transfer 0.7 already executed, skip verify

Each multisig proposal would never be executed more than once.

1.0.21

2 years ago

1.0.19

2 years ago

1.0.18

2 years ago

1.0.17

2 years ago

1.0.20

2 years ago

1.0.16

2 years ago

1.0.15

2 years ago

1.0.14

2 years ago

1.0.13

2 years ago

1.0.12

2 years ago

1.0.11

2 years ago

1.0.10

2 years ago

1.0.9

2 years ago

1.0.8

2 years ago

1.0.7

2 years ago

1.0.6

2 years ago

1.0.5

2 years ago

1.0.4

3 years ago

1.0.3

3 years ago

1.0.1

3 years ago

1.0.0

3 years ago