@@ -6,8 +6,10 @@ import {
6
6
} from "@certusone/wormhole-sdk/lib/cjs/solana/wormhole" ;
7
7
import { AnchorProvider , BN , Program } from "@coral-xyz/anchor" ;
8
8
import NodeWallet from "@coral-xyz/anchor/dist/cjs/nodewallet" ;
9
+ import { parseProductData } from "@pythnetwork/client" ;
9
10
import {
10
11
getPythClusterApiUrl ,
12
+ getPythProgramKeyForCluster ,
11
13
PythCluster ,
12
14
} from "@pythnetwork/client/lib/cluster" ;
13
15
import {
@@ -16,11 +18,15 @@ import {
16
18
Connection ,
17
19
Keypair ,
18
20
PublicKey ,
21
+ SystemProgram ,
22
+ TransactionInstruction ,
19
23
} from "@solana/web3.js" ;
20
24
import * as fs from "fs" ;
21
25
import {
22
26
decodeGovernancePayload ,
23
27
ExecutePostedVaa ,
28
+ MultisigParser ,
29
+ PythMultisigInstruction ,
24
30
WORMHOLE_ADDRESS ,
25
31
WORMHOLE_API_ENDPOINT ,
26
32
} from "xc_admin_common" ;
@@ -37,6 +43,8 @@ const REMOTE_EXECUTOR_ADDRESS = new PublicKey(
37
43
"exe6S3AxPVNmy46L4Nj6HrnnAVQUhwyYzMSNcnRn3qq"
38
44
) ;
39
45
46
+ const PRODUCT_ACCOUNT_SIZE = 512 ;
47
+ const PRICE_ACCOUNT_SIZE = 3312 ;
40
48
const CLAIM_RECORD_SEED = "CLAIM_RECORD" ;
41
49
const EXECUTOR_KEY_SEED = "EXECUTOR_KEY" ;
42
50
const CLUSTER : PythCluster = envOrErr ( "CLUSTER" ) as PythCluster ;
@@ -97,6 +105,9 @@ async function run() {
97
105
governancePayload instanceof ExecutePostedVaa &&
98
106
governancePayload . targetChainId == "pythnet"
99
107
) {
108
+ const multisigParser = MultisigParser . fromCluster ( CLUSTER ) ;
109
+ const preInstructions : TransactionInstruction [ ] = [ ] ;
110
+
100
111
console . log ( `Found VAA ${ lastSequenceNumber } , relaying ...` ) ;
101
112
await postVaaSolana (
102
113
provider . connection ,
@@ -110,6 +121,7 @@ async function run() {
110
121
let extraAccountMetas : AccountMeta [ ] = [
111
122
{ pubkey : executorKey , isSigner : false , isWritable : true } ,
112
123
] ;
124
+
113
125
for ( const ix of governancePayload . instructions ) {
114
126
extraAccountMetas . push ( {
115
127
pubkey : ix . programId ,
@@ -121,6 +133,67 @@ async function run() {
121
133
return ! acc . pubkey . equals ( executorKey ) ;
122
134
} )
123
135
) ;
136
+
137
+ const parsedInstruction = multisigParser . parseInstruction ( ix ) ;
138
+ if (
139
+ parsedInstruction instanceof PythMultisigInstruction &&
140
+ parsedInstruction . name == "addProduct"
141
+ ) {
142
+ const productSeed = "product:" + parsedInstruction . args . symbol ;
143
+ const productAddress = await PublicKey . createWithSeed (
144
+ provider . wallet . publicKey ,
145
+ productSeed ,
146
+ getPythProgramKeyForCluster ( CLUSTER as PythCluster )
147
+ ) ;
148
+ preInstructions . push (
149
+ SystemProgram . createAccountWithSeed ( {
150
+ fromPubkey : provider . wallet . publicKey ,
151
+ basePubkey : provider . wallet . publicKey ,
152
+ newAccountPubkey : productAddress ,
153
+ seed : productSeed ,
154
+ space : PRODUCT_ACCOUNT_SIZE ,
155
+ lamports :
156
+ await provider . connection . getMinimumBalanceForRentExemption (
157
+ PRODUCT_ACCOUNT_SIZE
158
+ ) ,
159
+ programId : getPythProgramKeyForCluster ( CLUSTER as PythCluster ) ,
160
+ } )
161
+ ) ;
162
+ } else if (
163
+ parsedInstruction instanceof PythMultisigInstruction &&
164
+ parsedInstruction . name == "addPrice"
165
+ ) {
166
+ const productAccount = await provider . connection . getAccountInfo (
167
+ parsedInstruction . accounts . named . productAccount . pubkey
168
+ ) ;
169
+ if ( productAccount ) {
170
+ const priceSeed =
171
+ "price:" + parseProductData ( productAccount . data ) . product . symbol ;
172
+ const priceAddress = await PublicKey . createWithSeed (
173
+ provider . wallet . publicKey ,
174
+ priceSeed ,
175
+ getPythProgramKeyForCluster ( CLUSTER as PythCluster )
176
+ ) ;
177
+ preInstructions . push (
178
+ SystemProgram . createAccountWithSeed ( {
179
+ fromPubkey : provider . wallet . publicKey ,
180
+ basePubkey : provider . wallet . publicKey ,
181
+ newAccountPubkey : priceAddress ,
182
+ seed : priceSeed ,
183
+ space : PRICE_ACCOUNT_SIZE ,
184
+ lamports :
185
+ await provider . connection . getMinimumBalanceForRentExemption (
186
+ PRICE_ACCOUNT_SIZE
187
+ ) ,
188
+ programId : getPythProgramKeyForCluster (
189
+ CLUSTER as PythCluster
190
+ ) ,
191
+ } )
192
+ ) ;
193
+ } else {
194
+ throw Error ( "Product account not found" ) ;
195
+ }
196
+ }
124
197
}
125
198
126
199
await remoteExecutor . methods
@@ -130,6 +203,7 @@ async function run() {
130
203
postedVaa : derivePostedVaaKey ( WORMHOLE_ADDRESS [ CLUSTER ] ! , vaa . hash ) ,
131
204
} )
132
205
. remainingAccounts ( extraAccountMetas )
206
+ . preInstructions ( preInstructions )
133
207
. rpc ( ) ;
134
208
}
135
209
} else if ( response . code == 5 ) {
0 commit comments