Skip to content

Commit 9879ae0

Browse files
authored
feat(wallet-cli): Add multisig commands (#54)
* feat(wallet-cli): Add multisig commands * Update util
1 parent bad8d2d commit 9879ae0

File tree

22 files changed

+1102
-72
lines changed

22 files changed

+1102
-72
lines changed

package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,22 +24,22 @@
2424
"devDependencies": {
2525
"@types/blessed": "0.1.22",
2626
"@types/node": "18.11.16",
27+
"@types/yup": "0.29.10",
2728
"@typescript-eslint/eslint-plugin": "5.62.0",
2829
"@typescript-eslint/parser": "5.62.0",
2930
"eslint": "8.46.0",
3031
"eslint-config-prettier": "9.0.0",
32+
"eslint-plugin-deprecation": "2.0.0",
3133
"eslint-plugin-header": "3.1.1",
3234
"eslint-plugin-jest": "27.2.3",
3335
"eslint-plugin-prettier": "4.2.1",
3436
"eslint-plugin-simple-import-sort": "10.0.0",
35-
"eslint-plugin-deprecation": "2.0.0",
3637
"jest": "29.3.1",
3738
"jest-jasmine2": "29.3.1",
3839
"oclif": "2.6.0",
3940
"prettier": "2.8.8",
40-
"typescript": "4.3.4",
41-
"yarn": "1.22.10",
42-
"@types/yup": "0.29.10"
41+
"typescript": "5.0.4",
42+
"yarn": "1.22.10"
4343
},
4444
"scripts": {
4545
"build": "tsc -b",
@@ -55,8 +55,8 @@
5555
"oclif:version": "oclif readme && git add README.md"
5656
},
5757
"dependencies": {
58-
"@ironfish/rust-nodejs": "2.0.0",
59-
"@ironfish/sdk": "2.0.0",
58+
"@ironfish/rust-nodejs": "2.1.0",
59+
"@ironfish/sdk": "2.1.0",
6060
"@oclif/core": "1.23.1",
6161
"@oclif/plugin-help": "5.1.12",
6262
"@oclif/plugin-not-found": "2.3.1",

src/commands/config/get.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ export class GetCommand extends IronfishCommand {
4545
const { args, flags } = await this.parse(GetCommand)
4646
const name = (args.name as string).trim()
4747

48-
const client = await connectRpcConfig(this.sdk, this.walletConfig, flags.local)
48+
const client = await connectRpcConfig(
49+
this.sdk,
50+
this.walletConfig,
51+
flags.local,
52+
)
4953

5054
const response = await client.config.getConfig({
5155
user: flags.user,

src/commands/config/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,11 @@ export class ShowCommand extends IronfishCommand {
2626
async start(): Promise<void> {
2727
const { flags } = await this.parse(ShowCommand)
2828

29-
const client = await connectRpcConfig(this.sdk, this.walletConfig, flags.local)
29+
const client = await connectRpcConfig(
30+
this.sdk,
31+
this.walletConfig,
32+
flags.local,
33+
)
3034
const response = await client.config.getConfig({ user: flags.user })
3135

3236
let output = JSON.stringify(response.content, undefined, ' ')

src/commands/config/set.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,11 @@ export class SetCommand extends IronfishCommand {
4141
const name = args.name as string
4242
const value = args.value as string
4343

44-
const client = await connectRpcConfig(this.sdk, this.walletConfig, flags.local)
44+
const client = await connectRpcConfig(
45+
this.sdk,
46+
this.walletConfig,
47+
flags.local,
48+
)
4549
await client.config.setConfig({ name, value })
4650

4751
this.exit(0)

src/commands/config/unset.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,11 @@ export class UnsetCommand extends IronfishCommand {
3232
const { args, flags } = await this.parse(UnsetCommand)
3333
const name = args.name as string
3434

35-
const client = await connectRpcConfig(this.sdk, this.walletConfig, flags.local)
35+
const client = await connectRpcConfig(
36+
this.sdk,
37+
this.walletConfig,
38+
flags.local,
39+
)
3640
await client.config.unsetConfig({ name })
3741

3842
this.exit(0)
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4+
5+
import { Flags } from '@oclif/core'
6+
import { IronfishCommand } from '../../../command'
7+
import { RemoteFlags } from '../../../flags'
8+
import { longPrompt } from '../../../utils/longPrompt'
9+
import { MultisigTransactionJson } from '../../../utils/multisig'
10+
11+
export class CreateSigningPackage extends IronfishCommand {
12+
static description = `Creates a signing package for a given transaction for a multisig account`
13+
14+
static flags = {
15+
...RemoteFlags,
16+
account: Flags.string({
17+
char: 'f',
18+
description: 'The account to use when creating the signing package',
19+
required: false,
20+
}),
21+
unsignedTransaction: Flags.string({
22+
char: 'u',
23+
description:
24+
'The unsigned transaction for which the signing share will be created',
25+
}),
26+
commitment: Flags.string({
27+
char: 'c',
28+
description:
29+
'The signing commitments from participants to be used for creating the signing package',
30+
multiple: true,
31+
}),
32+
path: Flags.string({
33+
description: 'Path to a JSON file containing multisig transaction data',
34+
}),
35+
}
36+
37+
async start(): Promise<void> {
38+
const { flags } = await this.parse(CreateSigningPackage)
39+
40+
const loaded = await MultisigTransactionJson.load(
41+
this.sdk.fileSystem,
42+
flags.path,
43+
)
44+
const options = MultisigTransactionJson.resolveFlags(flags, loaded)
45+
46+
let unsignedTransaction = options.unsignedTransaction
47+
if (!unsignedTransaction) {
48+
unsignedTransaction = await longPrompt('Enter the unsigned transaction', {
49+
required: true,
50+
})
51+
}
52+
53+
let commitments = options.commitment
54+
if (!commitments) {
55+
const input = await longPrompt(
56+
'Enter the signing commitments separated by commas',
57+
{
58+
required: true,
59+
},
60+
)
61+
commitments = input.split(',')
62+
}
63+
commitments = commitments.map((s) => s.trim())
64+
65+
const client = await this.sdk.connectRpc()
66+
67+
const signingPackageResponse =
68+
await client.wallet.multisig.createSigningPackage({
69+
account: flags.account,
70+
unsignedTransaction,
71+
commitments,
72+
})
73+
74+
this.log(
75+
`Signing Package for commitments from ${commitments.length} participants:\n`,
76+
)
77+
this.log(signingPackageResponse.content.signingPackage)
78+
79+
this.log()
80+
this.log('Next step:')
81+
this.log(
82+
'Send the signing package to all of the participants who provided a commitment.',
83+
)
84+
}
85+
}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/* This Source Code Form is subject to the terms of the Mozilla Public
2+
* License, v. 2.0. If a copy of the MPL was not distributed with this
3+
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4+
import { UnsignedTransaction } from '@ironfish/sdk'
5+
import { CliUx, Flags } from '@oclif/core'
6+
import { IronfishCommand } from '../../../command'
7+
import { RemoteFlags } from '../../../flags'
8+
import { longPrompt } from '../../../utils/longPrompt'
9+
import { MultisigTransactionJson } from '../../../utils/multisig'
10+
import { renderUnsignedTransactionDetails } from '../../../utils/transaction'
11+
12+
export class CreateSigningCommitmentCommand extends IronfishCommand {
13+
static description =
14+
'Create a signing commitment from a participant for a given transaction'
15+
16+
static flags = {
17+
...RemoteFlags,
18+
account: Flags.string({
19+
char: 'f',
20+
description:
21+
'The account to use for generating the commitment, must be a multisig participant account',
22+
required: false,
23+
}),
24+
unsignedTransaction: Flags.string({
25+
char: 'u',
26+
description: 'The unsigned transaction that needs to be signed',
27+
}),
28+
identity: Flags.string({
29+
char: 'i',
30+
description:
31+
'The identity of the participants that will sign the transaction (may be specified multiple times to add multiple signers)',
32+
multiple: true,
33+
}),
34+
confirm: Flags.boolean({
35+
default: false,
36+
description: 'Confirm creating signing commitment without confirming',
37+
}),
38+
path: Flags.string({
39+
description: 'Path to a JSON file containing multisig transaction data',
40+
}),
41+
}
42+
43+
async start(): Promise<void> {
44+
const { flags } = await this.parse(CreateSigningCommitmentCommand)
45+
46+
const loaded = await MultisigTransactionJson.load(
47+
this.sdk.fileSystem,
48+
flags.path,
49+
)
50+
const options = MultisigTransactionJson.resolveFlags(flags, loaded)
51+
52+
let identities = options.identity
53+
if (!identities || identities.length < 2) {
54+
const input = await longPrompt(
55+
'Enter the identities separated by commas',
56+
{
57+
required: true,
58+
},
59+
)
60+
identities = input.split(',')
61+
62+
if (identities.length < 2) {
63+
this.error('Minimum number of identities must be at least 2')
64+
}
65+
}
66+
identities = identities.map((i) => i.trim())
67+
68+
let unsignedTransactionInput = options.unsignedTransaction
69+
if (!unsignedTransactionInput) {
70+
unsignedTransactionInput = await longPrompt(
71+
'Enter the unsigned transaction',
72+
{
73+
required: true,
74+
},
75+
)
76+
}
77+
78+
const client = await this.sdk.connectRpc()
79+
const unsignedTransaction = new UnsignedTransaction(
80+
Buffer.from(unsignedTransactionInput, 'hex'),
81+
)
82+
83+
await renderUnsignedTransactionDetails(
84+
client,
85+
unsignedTransaction,
86+
flags.account,
87+
this.logger,
88+
)
89+
90+
if (!flags.confirm) {
91+
const confirmed = await CliUx.ux.confirm(
92+
'Confirm signing commitment creation (Y/N)',
93+
)
94+
if (!confirmed) {
95+
this.error('Creating signing commitment aborted')
96+
}
97+
}
98+
99+
const response = await client.wallet.multisig.createSigningCommitment({
100+
account: flags.account,
101+
unsignedTransaction: unsignedTransactionInput,
102+
signers: identities.map((identity) => ({ identity })),
103+
})
104+
105+
this.log('\nCommitment:\n')
106+
this.log(response.content.commitment)
107+
108+
this.log()
109+
this.log('Next step:')
110+
this.log('Send the commitment to the multisig account coordinator.')
111+
}
112+
}

0 commit comments

Comments
 (0)