dYdX Chain Technical Docs
  • πŸ‘‹dYdX Chain Technical Documentation
  • Getting Started
    • πŸ’‘Developer Tools
      • dYdX Chain Client for Javascript
        • Example #1: Accessing Subaccount Data
        • Example #2: Faucet Endpoints
        • Additional Javascript Client Examples
      • dYdX Chain Client for Python
        • Example #1: Placing, Replacing, and Canceling Orders
        • Exmaple #2: Trading with HRN
        • Additional Python Client Examples
      • dYdX CLI
        • Setup the dYdX CLI
        • EOA Wallets
        • Multisig wallet
        • Submit a governance proposal
    • ✨Developer Guides
    • 🦊User Guides
      • How to use Squid UI for cross-chain deposits to dYdX V4
      • How to use CCTP UI for cross-chain deposits to dYdX V4
      • How to withdraw and transfer funds from a subaccount using Command Line
    • ❓FAQ & Resources
    • πŸ–₯️Open Source Repositories
  • Validators
    • πŸ› οΈGetting set up
      • Hardware Requirements
      • Required Node Configs
      • Running a Validator
      • Running a Full Node
      • Snapshots
      • Validator Upgrades
    • ❓FAQ & Resources
    • πŸ§‘Active Validator Slack
  • Front End & Wallets
    • πŸ“šArchitecture Overview
    • 🌎Web Front End
    • ❓FAQ & Resources
  • Data Dashboards & Open APIs
    • πŸ“ŠPublic Dashboards
    • ❓FAQ & Resources
  • Disclaimers
    • πŸ“„Disclaimer
    • πŸ”“Privacy Policy
Powered by GitBook
On this page
  • Prerequisites
  • Instantiate a multisig
  • 1. Add wallets locally
  • 2. Instantiate the multisig
  • 3. EXAMPLE scenario multisig
  • 4. Create send token Tx
  • 5. Sign Token Tx
  • 6. Combine signatures
  • 7. Broadcast Send Tokens Tx
  1. Getting Started
  2. Developer Tools
  3. dYdX CLI

Multisig wallet

PreviousEOA WalletsNextSubmit a governance proposal

Last updated 1 year ago

Prerequisites

Instantiate a multisig

Note that a multisig is defined solely by the ordered list of wallets that are parts of it and its threshold - meaning that so long as the input parameter are unchanged, the same multisig address can be instantiated anywhere, including on tools like

1. Add wallets locally

πŸ’‘ You do not need to own a wallet that is part of the multisig to instantiate it locally - adding only offline ones is

To verify your local setup, list all wallets with

dydxprotocold keys show

As an example, let’s imagine we have a generated wallet A with, e.g.:

dydxprotocold keys add walletA 

And have added offline walletB and walletC

dydxprotocold keys add walletB --pubkey='{"@type":"/cosmos.crypto.secp256k1.PubKey"
dydxprotocold keys add walletC --pubkey='{"@type":"/cosmos.crypto.secp256k1.PubKey",

2. Instantiate the multisig

You need the names of the wallets, e.g. walletA , walletB and walletC and the threshold. ourMultisig is an arbitrary name, choose one that is meaningful to you

$ dydxprotocold keys add ourMultisig --multisig="walletA,walletB,walletC" --multisig-thr

- address: dydx1d02xdy...sw9fcq7jerkwe
  name: ourMultisig
  pubkey: '{"@type":"/cosmos.crypto.multisig.LegacyAminoPubKey","threshold":2,"public_keys":[{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A/C+dj94...9SCD2Uu11"},{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A9QEyr+iQoCI...IP76IjzVG2vEMf"},{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"ArarpFkTpgJOnjvUen...KMU5c2zbQ0"}]}'
  type: multi

Now let's send some small funds to the multisig address in order to activate the address.

It is important to activate the address by sending a small amount of tokens to the address otherwise the network will not recognise the multisig address.

$ dydxprotocold tx bank send test dydx1z79c98w069eugzjme0dkd6quuhm8vfh2a00smh \
    100000000000000000adv4tnt \
    --fees 5000000000000000adv4tnt \
    --node https://dydx-testnet-rpc.polkachu.com:443 \
    --chain-id dydx-testnet-4

3. EXAMPLE scenario multisig

Goal: send 700000000000000000adydx (0.7 DYDX) from multisig dydx1z79c98w069eugzjme0dkd6quuhm8vfh2a00smh (added as ourMultisig ) to address dydx1h6x6pxkv2fv3jzsx5hc5efaarhue7gpsdmxafk

πŸ’‘ Transferring tokens to a multisig address does not differ from transferring tokens to other address types

Let’s say that we have

  1. WalletA , dydx18ym0ts7fw40x2qqw73plgg7wgmkljvvd7x8jk0 - the one that we control

  2. WalletB , dydx1n72zp2myk6qzsec954nd6lap7tn5f6mwtr2qne - an offline wallet of one of our counterparties in the multisig

  3. WalletC , dydx1h03ny3j2k4yltt767r3tjw92qs0vj3p2g5w6x3 - an offline wallet of the other counterparty in the multisig

  4. ourMultisig , dydx1z79c98w069eugzjme0dkd6quuhm8vfh2a00smh - a 2/3 multisig, instantiated with WalletA , WalletB , and WalletC

dydxprotocold keys show ourMultisig
- address: dydx1z79c98w069eugzjme0dkd6quuhm8vfh2a00smh
  name: ourMultisig
  pubkey: '{"@type":"/cosmos.crypto.multisig.LegacyAminoPubKey","threshold":2,"public_keys":[{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A/C+dj94...9SCD2Uu11"},{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A9QEyr+iQoCI...IP76IjzVG2vEMf"},{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"ArarpFkTpgJOnjvUen...KMU5c2zbQ0"}]}'
  type: multi

Querying the multisig balances:

dydxprotocold query bank balances dydx1z79c98w069eugzjme0dkd6quuhm8vfh2a00smh

balances:
- amount: "231357500000000000000"
  denom: adv4tnt
pagination:
  next_key: null
  total: "0"

4. Create send token Tx

This step can be executed from any workstation. It is not even necessary to have added walletA , walletB , walletC nor ourMultisig

$  dydxprotocold tx bank send ourMultisig \
     dydx1h6x6pxkv2fv3jzsx5hc5efaarhue7gpsdmxafk \
     100000000000000000adv4tnt \
     --fees 5000000000000000adv4tnt \
     --node https://dydx-testnet-rpc.polkachu.com:443 \
     --chain-id dydx-testnet-4 \
     --generate-only > unsignedTx.json
  • dydx1z79c98w069eugzjme0dkd6quuhm8vfh2a00smh is the address of the multisig from which we send tokens - in case you have instantiated the multisig locally, you can use it’s name, e.g. ourMultisig

  • dydx1h6x6pxkv2fv3jzsx5hc5efaarhue7gpsdmxafk is the recipient address

  • 700000000000000000adydx is the amount to send - 0.7 DYDX

  • --fees value 5000000000000000adydx may need adjustment; the transaction will fail only when broadcasted if this amount is not enough

  • --generate-only will prevent the transaction to be submitted

The result will be stored in the file named unsignedTx.json

unsignedTx.json
```json
{
	"body": {
		"messages": [
			{
				"@type": "/cosmos.bank.v1beta1.MsgSend",
				"from_address": "dydx1z79c98w069eugzjme0dkd6quuhm8vfh2a00smh",
				"to_address": "dydx1h6x6pxkv2fv3jzsx5hc5efaarhue7gpsdmxafk",
				"amount": [{ "denom": "adv4tnt", "amount": "100000000000000000" }]
			}
		],
		"memo": "",
		"timeout_height": "0",
		"extension_options": [],
		"non_critical_extension_options": []
	},
	"auth_info": {
		"signer_infos": [],
		"fee": {
			"amount": [{ "denom": "adv4tnt", "amount": "5000000000000000" }],
			"gas_limit": "200000",
			"payer": "",
			"granter": ""
		},
		"tip": null
	},
	"signatures": []
}

```

5. Sign Token Tx

πŸ’‘ Each of the required signers must use their own instances of dydxprotocold CLI and their private keys. The unsignedTx.json file needs to be securely transferred to them.

πŸ’‘ Each signer should verify the transaction details in unsignedTx.json before signing!

dydxprotocold tx sign unsignedTx.json \
    --from walletName \
    --multisig=ourMultisig \
    --multisig= --output-document=walletAsignedTx.json \
    --node https://dydx-testnet-rpc.polkachu.com:443 \
    --chain-id dydx-testnet-4
  • Replace walletName with the respective wallet name ( walletA , walletB , or walletC ).

  • Each signer must have instantiated the multisig locally - they must see dydx1z79c98w069eugzjme0dkd6quuhm8vfh2a00smh when listing their know wallets

  • The value of --multisig can also be the multisig name, e.g. ourMultisig

After this step, there should be two separate signed transaction files, e.g., walletASignedTx.json and walletBSignedTx.json.

// walletASignedTx.json
```json
{
	"signatures": [
		{
			"public_key": {
				"@type": "/cosmos.crypto.secp256k1.PubKey",
				"key": "ArarpFkTpgJOn...wkKMU5c2zbQ0"
			},
			"data": {
				"single": {
					"mode": "SIGN_MODE_LEGACY_AMINO_JSON",
					"signature": "Or9KGawXT...f3kT6DA=="
				}
			},
			"sequence": "0"
		}
	]
}

```

6. Combine signatures

This step can be executed from a workstation where the multisig has been instantiated. Not necessarily an owner of a wallet in the multisig though.

dydxprotocold tx multisign unsignedTx.json \
    ourMultisig \
    walletASignedTx.json \
    walletBSignedTx.json \ 
    --node https://dydx-testnet-rpc.polkachu.com:443 \
    --chain-id dydx-testnet-4 > multisignedTx.json

One needs all the files generated until now: unsignedTx.json , walletASignedTx.json and walletBSignedTx.json . ourMultisig cannot be the multisig address The result is a file called multisignedTx.json

//multisignedTx.json
```json
{
	"body": {
		"messages": [
			{
				"@type": "/cosmos.bank.v1beta1.MsgSend",
				"from_address": "dydx1z79c98w069eugzjme0dkd6quuhm8vfh2a00smh",
				"to_address": "dydx1h6x6pxkv2fv3jzsx5hc5efaarhue7gpsdmxafk",
				"amount": [{ "denom": "adv4tnt", "amount": "100000000000000000" }]
			}
		],
		"memo": "",
		"timeout_height": "0",
		"extension_options": [],
		"non_critical_extension_options": []
	},
	"auth_info": {
		"signer_infos": [
			{
				"public_key": {
					"@type": "/cosmos.crypto.multisig.LegacyAminoPubKey",
					"threshold": 2,
					"public_keys": [
						{
							"@type": "/cosmos.crypto.secp256k1.PubKey",
							"key": "A/C+dj94G1t...CD2Uu11"
						},
						{
							"@type": "/cosmos.crypto.secp256k1.PubKey",
							"key": "A9QEyr+i...P76IjzVG2vEMf"
						},
						{
							"@type": "/cosmos.crypto.secp256k1.PubKey",
							"key": "ArarpFkTpgJO....kKMU5c2zbQ0"
						}
					]
				},
				"mode_info": {
					"multi": {
						"bitarray": { "extra_bits_stored": 3, "elems": "oA==" },
						"mode_infos": [
							{ "single": { "mode": "SIGN_MODE_LEGACY_AMINO_JSON" } },
							{ "single": { "mode": "SIGN_MODE_LEGACY_AMINO_JSON" } }
						]
					}
				},
				"sequence": "0"
			}
		],
		"fee": {
			"amount": [{ "denom": "adv4tnt", "amount": "5000000000000000" }],
			"gas_limit": "200000",
			"payer": "",
			"granter": ""
		},
		"tip": null
	},
	"signatures": [
		"CkDEtBh262Ur...oZrBdEd/R/eRPoM"
	]
}

```

7. Broadcast Send Tokens Tx

$ dydxprotocold tx broadcast multisignedTx.json \
    --node https://dydx-testnet-rpc.polkachu.com:443 
    --chain-id dydx-testnet-4
    
    
Code: 0
codespace: ""
data: ""
events: []
gas_used: "0"
gas_wanted: "0"
height: "0"
info: ""
logs: []
raw_log: '[]'
timestamp: ""
tx: null
txhash: F51017738F91D758CBF857D0B...739F7177EFC778BA2811585F84

You can use the txhash to check MintScan for the transaction status. If nothing is available on MintScan, then the broadcast or signing were unsuccessful.

Key order matters and keys are automatically sorted by their public key. At the time of writing, this is how instantiates multisig wallets. To change the key ordering, use the --nosort flag.

πŸ’‘
dYdX CLI
EOA Wallets
https://multisig.keplr.app/
https://multisig.keplr.app/