@@ -7,10 +7,11 @@ import {
7
7
import * as anchor from "@project-serum/anchor" ;
8
8
import NodeWallet from "@project-serum/anchor/dist/cjs/nodewallet" ;
9
9
import {
10
+ AccountMeta ,
10
11
Keypair ,
11
12
LAMPORTS_PER_SOL ,
12
13
PublicKey ,
13
- SystemProgram ,
14
+ SystemProgram , TransactionInstruction ,
14
15
} from "@solana/web3.js" ;
15
16
import Squads from "@sqds/mesh" ;
16
17
import bs58 from "bs58" ;
@@ -45,18 +46,51 @@ program
45
46
"keys/key.json"
46
47
)
47
48
. option ( "-p, --payload <hex-string>" , "payload to sign" , "0xdeadbeef" )
48
- . action ( ( options ) => {
49
- createMultisigTx (
49
+ . action ( async ( options ) => {
50
+ const squad = await getSquadsClient ( options . cluster , options . ledger , options . ledgerDerivationAccount , options . ledgerDerivationChange , options . wallet ) ;
51
+ await createWormholeMsgMultisigTx (
50
52
options . cluster ,
51
- new PublicKey ( options . vaultAddress ) ,
53
+ squad ,
52
54
options . ledger ,
53
- options . ledgerDerivationAccount ,
54
- options . ledgerDerivationChange ,
55
- options . wallet ,
55
+ new PublicKey ( options . vaultAddress ) ,
56
56
options . payload
57
57
) ;
58
58
} ) ;
59
59
60
+ program
61
+ . command ( "set-is-active" )
62
+ . description ( "Create a new multisig transaction to set the attester is-active flag" )
63
+ . option ( "-c, --cluster <network>" , "solana cluster to use" , "devnet" )
64
+ . requiredOption ( "-v, --vault-address <address>" , "multisig vault address" )
65
+ . option ( "-l, --ledger" , "use ledger" )
66
+ . option (
67
+ "-lda, --ledger-derivation-account <number>" ,
68
+ "ledger derivation account to use"
69
+ )
70
+ . option (
71
+ "-ldc, --ledger-derivation-change <number>" ,
72
+ "ledger derivation change to use"
73
+ )
74
+ . option (
75
+ "-w, --wallet <filepath>" ,
76
+ "multisig wallet secret key filepath" ,
77
+ "keys/key.json"
78
+ )
79
+ . option ( "-a, --attester <program id>" )
80
+ . option ( "-i, --is-active <true/false>" , "set the isActive field to this value" , true )
81
+ . action ( async ( options ) => {
82
+ const squad = await getSquadsClient ( options . cluster , options . ledger , options . ledgerDerivationAccount , options . ledgerDerivationChange , options . wallet ) ;
83
+ const msAccount = await squad . getMultisig ( options . vaultAddress ) ;
84
+ const vaultAuthority = squad . getAuthorityPDA (
85
+ msAccount . publicKey ,
86
+ msAccount . authorityIndex
87
+ ) ;
88
+ const attesterProgramId = new PublicKey ( options . attester ) ;
89
+ const txKey = await createTx ( squad , options . ledger , new PublicKey ( options . vaultAddress ) ) ;
90
+ const instructions = [ await setIsActiveIx ( vaultAuthority , vaultAuthority , attesterProgramId , options . active ) ] ;
91
+ await addInstructionsToTx ( squad , options . ledger , txKey , instructions ) ;
92
+ } ) ;
93
+
60
94
program
61
95
. command ( "execute" )
62
96
. description ( "Execute a multisig transaction that is ready" )
@@ -112,6 +146,110 @@ const solanaClusterMappingToWormholeNetwork: Record<Cluster, WormholeNetwork> =
112
146
"mainnet-beta" : "MAINNET" ,
113
147
} ;
114
148
149
+ async function getSquadsClient ( cluster : Cluster ,
150
+ ledger : boolean ,
151
+ ledgerDerivationAccount : number | undefined ,
152
+ ledgerDerivationChange : number | undefined ,
153
+ walletPath : string ,
154
+ ) {
155
+ let wallet : LedgerNodeWallet | NodeWallet ;
156
+ if ( ledger ) {
157
+ console . log ( "Please connect to ledger..." ) ;
158
+ wallet = await LedgerNodeWallet . createWallet (
159
+ ledgerDerivationAccount ,
160
+ ledgerDerivationChange
161
+ ) ;
162
+ console . log ( `Ledger connected! ${ wallet . publicKey . toBase58 ( ) } ` ) ;
163
+ } else {
164
+ wallet = new NodeWallet (
165
+ Keypair . fromSecretKey (
166
+ Uint8Array . from ( JSON . parse ( fs . readFileSync ( walletPath , "ascii" ) ) )
167
+ )
168
+ ) ;
169
+ console . log ( `Loaded wallet with address: ${ wallet . publicKey . toBase58 ( ) } ` ) ;
170
+ }
171
+ const squad =
172
+ cluster === "devnet" ? Squads . devnet ( wallet ) : Squads . mainnet ( wallet ) ;
173
+ return squad ;
174
+ }
175
+
176
+ async function createTx (
177
+ squad : Squads ,
178
+ ledger : boolean ,
179
+ vault : PublicKey ,
180
+ ) : Promise < PublicKey > {
181
+ const msAccount = await squad . getMultisig ( vault ) ;
182
+
183
+ console . log ( "Creating new transaction..." ) ;
184
+ if ( ledger ) {
185
+ console . log ( "Please approve the transaction on your ledger device..." ) ;
186
+ }
187
+ const newTx = await squad . createTransaction (
188
+ msAccount . publicKey ,
189
+ msAccount . authorityIndex
190
+ ) ;
191
+ console . log ( `Tx Address: ${ newTx . publicKey . toBase58 ( ) } ` ) ;
192
+
193
+ return newTx . publicKey ;
194
+ }
195
+
196
+ /** Adds the given instructions to the squads transaction at `txKey` and activates the transaction (makes it ready for signing). */
197
+ async function addInstructionsToTx (
198
+ squad : Squads ,
199
+ ledger : boolean ,
200
+ txKey : PublicKey ,
201
+ instructions : TransactionInstruction [ ]
202
+ ) {
203
+ for ( let i = 0 ; i < instructions . length ; i ++ ) {
204
+ console . log ( `Adding instruction ${ i } /${ instructions . length } to transaction...` ) ;
205
+ if ( ledger ) {
206
+ console . log ( "Please approve the transaction on your ledger device..." ) ;
207
+ }
208
+ await squad . addInstruction ( txKey , instructions [ i ] ) ;
209
+ }
210
+
211
+ console . log ( "Activating transaction..." ) ;
212
+ if ( ledger )
213
+ console . log ( "Please approve the transaction on your ledger device..." ) ;
214
+ await squad . activateTransaction ( txKey ) ;
215
+ console . log ( "Transaction created." ) ;
216
+ }
217
+
218
+ async function setIsActiveIx (
219
+ payerKey : PublicKey ,
220
+ opsOwnerKey : PublicKey ,
221
+ attesterProgramId : PublicKey ,
222
+ isActive : boolean
223
+ ) : Promise < TransactionInstruction > {
224
+ const configKey = PublicKey . createProgramAddressSync ( [ Buffer . from ( "pyth2wormhole-config-v3" ) ] , attesterProgramId )
225
+ const config : AccountMeta = {
226
+ pubkey : configKey ,
227
+ isSigner : false ,
228
+ isWritable : true ,
229
+ }
230
+
231
+ const opsOwner : AccountMeta = {
232
+ pubkey : opsOwnerKey ,
233
+ isSigner : true ,
234
+ isWritable : true ,
235
+ }
236
+ const payer : AccountMeta = {
237
+ pubkey : payerKey ,
238
+ isSigner : true ,
239
+ isWritable : true ,
240
+ }
241
+
242
+ const isActiveInt = ( isActive === true ) ? 1 : 0 ;
243
+ // first byte is the isActive instruction, second byte is true/false
244
+ const data = new Buffer ( [ 4 , isActiveInt ] )
245
+
246
+ return {
247
+ keys : [ config , opsOwner , payer ] ,
248
+ programId : attesterProgramId ,
249
+ data : data ,
250
+ }
251
+ }
252
+
115
253
async function getWormholeMessageIx (
116
254
cluster : Cluster ,
117
255
payer : PublicKey ,
@@ -155,33 +293,13 @@ async function getWormholeMessageIx(
155
293
] ;
156
294
}
157
295
158
- async function createMultisigTx (
296
+ async function createWormholeMsgMultisigTx (
159
297
cluster : Cluster ,
160
- vault : PublicKey ,
298
+ squad : Squads ,
161
299
ledger : boolean ,
162
- ledgerDerivationAccount : number | undefined ,
163
- ledgerDerivationChange : number | undefined ,
164
- walletPath : string ,
300
+ vault : PublicKey ,
165
301
payload : string
166
302
) {
167
- let wallet : LedgerNodeWallet | NodeWallet ;
168
- if ( ledger ) {
169
- console . log ( "Please connect to ledger..." ) ;
170
- wallet = await LedgerNodeWallet . createWallet (
171
- ledgerDerivationAccount ,
172
- ledgerDerivationChange
173
- ) ;
174
- console . log ( `Ledger connected! ${ wallet . publicKey . toBase58 ( ) } ` ) ;
175
- } else {
176
- wallet = new NodeWallet (
177
- Keypair . fromSecretKey (
178
- Uint8Array . from ( JSON . parse ( fs . readFileSync ( walletPath , "ascii" ) ) )
179
- )
180
- ) ;
181
- console . log ( `Loaded wallet with address: ${ wallet . publicKey . toBase58 ( ) } ` ) ;
182
- }
183
- const squad =
184
- cluster === "devnet" ? Squads . devnet ( wallet ) : Squads . mainnet ( wallet ) ;
185
303
const msAccount = await squad . getMultisig ( vault ) ;
186
304
187
305
const emitter = squad . getAuthorityPDA (
@@ -190,18 +308,13 @@ async function createMultisigTx(
190
308
) ;
191
309
console . log ( `Emitter Address: ${ emitter . toBase58 ( ) } ` ) ;
192
310
193
- console . log ( "Creating new transaction..." ) ;
194
- if ( ledger )
195
- console . log ( "Please approve the transaction on your ledger device..." ) ;
196
- const newTx = await squad . createTransaction (
197
- msAccount . publicKey ,
198
- msAccount . authorityIndex
199
- ) ;
200
- console . log ( `Tx Address: ${ newTx . publicKey . toBase58 ( ) } ` ) ;
311
+ const txKey = await createTx ( squad , ledger , vault ) ;
201
312
202
313
const message = Keypair . generate ( ) ;
314
+
315
+ fs . mkdirSync ( 'keys' , { recursive : true } ) ;
203
316
// save message to Uint8 array keypair file called mesage.json
204
- fs . writeFileSync ( `keys/message.json` , `[${ message . secretKey . toString ( ) } ]` ) ;
317
+ fs . writeFileSync ( `keys/message- ${ txKey . toBase58 ( ) } .json` , `[${ message . secretKey . toString ( ) } ]` ) ;
205
318
console . log ( `Message Address: ${ message . publicKey . toBase58 ( ) } ` ) ;
206
319
207
320
console . log ( "Creating wormhole instructions..." ) ;
@@ -215,22 +328,7 @@ async function createMultisigTx(
215
328
) ;
216
329
console . log ( "Wormhole instructions created." ) ;
217
330
218
- console . log ( "Adding instruction 1/2 to transaction..." ) ;
219
- if ( ledger )
220
- console . log ( "Please approve the transaction on your ledger device..." ) ;
221
- // transfer sol to the message account
222
- await squad . addInstruction ( newTx . publicKey , wormholeIxs [ 0 ] ) ;
223
- console . log ( "Adding instruction 2/2 to transaction..." ) ;
224
- if ( ledger )
225
- console . log ( "Please approve the transaction on your ledger device..." ) ;
226
- // wormhole post message ix
227
- await squad . addInstruction ( newTx . publicKey , wormholeIxs [ 1 ] ) ;
228
-
229
- console . log ( "Activating transaction..." ) ;
230
- if ( ledger )
231
- console . log ( "Please approve the transaction on your ledger device..." ) ;
232
- await squad . activateTransaction ( newTx . publicKey ) ;
233
- console . log ( "Transaction created." ) ;
331
+ await addInstructionsToTx ( squad , ledger , txKey , wormholeIxs ) ;
234
332
}
235
333
236
334
async function executeMultisigTx (
@@ -328,6 +426,12 @@ async function executeMultisigTx(
328
426
} `
329
427
) ;
330
428
429
+ console . log (
430
+ "Sleeping for 10 seconds to allow guardians enough time to get the sequence number..."
431
+ ) ;
432
+ await new Promise ( ( resolve ) => setTimeout ( resolve , 10000 ) ) ;
433
+
434
+
331
435
const txDetails = await squad . connection . getParsedTransaction (
332
436
signature ,
333
437
"confirmed"
@@ -341,11 +445,10 @@ async function executeMultisigTx(
341
445
) ;
342
446
console . log ( `Sequence number: ${ sequenceNumber } ` ) ;
343
447
344
- // sleep for 5 seconds
345
448
console . log (
346
- "Sleeping for 5 seconds to allow guardians enough time to create VAA..."
449
+ "Sleeping for 10 seconds to allow guardians enough time to create VAA..."
347
450
) ;
348
- await new Promise ( ( resolve ) => setTimeout ( resolve , 5000 ) ) ;
451
+ await new Promise ( ( resolve ) => setTimeout ( resolve , 10000 ) ) ;
349
452
350
453
// fetch VAA
351
454
console . log ( "Fetching VAA..." ) ;
@@ -357,6 +460,7 @@ async function executeMultisigTx(
357
460
const { vaaBytes } = await response . json ( ) ;
358
461
console . log ( `VAA (Base64): ${ vaaBytes } ` ) ;
359
462
const parsedVaa = await parse ( vaaBytes ) ;
463
+ console . log ( `VAA (Hex): ${ Buffer . from ( vaaBytes ) . toString ( 'hex' ) } ` )
360
464
console . log ( `Emitter chain: ${ parsedVaa . emitter_chain } ` ) ;
361
465
console . log ( `Nonce: ${ parsedVaa . nonce } ` ) ;
362
466
console . log ( `Payload: ${ Buffer . from ( parsedVaa . payload ) . toString ( 'hex' ) } ` ) ;
0 commit comments