@pythnetwork/pyth-solana-receiver 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,114 @@
1
+ # Pyth Solana Receiver JS SDK
2
+
3
+ This is a Javascript SDK to interact with the Pyth Solana Receiver contract whose code lives [here](/target_chains/solana).
4
+
5
+ ## Pull model
6
+
7
+ The Pyth Solana Receiver allows users to consume Pyth price updates on a pull basis. This means that the user is responsible for submitting the price data on-chain whenever they want to interact with an app that requires a price update.
8
+
9
+ Price updates get posted into price update accounts, owned by the Receiver contract. Once an update has been posted to a price update account, it can be used by anyone by simply passing the price update account as one of the accounts in a Solana instruction.
10
+ Price update accounts can be closed by whoever wrote them to recover the rent.
11
+
12
+ ## Example use
13
+
14
+ ```ts
15
+ import { Connection, PublicKey } from "@solana/web3.js";
16
+ import { PriceServiceConnection } from "@pythnetwork/price-service-client";
17
+ import { PythSolanaReceiver } from "@pythnetwork/pyth-solana-receiver";
18
+ import { MyFirstPythApp, IDL } from "./idl/my_first_pyth_app";
19
+
20
+ const SOL_PRICE_FEED_ID =
21
+ "0xef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d";
22
+ const ETH_PRICE_FEED_ID =
23
+ "0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace";
24
+
25
+ const priceServiceConnection = new PriceServiceConnection(
26
+ "https://hermes.pyth.network/",
27
+ { priceFeedRequestConfig: { binary: true } }
28
+ );
29
+ const priceUpdateData = await priceServiceConnection.getLatestVaas([
30
+ SOL_PRICE_FEED_ID,
31
+ ETH_PRICE_FEED_ID,
32
+ ]); // Fetch off-chain price update data
33
+
34
+ const myFirstPythApp = new Program<MyFirstPythApp>(
35
+ IDL as MyFirstPythApp,
36
+ MY_FIRST_PYTH_APP_PROGRAM_ID,
37
+ {}
38
+ );
39
+
40
+ const transactionBuilder = pythSolanaReceiver.newTransactionBuilder({});
41
+ await transactionBuilder.addPostPriceUpdates(priceUpdateData);
42
+ await transactionBuilder.addPriceConsumerInstructions(
43
+ async (
44
+ getPriceUpdateAccount: (priceFeedId: string) => PublicKey
45
+ ): Promise<InstructionWithEphemeralSigners[]> => {
46
+ return [
47
+ {
48
+ instruction: await myFirstPythApp.methods
49
+ .consume()
50
+ .accounts({
51
+ solPriceUpdate: getPriceUpdateAccount(SOL_PRICE_FEED_ID),
52
+ ethPriceUpdate: getPriceUpdateAccount(ETH_PRICE_FEED_ID),
53
+ })
54
+ .instruction(),
55
+ signers: [],
56
+ },
57
+ ];
58
+ }
59
+ );
60
+ await pythSolanaReceiver.provider.sendAll(
61
+ await transactionBuilder.buildVersionedTransactions({
62
+ computeUnitPriceMicroLamports: 1000000,
63
+ })
64
+ );
65
+ ```
66
+
67
+ Alternatively you can use the instruction builder methods from `PythSolanaReceiver` :
68
+
69
+ ```ts
70
+ import { PublicKey } from "@solana/web3.js";
71
+ import { PriceServiceConnection } from "@pythnetwork/price-service-client";
72
+ import { PythSolanaReceiver } from "@pythnetwork/pyth-solana-receiver";
73
+ import { MyFirstPythApp, IDL } from "./idl/my_first_pyth_app";
74
+
75
+ const SOL_PRICE_FEED_ID =
76
+ "0xef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d";
77
+ const ETH_PRICE_FEED_ID =
78
+ "0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace";
79
+
80
+ const priceServiceConnection = new PriceServiceConnection(
81
+ "https://hermes.pyth.network/",
82
+ { priceFeedRequestConfig: { binary: true } }
83
+ );
84
+ const priceUpdateData = await priceServiceConnection.getLatestVaas([
85
+ SOL_PRICE_FEED_ID,
86
+ ETH_PRICE_FEED_ID,
87
+ ]); // Fetch off-chain price update data
88
+
89
+ const pythSolanaReceiver = new PythSolanaReceiver({ connection, wallet });
90
+ const { postInstructions, closeInstructions, priceFeedIdToPriceUpdateAccount } =
91
+ await pythSolanaReceiver.buildPostPriceUpdateInstructions(priceUpdateData); // Get instructions to post the price update data and to close the accounts later
92
+
93
+ const myFirstPythApp = new Program<MyFirstPythApp>(
94
+ IDL as MyFirstPythApp,
95
+ MY_FIRST_PYTH_APP_PROGRAM_ID,
96
+ {}
97
+ );
98
+ const consumerInstruction: InstructionWithEphemeralSigners = {
99
+ instruction: await myFirstPythApp.methods
100
+ .consume()
101
+ .accounts({
102
+ solPriceUpdate: priceFeedIdToPriceUpdateAccount[SOL_PRICE_FEED_ID],
103
+ ethPriceUpdate: priceFeedIdToPriceUpdateAccount[ETH_PRICE_FEED_ID],
104
+ })
105
+ .instruction(),
106
+ signers: [],
107
+ };
108
+
109
+ const transactions = pythSolanaReceiver.batchIntoVersionedTransactions(
110
+ [...postInstructions, consumerInstruction, ...closeInstructions],
111
+ { computeUnitPriceMicroLamports: 1000000 }
112
+ ); // Put all the instructions together
113
+ await pythSolanaReceiver.provider.sendAll(transactions);
114
+ ```
@@ -1,13 +1,130 @@
1
1
  /// <reference types="node" />
2
2
  import { AnchorProvider, Program } from "@coral-xyz/anchor";
3
- import { Connection, Signer, VersionedTransaction } from "@solana/web3.js";
3
+ import { Connection, Signer, Transaction, VersionedTransaction } from "@solana/web3.js";
4
4
  import { PythSolanaReceiver as PythSolanaReceiverProgram } from "./idl/pyth_solana_receiver";
5
5
  import { WormholeCoreBridgeSolana } from "./idl/wormhole_core_bridge_solana";
6
6
  import { PublicKey } from "@solana/web3.js";
7
7
  import { Wallet } from "@coral-xyz/anchor/dist/cjs/provider";
8
- import { InstructionWithEphemeralSigners } from "@pythnetwork/solana-utils";
9
- import { PriorityFeeConfig } from "@pythnetwork/solana-utils/lib/transaction";
10
- export declare const DEFAULT_TREASURY_ID = 0;
8
+ import { TransactionBuilder, InstructionWithEphemeralSigners, PriorityFeeConfig } from "@pythnetwork/solana-utils";
9
+ /**
10
+ * Configuration for the PythTransactionBuilder
11
+ * @property closeUpdateAccounts (default: true) if true, the builder will add instructions to close the price update accounts and the encoded vaa accounts to recover the rent
12
+ */
13
+ export type PythTransactionBuilderConfig = {
14
+ closeUpdateAccounts?: boolean;
15
+ };
16
+ /**
17
+ * A builder class to build transactions that:
18
+ * - Post price updates (fully or partially verified)
19
+ * - Consume price updates in a consumer program
20
+ * - (Optionally) Close price update and encoded vaa accounts to recover the rent (`closeUpdateAccounts` in `PythTransactionBuilderConfig`)
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * const priceUpdateData = await priceServiceConnection.getLatestVaas([
25
+ * SOL_PRICE_FEED_ID,
26
+ * ETH_PRICE_FEED_ID,
27
+ * ]);
28
+ *
29
+ * const transactionBuilder = pythSolanaReceiver.newTransactionBuilder({});
30
+ * await transactionBuilder.addPostPriceUpdates(priceUpdateData);
31
+ * await transactionBuilder.addPriceConsumerInstructions(...)
32
+ *
33
+ * await pythSolanaReceiver.provider.sendAll(await transactionBuilder.buildVersionedTransactions({computeUnitPriceMicroLamports:1000000}))
34
+ * ```
35
+ */
36
+ export declare class PythTransactionBuilder extends TransactionBuilder {
37
+ readonly pythSolanaReceiver: PythSolanaReceiver;
38
+ readonly closeInstructions: InstructionWithEphemeralSigners[];
39
+ readonly priceFeedIdToPriceUpdateAccount: Record<string, PublicKey>;
40
+ readonly closeUpdateAccounts: boolean;
41
+ constructor(pythSolanaReceiver: PythSolanaReceiver, config: PythTransactionBuilderConfig);
42
+ /**
43
+ * Add instructions to post price updates to the builder.
44
+ *
45
+ * @param priceUpdateDataArray the output of the `@pythnetwork/price-service-client`'s `PriceServiceConnection.getLatestVaas`. This is an array of verifiable price updates.
46
+ */
47
+ addPostPriceUpdates(priceUpdateDataArray: string[]): Promise<void>;
48
+ /**
49
+ * Add instructions to post partially verified price updates to the builder.
50
+ *
51
+ * @param priceUpdateDataArray the output of the `@pythnetwork/price-service-client`'s `PriceServiceConnection.getLatestVaas`. This is an array of verifiable price updates.
52
+ *
53
+ * Partially verified price updates are price updates where not all the guardian signatures have been verified. By default this methods checks `DEFAULT_REDUCED_GUARDIAN_SET_SIZE` signatures when posting the VAA.
54
+ * If you are a on-chain program developer, make sure you understand the risks of consuming partially verified price updates here: {@link https://github.com/pyth-network/pyth-crosschain/blob/main/target_chains/solana/pyth_solana_receiver_state/src/price_update.rs}.
55
+ *
56
+ * @example
57
+ * ```typescript
58
+ * const priceUpdateData = await priceServiceConnection.getLatestVaas([
59
+ * SOL_PRICE_FEED_ID,
60
+ * ETH_PRICE_FEED_ID,
61
+ * ]);
62
+ *
63
+ * const transactionBuilder = pythSolanaReceiver.newTransactionBuilder({});
64
+ * await transactionBuilder.addPostPartiallyVerifiedPriceUpdates(priceUpdateData);
65
+ * await transactionBuilder.addPriceConsumerInstructions(...)
66
+ * ...
67
+ * ```
68
+ */
69
+ addPostPartiallyVerifiedPriceUpdates(priceUpdateDataArray: string[]): Promise<void>;
70
+ /**
71
+ * Add instructions that consume price updates to the builder.
72
+ *
73
+ * @param getInstructions a function that given a mapping of price feed IDs to price update accounts, generates a series of instructions. Price updates get posted to ephemeral accounts and this function allows the user to indicate which accounts in their instruction need to be "replaced" with each price update account.
74
+ * If multiple price updates for the same price feed id are posted with the same builder, the account corresponding to the last update to get posted will be used.
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * ...
79
+ * await transactionBuilder.addPostPriceUpdates(priceUpdateData);
80
+ * await transactionBuilder.addPriceConsumerInstructions(
81
+ * async (
82
+ * getPriceUpdateAccount: ( priceFeedId: string) => PublicKey
83
+ * ): Promise<InstructionWithEphemeralSigners[]> => {
84
+ * return [
85
+ * {
86
+ * instruction: await myFirstPythApp.methods
87
+ * .consume()
88
+ * .accounts({
89
+ * solPriceUpdate: getPriceUpdateAccount(SOL_PRICE_FEED_ID),
90
+ * ethPriceUpdate: getPriceUpdateAccount(ETH_PRICE_FEED_ID),
91
+ * })
92
+ * .instruction(),
93
+ * signers: [],
94
+ * },
95
+ * ];
96
+ * }
97
+ * );
98
+ * ```
99
+ */
100
+ addPriceConsumerInstructions(getInstructions: (getPriceUpdateAccount: (priceFeedId: string) => PublicKey) => Promise<InstructionWithEphemeralSigners[]>): Promise<void>;
101
+ /**
102
+ * Returns all the added instructions batched into versioned transactions, plus for each transaction the ephemeral signers that need to sign it
103
+ */
104
+ buildVersionedTransactions(args: PriorityFeeConfig): Promise<{
105
+ tx: VersionedTransaction;
106
+ signers: Signer[];
107
+ }[]>;
108
+ /**
109
+ * Returns all the added instructions batched into transactions, plus for each transaction the ephemeral signers that need to sign it
110
+ */
111
+ buildLegacyTransactions(args: PriorityFeeConfig): {
112
+ tx: Transaction;
113
+ signers: Signer[];
114
+ }[];
115
+ /**
116
+ * This method is used to retrieve the address of the price update account where the price update for a given price feed id will be posted.
117
+ * If multiple price updates for the same price feed id will be posted with the same builder, the address of the account corresponding to the last update to get posted will be returned.
118
+ * */
119
+ getPriceUpdateAccount(priceFeedId: string): PublicKey;
120
+ }
121
+ /**
122
+ * A class to interact with the Pyth Solana Receiver program.
123
+ *
124
+ * This class provides helpful methods to build instructions to interact with the Pyth Solana Receiver program:
125
+ * - Post price updates (fully or partially verified)
126
+ * - Close price update and encoded vaa accounts to recover rent
127
+ */
11
128
  export declare class PythSolanaReceiver {
12
129
  readonly connection: Connection;
13
130
  readonly wallet: Wallet;
@@ -20,30 +137,66 @@ export declare class PythSolanaReceiver {
20
137
  wormholeProgramId?: PublicKey;
21
138
  receiverProgramId?: PublicKey;
22
139
  });
23
- withPriceUpdate(priceUpdateDataArray: string[], getInstructions: (priceFeedIdToPriceUpdateAccount: Record<string, PublicKey>) => Promise<InstructionWithEphemeralSigners[]>, priorityFeeConfig?: PriorityFeeConfig): Promise<{
24
- tx: VersionedTransaction;
25
- signers: Signer[];
26
- }[]>;
27
- withPartiallyVerifiedPriceUpdate(priceUpdateDataArray: string[], getInstructions: (priceFeedIdToPriceUpdateAccount: Record<string, PublicKey>) => Promise<InstructionWithEphemeralSigners[]>, priorityFeeConfig?: PriorityFeeConfig): Promise<{
28
- tx: VersionedTransaction;
29
- signers: Signer[];
30
- }[]>;
140
+ /**
141
+ * Get a new transaction builder to build transactions that interact with the Pyth Solana Receiver program and consume price updates
142
+ */
143
+ newTransactionBuilder(config: PythTransactionBuilderConfig): PythTransactionBuilder;
144
+ /**
145
+ * Build a series of helper instructions that post price updates to the Pyth Solana Receiver program and another series to close the price update accounts.
146
+ *
147
+ * This function uses partially verified price updates. Partially verified price updates are price updates where not all the guardian signatures have been verified. By default this methods checks `DEFAULT_REDUCED_GUARDIAN_SET_SIZE` signatures when posting the VAA.
148
+ * If you are a on-chain program developer, make sure you understand the risks of consuming partially verified price updates here: {@link https://github.com/pyth-network/pyth-crosschain/blob/main/target_chains/solana/pyth_solana_receiver_state/src/price_update.rs}.
149
+ *
150
+ * @param priceUpdateDataArray the output of the `@pythnetwork/price-service-client`'s `PriceServiceConnection.getLatestVaas`. This is an array of verifiable price updates.
151
+ * @returns `postInstructions`: the instructions to post the price updates, these should be called before consuming the price updates
152
+ * @returns `priceFeedIdToPriceUpdateAccount`: this is a map of price feed IDs to Solana address. Given a price feed ID, you can use this map to find the account where `postInstructions` will post the price update.
153
+ * @returns `closeInstructions`: the instructions to close the price update accounts, these should be called after consuming the price updates
154
+ */
31
155
  buildPostPriceUpdateAtomicInstructions(priceUpdateDataArray: string[]): Promise<{
32
156
  postInstructions: InstructionWithEphemeralSigners[];
33
157
  priceFeedIdToPriceUpdateAccount: Record<string, PublicKey>;
34
- cleanupInstructions: InstructionWithEphemeralSigners[];
158
+ closeInstructions: InstructionWithEphemeralSigners[];
35
159
  }>;
160
+ /**
161
+ * Build a series of helper instructions that post a VAA in an encoded VAA account. This function is bespoke for posting Pyth VAAs and might not work for other usecases.
162
+ *
163
+ * @param vaa a Wormhole VAA
164
+ * @returns `postInstructions`: the instructions to post the VAA
165
+ * @returns `encodedVaaAddress`: the address of the encoded VAA account where the VAA will be posted
166
+ * @returns `closeInstructions`: the instructions to close the encoded VAA account
167
+ */
36
168
  buildPostEncodedVaaInstructions(vaa: Buffer): Promise<{
37
169
  postInstructions: InstructionWithEphemeralSigners[];
38
170
  encodedVaaAddress: PublicKey;
39
- cleanupInstructions: InstructionWithEphemeralSigners[];
171
+ closeInstructions: InstructionWithEphemeralSigners[];
40
172
  }>;
173
+ /**
174
+ * Build a series of helper instructions that post price updates to the Pyth Solana Receiver program and another series to close the encoded vaa accounts and the price update accounts.
175
+ *
176
+ * @param priceUpdateDataArray the output of the `@pythnetwork/price-service-client`'s `PriceServiceConnection.getLatestVaas`. This is an array of verifiable price updates.
177
+ * @returns `postInstructions`: the instructions to post the price updates, these should be called before consuming the price updates
178
+ * @returns `priceFeedIdToPriceUpdateAccount`: this is a map of price feed IDs to Solana address. Given a price feed ID, you can use this map to find the account where `postInstructions` will post the price update.
179
+ * @returns `closeInstructions`: the instructions to close the price update accounts, these should be called after consuming the price updates
180
+ */
41
181
  buildPostPriceUpdateInstructions(priceUpdateDataArray: string[]): Promise<{
42
182
  postInstructions: InstructionWithEphemeralSigners[];
43
183
  priceFeedIdToPriceUpdateAccount: Record<string, PublicKey>;
44
- cleanupInstructions: InstructionWithEphemeralSigners[];
184
+ closeInstructions: InstructionWithEphemeralSigners[];
45
185
  }>;
186
+ /**
187
+ * Build an instruction to close an encoded VAA account, recovering the rent.
188
+ */
46
189
  buildCloseEncodedVaaInstruction(encodedVaa: PublicKey): Promise<InstructionWithEphemeralSigners>;
190
+ /**
191
+ * Build an instruction to close a price update account, recovering the rent.
192
+ */
47
193
  buildClosePriceUpdateInstruction(priceUpdateAccount: PublicKey): Promise<InstructionWithEphemeralSigners>;
194
+ /**
195
+ * Returns a set of versioned transactions that contain the provided instructions in the same order and with efficient batching
196
+ */
197
+ batchIntoVersionedTransactions(instructions: InstructionWithEphemeralSigners[], priorityFeeConfig: PriorityFeeConfig): Promise<{
198
+ tx: VersionedTransaction;
199
+ signers: Signer[];
200
+ }[]>;
48
201
  }
49
202
  //# sourceMappingURL=PythSolanaReceiver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PythSolanaReceiver.d.ts","sourceRoot":"","sources":["../src/PythSolanaReceiver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PythSolanaReceiver.d.ts","sourceRoot":"","sources":["../src/PythSolanaReceiver.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PythSolanaReceiver = exports.DEFAULT_TREASURY_ID = void 0;
3
+ exports.PythSolanaReceiver = exports.PythTransactionBuilder = void 0;
4
4
  const anchor_1 = require("@coral-xyz/anchor");
5
5
  const pyth_solana_receiver_1 = require("./idl/pyth_solana_receiver");
6
6
  const wormhole_core_bridge_solana_1 = require("./idl/wormhole_core_bridge_solana");
@@ -10,7 +10,147 @@ const price_service_sdk_1 = require("@pythnetwork/price-service-sdk");
10
10
  const compute_budget_1 = require("./compute_budget");
11
11
  const vaa_1 = require("./vaa");
12
12
  const solana_utils_1 = require("@pythnetwork/solana-utils");
13
- exports.DEFAULT_TREASURY_ID = 0;
13
+ /**
14
+ * A builder class to build transactions that:
15
+ * - Post price updates (fully or partially verified)
16
+ * - Consume price updates in a consumer program
17
+ * - (Optionally) Close price update and encoded vaa accounts to recover the rent (`closeUpdateAccounts` in `PythTransactionBuilderConfig`)
18
+ *
19
+ * @example
20
+ * ```typescript
21
+ * const priceUpdateData = await priceServiceConnection.getLatestVaas([
22
+ * SOL_PRICE_FEED_ID,
23
+ * ETH_PRICE_FEED_ID,
24
+ * ]);
25
+ *
26
+ * const transactionBuilder = pythSolanaReceiver.newTransactionBuilder({});
27
+ * await transactionBuilder.addPostPriceUpdates(priceUpdateData);
28
+ * await transactionBuilder.addPriceConsumerInstructions(...)
29
+ *
30
+ * await pythSolanaReceiver.provider.sendAll(await transactionBuilder.buildVersionedTransactions({computeUnitPriceMicroLamports:1000000}))
31
+ * ```
32
+ */
33
+ class PythTransactionBuilder extends solana_utils_1.TransactionBuilder {
34
+ pythSolanaReceiver;
35
+ closeInstructions;
36
+ priceFeedIdToPriceUpdateAccount;
37
+ closeUpdateAccounts;
38
+ constructor(pythSolanaReceiver, config) {
39
+ super(pythSolanaReceiver.wallet.publicKey, pythSolanaReceiver.connection);
40
+ this.pythSolanaReceiver = pythSolanaReceiver;
41
+ this.closeInstructions = [];
42
+ this.priceFeedIdToPriceUpdateAccount = {};
43
+ this.closeUpdateAccounts = config.closeUpdateAccounts ?? true;
44
+ }
45
+ /**
46
+ * Add instructions to post price updates to the builder.
47
+ *
48
+ * @param priceUpdateDataArray the output of the `@pythnetwork/price-service-client`'s `PriceServiceConnection.getLatestVaas`. This is an array of verifiable price updates.
49
+ */
50
+ async addPostPriceUpdates(priceUpdateDataArray) {
51
+ const { postInstructions, priceFeedIdToPriceUpdateAccount, closeInstructions, } = await this.pythSolanaReceiver.buildPostPriceUpdateInstructions(priceUpdateDataArray);
52
+ this.closeInstructions.push(...closeInstructions);
53
+ Object.assign(this.priceFeedIdToPriceUpdateAccount, priceFeedIdToPriceUpdateAccount);
54
+ this.addInstructions(postInstructions);
55
+ }
56
+ /**
57
+ * Add instructions to post partially verified price updates to the builder.
58
+ *
59
+ * @param priceUpdateDataArray the output of the `@pythnetwork/price-service-client`'s `PriceServiceConnection.getLatestVaas`. This is an array of verifiable price updates.
60
+ *
61
+ * Partially verified price updates are price updates where not all the guardian signatures have been verified. By default this methods checks `DEFAULT_REDUCED_GUARDIAN_SET_SIZE` signatures when posting the VAA.
62
+ * If you are a on-chain program developer, make sure you understand the risks of consuming partially verified price updates here: {@link https://github.com/pyth-network/pyth-crosschain/blob/main/target_chains/solana/pyth_solana_receiver_state/src/price_update.rs}.
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * const priceUpdateData = await priceServiceConnection.getLatestVaas([
67
+ * SOL_PRICE_FEED_ID,
68
+ * ETH_PRICE_FEED_ID,
69
+ * ]);
70
+ *
71
+ * const transactionBuilder = pythSolanaReceiver.newTransactionBuilder({});
72
+ * await transactionBuilder.addPostPartiallyVerifiedPriceUpdates(priceUpdateData);
73
+ * await transactionBuilder.addPriceConsumerInstructions(...)
74
+ * ...
75
+ * ```
76
+ */
77
+ async addPostPartiallyVerifiedPriceUpdates(priceUpdateDataArray) {
78
+ const { postInstructions, priceFeedIdToPriceUpdateAccount, closeInstructions, } = await this.pythSolanaReceiver.buildPostPriceUpdateAtomicInstructions(priceUpdateDataArray);
79
+ this.closeInstructions.push(...closeInstructions);
80
+ Object.assign(this.priceFeedIdToPriceUpdateAccount, priceFeedIdToPriceUpdateAccount);
81
+ this.addInstructions(postInstructions);
82
+ }
83
+ /**
84
+ * Add instructions that consume price updates to the builder.
85
+ *
86
+ * @param getInstructions a function that given a mapping of price feed IDs to price update accounts, generates a series of instructions. Price updates get posted to ephemeral accounts and this function allows the user to indicate which accounts in their instruction need to be "replaced" with each price update account.
87
+ * If multiple price updates for the same price feed id are posted with the same builder, the account corresponding to the last update to get posted will be used.
88
+ *
89
+ * @example
90
+ * ```typescript
91
+ * ...
92
+ * await transactionBuilder.addPostPriceUpdates(priceUpdateData);
93
+ * await transactionBuilder.addPriceConsumerInstructions(
94
+ * async (
95
+ * getPriceUpdateAccount: ( priceFeedId: string) => PublicKey
96
+ * ): Promise<InstructionWithEphemeralSigners[]> => {
97
+ * return [
98
+ * {
99
+ * instruction: await myFirstPythApp.methods
100
+ * .consume()
101
+ * .accounts({
102
+ * solPriceUpdate: getPriceUpdateAccount(SOL_PRICE_FEED_ID),
103
+ * ethPriceUpdate: getPriceUpdateAccount(ETH_PRICE_FEED_ID),
104
+ * })
105
+ * .instruction(),
106
+ * signers: [],
107
+ * },
108
+ * ];
109
+ * }
110
+ * );
111
+ * ```
112
+ */
113
+ async addPriceConsumerInstructions(getInstructions) {
114
+ this.addInstructions(await getInstructions(this.getPriceUpdateAccount.bind(this)));
115
+ }
116
+ /**
117
+ * Returns all the added instructions batched into versioned transactions, plus for each transaction the ephemeral signers that need to sign it
118
+ */
119
+ async buildVersionedTransactions(args) {
120
+ if (this.closeUpdateAccounts) {
121
+ this.addInstructions(this.closeInstructions);
122
+ }
123
+ return super.buildVersionedTransactions(args);
124
+ }
125
+ /**
126
+ * Returns all the added instructions batched into transactions, plus for each transaction the ephemeral signers that need to sign it
127
+ */
128
+ buildLegacyTransactions(args) {
129
+ if (this.closeUpdateAccounts) {
130
+ this.addInstructions(this.closeInstructions);
131
+ }
132
+ return super.buildLegacyTransactions(args);
133
+ }
134
+ /**
135
+ * This method is used to retrieve the address of the price update account where the price update for a given price feed id will be posted.
136
+ * If multiple price updates for the same price feed id will be posted with the same builder, the address of the account corresponding to the last update to get posted will be returned.
137
+ * */
138
+ getPriceUpdateAccount(priceFeedId) {
139
+ const priceUpdateAccount = this.priceFeedIdToPriceUpdateAccount[priceFeedId];
140
+ if (!priceUpdateAccount) {
141
+ throw new Error(`No price update account found for the price feed ID ${priceFeedId}. Make sure to call addPostPriceUpdates or addPostPartiallyVerifiedPriceUpdates before calling this function.`);
142
+ }
143
+ return priceUpdateAccount;
144
+ }
145
+ }
146
+ exports.PythTransactionBuilder = PythTransactionBuilder;
147
+ /**
148
+ * A class to interact with the Pyth Solana Receiver program.
149
+ *
150
+ * This class provides helpful methods to build instructions to interact with the Pyth Solana Receiver program:
151
+ * - Post price updates (fully or partially verified)
152
+ * - Close price update and encoded vaa accounts to recover rent
153
+ */
14
154
  class PythSolanaReceiver {
15
155
  connection;
16
156
  wallet;
@@ -26,26 +166,27 @@ class PythSolanaReceiver {
26
166
  this.receiver = new anchor_1.Program(pyth_solana_receiver_1.IDL, receiverProgramId, this.provider);
27
167
  this.wormhole = new anchor_1.Program(wormhole_core_bridge_solana_1.IDL, wormholeProgramId, this.provider);
28
168
  }
29
- async withPriceUpdate(priceUpdateDataArray, getInstructions, priorityFeeConfig) {
30
- const { postInstructions, priceFeedIdToPriceUpdateAccount: priceFeedIdToPriceUpdateAccount, cleanupInstructions, } = await this.buildPostPriceUpdateInstructions(priceUpdateDataArray);
31
- return solana_utils_1.TransactionBuilder.batchIntoVersionedTransactions(this.wallet.publicKey, this.connection, [
32
- ...postInstructions,
33
- ...(await getInstructions(priceFeedIdToPriceUpdateAccount)),
34
- ...cleanupInstructions,
35
- ], priorityFeeConfig ?? {});
36
- }
37
- async withPartiallyVerifiedPriceUpdate(priceUpdateDataArray, getInstructions, priorityFeeConfig) {
38
- const { postInstructions, priceFeedIdToPriceUpdateAccount, cleanupInstructions, } = await this.buildPostPriceUpdateAtomicInstructions(priceUpdateDataArray);
39
- return solana_utils_1.TransactionBuilder.batchIntoVersionedTransactions(this.wallet.publicKey, this.connection, [
40
- ...postInstructions,
41
- ...(await getInstructions(priceFeedIdToPriceUpdateAccount)),
42
- ...cleanupInstructions,
43
- ], priorityFeeConfig ?? {});
169
+ /**
170
+ * Get a new transaction builder to build transactions that interact with the Pyth Solana Receiver program and consume price updates
171
+ */
172
+ newTransactionBuilder(config) {
173
+ return new PythTransactionBuilder(this, config);
44
174
  }
175
+ /**
176
+ * Build a series of helper instructions that post price updates to the Pyth Solana Receiver program and another series to close the price update accounts.
177
+ *
178
+ * This function uses partially verified price updates. Partially verified price updates are price updates where not all the guardian signatures have been verified. By default this methods checks `DEFAULT_REDUCED_GUARDIAN_SET_SIZE` signatures when posting the VAA.
179
+ * If you are a on-chain program developer, make sure you understand the risks of consuming partially verified price updates here: {@link https://github.com/pyth-network/pyth-crosschain/blob/main/target_chains/solana/pyth_solana_receiver_state/src/price_update.rs}.
180
+ *
181
+ * @param priceUpdateDataArray the output of the `@pythnetwork/price-service-client`'s `PriceServiceConnection.getLatestVaas`. This is an array of verifiable price updates.
182
+ * @returns `postInstructions`: the instructions to post the price updates, these should be called before consuming the price updates
183
+ * @returns `priceFeedIdToPriceUpdateAccount`: this is a map of price feed IDs to Solana address. Given a price feed ID, you can use this map to find the account where `postInstructions` will post the price update.
184
+ * @returns `closeInstructions`: the instructions to close the price update accounts, these should be called after consuming the price updates
185
+ */
45
186
  async buildPostPriceUpdateAtomicInstructions(priceUpdateDataArray) {
46
187
  const postInstructions = [];
47
188
  const priceFeedIdToPriceUpdateAccount = {};
48
- const cleanupInstructions = [];
189
+ const closeInstructions = [];
49
190
  for (const priceUpdateData of priceUpdateDataArray) {
50
191
  const accumulatorUpdateData = (0, price_service_sdk_1.parseAccumulatorUpdateData)(Buffer.from(priceUpdateData, "base64"));
51
192
  const guardianSetIndex = (0, vaa_1.getGuardianSetIndex)(accumulatorUpdateData.vaa);
@@ -57,31 +198,39 @@ class PythSolanaReceiver {
57
198
  .postUpdateAtomic({
58
199
  vaa: trimmedVaa,
59
200
  merklePriceUpdate: update,
60
- treasuryId: exports.DEFAULT_TREASURY_ID,
201
+ treasuryId: address_1.DEFAULT_TREASURY_ID,
61
202
  })
62
203
  .accounts({
63
204
  priceUpdateAccount: priceUpdateKeypair.publicKey,
64
- treasury: (0, address_1.getTreasuryPda)(exports.DEFAULT_TREASURY_ID),
65
- config: (0, address_1.getConfigPda)(),
66
- guardianSet: (0, address_1.getGuardianSetPda)(guardianSetIndex),
205
+ treasury: (0, address_1.getTreasuryPda)(address_1.DEFAULT_TREASURY_ID, this.receiver.programId),
206
+ config: (0, address_1.getConfigPda)(this.receiver.programId),
207
+ guardianSet: (0, address_1.getGuardianSetPda)(guardianSetIndex, this.wormhole.programId),
67
208
  })
68
209
  .instruction(),
69
210
  signers: [priceUpdateKeypair],
70
211
  computeUnits: compute_budget_1.POST_UPDATE_ATOMIC_COMPUTE_BUDGET,
71
212
  });
72
213
  priceFeedIdToPriceUpdateAccount["0x" + (0, price_service_sdk_1.parsePriceFeedMessage)(update.message).feedId.toString("hex")] = priceUpdateKeypair.publicKey;
73
- cleanupInstructions.push(await this.buildClosePriceUpdateInstruction(priceUpdateKeypair.publicKey));
214
+ closeInstructions.push(await this.buildClosePriceUpdateInstruction(priceUpdateKeypair.publicKey));
74
215
  }
75
216
  }
76
217
  return {
77
218
  postInstructions,
78
219
  priceFeedIdToPriceUpdateAccount,
79
- cleanupInstructions,
220
+ closeInstructions,
80
221
  };
81
222
  }
223
+ /**
224
+ * Build a series of helper instructions that post a VAA in an encoded VAA account. This function is bespoke for posting Pyth VAAs and might not work for other usecases.
225
+ *
226
+ * @param vaa a Wormhole VAA
227
+ * @returns `postInstructions`: the instructions to post the VAA
228
+ * @returns `encodedVaaAddress`: the address of the encoded VAA account where the VAA will be posted
229
+ * @returns `closeInstructions`: the instructions to close the encoded VAA account
230
+ */
82
231
  async buildPostEncodedVaaInstructions(vaa) {
83
232
  const postInstructions = [];
84
- const cleanupInstructions = [];
233
+ const closeInstructions = [];
85
234
  const encodedVaaKeypair = new web3_js_1.Keypair();
86
235
  const guardianSetIndex = (0, vaa_1.getGuardianSetIndex)(vaa);
87
236
  postInstructions.push(await (0, vaa_1.buildEncodedVaaCreateInstruction)(this.wormhole, vaa, encodedVaaKeypair));
@@ -94,62 +243,73 @@ class PythSolanaReceiver {
94
243
  .instruction(),
95
244
  signers: [],
96
245
  });
97
- postInstructions.push(...(await (0, vaa_1.buildWriteEncodedVaaWithSplit)(this.wormhole, vaa, encodedVaaKeypair.publicKey)));
246
+ postInstructions.push(...(await (0, vaa_1.buildWriteEncodedVaaWithSplitInstructions)(this.wormhole, vaa, encodedVaaKeypair.publicKey)));
98
247
  postInstructions.push({
99
248
  instruction: await this.wormhole.methods
100
249
  .verifyEncodedVaaV1()
101
250
  .accounts({
102
- guardianSet: (0, address_1.getGuardianSetPda)(guardianSetIndex),
251
+ guardianSet: (0, address_1.getGuardianSetPda)(guardianSetIndex, this.wormhole.programId),
103
252
  draftVaa: encodedVaaKeypair.publicKey,
104
253
  })
105
254
  .instruction(),
106
255
  signers: [],
107
256
  computeUnits: compute_budget_1.VERIFY_ENCODED_VAA_COMPUTE_BUDGET,
108
257
  });
109
- cleanupInstructions.push(await this.buildCloseEncodedVaaInstruction(encodedVaaKeypair.publicKey));
258
+ closeInstructions.push(await this.buildCloseEncodedVaaInstruction(encodedVaaKeypair.publicKey));
110
259
  return {
111
260
  postInstructions,
112
261
  encodedVaaAddress: encodedVaaKeypair.publicKey,
113
- cleanupInstructions,
262
+ closeInstructions,
114
263
  };
115
264
  }
265
+ /**
266
+ * Build a series of helper instructions that post price updates to the Pyth Solana Receiver program and another series to close the encoded vaa accounts and the price update accounts.
267
+ *
268
+ * @param priceUpdateDataArray the output of the `@pythnetwork/price-service-client`'s `PriceServiceConnection.getLatestVaas`. This is an array of verifiable price updates.
269
+ * @returns `postInstructions`: the instructions to post the price updates, these should be called before consuming the price updates
270
+ * @returns `priceFeedIdToPriceUpdateAccount`: this is a map of price feed IDs to Solana address. Given a price feed ID, you can use this map to find the account where `postInstructions` will post the price update.
271
+ * @returns `closeInstructions`: the instructions to close the price update accounts, these should be called after consuming the price updates
272
+ */
116
273
  async buildPostPriceUpdateInstructions(priceUpdateDataArray) {
117
274
  const postInstructions = [];
118
275
  const priceFeedIdToPriceUpdateAccount = {};
119
- const cleanupInstructions = [];
276
+ const closeInstructions = [];
120
277
  for (const priceUpdateData of priceUpdateDataArray) {
121
278
  const accumulatorUpdateData = (0, price_service_sdk_1.parseAccumulatorUpdateData)(Buffer.from(priceUpdateData, "base64"));
122
- const { postInstructions: postEncodedVaaInstructions, encodedVaaAddress: encodedVaa, cleanupInstructions: postEncodedVaaCleanupInstructions, } = await this.buildPostEncodedVaaInstructions(accumulatorUpdateData.vaa);
279
+ const { postInstructions: postEncodedVaaInstructions, encodedVaaAddress: encodedVaa, closeInstructions: postEncodedVaacloseInstructions, } = await this.buildPostEncodedVaaInstructions(accumulatorUpdateData.vaa);
123
280
  postInstructions.push(...postEncodedVaaInstructions);
124
- cleanupInstructions.push(...postEncodedVaaCleanupInstructions);
281
+ closeInstructions.push(...postEncodedVaacloseInstructions);
125
282
  for (const update of accumulatorUpdateData.updates) {
126
283
  const priceUpdateKeypair = new web3_js_1.Keypair();
127
284
  postInstructions.push({
128
285
  instruction: await this.receiver.methods
129
286
  .postUpdate({
130
287
  merklePriceUpdate: update,
131
- treasuryId: exports.DEFAULT_TREASURY_ID,
288
+ treasuryId: address_1.DEFAULT_TREASURY_ID,
132
289
  })
133
290
  .accounts({
134
291
  encodedVaa,
135
292
  priceUpdateAccount: priceUpdateKeypair.publicKey,
136
- treasury: (0, address_1.getTreasuryPda)(exports.DEFAULT_TREASURY_ID),
137
- config: (0, address_1.getConfigPda)(),
293
+ treasury: (0, address_1.getTreasuryPda)(address_1.DEFAULT_TREASURY_ID, this.receiver.programId),
294
+ config: (0, address_1.getConfigPda)(this.receiver.programId),
138
295
  })
139
296
  .instruction(),
140
297
  signers: [priceUpdateKeypair],
141
298
  computeUnits: compute_budget_1.POST_UPDATE_COMPUTE_BUDGET,
142
299
  });
143
300
  priceFeedIdToPriceUpdateAccount["0x" + (0, price_service_sdk_1.parsePriceFeedMessage)(update.message).feedId.toString("hex")] = priceUpdateKeypair.publicKey;
144
- cleanupInstructions.push(await this.buildClosePriceUpdateInstruction(priceUpdateKeypair.publicKey));
301
+ closeInstructions.push(await this.buildClosePriceUpdateInstruction(priceUpdateKeypair.publicKey));
145
302
  }
146
303
  }
147
304
  return {
148
305
  postInstructions,
149
306
  priceFeedIdToPriceUpdateAccount,
150
- cleanupInstructions,
307
+ closeInstructions,
151
308
  };
152
309
  }
310
+ /**
311
+ * Build an instruction to close an encoded VAA account, recovering the rent.
312
+ */
153
313
  async buildCloseEncodedVaaInstruction(encodedVaa) {
154
314
  const instruction = await this.wormhole.methods
155
315
  .closeEncodedVaa()
@@ -157,6 +317,9 @@ class PythSolanaReceiver {
157
317
  .instruction();
158
318
  return { instruction, signers: [] };
159
319
  }
320
+ /**
321
+ * Build an instruction to close a price update account, recovering the rent.
322
+ */
160
323
  async buildClosePriceUpdateInstruction(priceUpdateAccount) {
161
324
  const instruction = await this.receiver.methods
162
325
  .reclaimRent()
@@ -164,5 +327,11 @@ class PythSolanaReceiver {
164
327
  .instruction();
165
328
  return { instruction, signers: [] };
166
329
  }
330
+ /**
331
+ * Returns a set of versioned transactions that contain the provided instructions in the same order and with efficient batching
332
+ */
333
+ async batchIntoVersionedTransactions(instructions, priorityFeeConfig) {
334
+ return solana_utils_1.TransactionBuilder.batchIntoVersionedTransactions(this.wallet.publicKey, this.connection, instructions, priorityFeeConfig);
335
+ }
167
336
  }
168
337
  exports.PythSolanaReceiver = PythSolanaReceiver;
package/lib/address.d.ts CHANGED
@@ -1,7 +1,29 @@
1
1
  import { PublicKey } from "@solana/web3.js";
2
+ /**
3
+ * The default Pyth Solana Receiver program ID.
4
+ * The program is deployed at this address on all SVM networks.
5
+ */
2
6
  export declare const DEFAULT_RECEIVER_PROGRAM_ID: PublicKey;
7
+ /**
8
+ * The default Wormhole program ID.
9
+ * The program is deployed at this address on all SVM networks.
10
+ */
3
11
  export declare const DEFAULT_WORMHOLE_PROGRAM_ID: PublicKey;
4
- export declare const getGuardianSetPda: (guardianSetIndex: number) => PublicKey;
5
- export declare const getTreasuryPda: (treasuryId: number) => PublicKey;
6
- export declare const getConfigPda: () => PublicKey;
12
+ /**
13
+ * Returns the address of a guardian set account from the Wormhole program.
14
+ */
15
+ export declare const getGuardianSetPda: (guardianSetIndex: number, wormholeProgramId: PublicKey) => PublicKey;
16
+ /**
17
+ * The Pyth Solana Receiver has one treasury account for each u8 `treasuryId`.
18
+ * This is meant to avoid write-locks on the treasury account by load-balancing the writes across multiple accounts.
19
+ */
20
+ export declare const DEFAULT_TREASURY_ID = 0;
21
+ /**
22
+ * Returns the address of a treasury account from the Pyth Solana Receiver program.
23
+ */
24
+ export declare const getTreasuryPda: (treasuryId: number, receiverProgramId: PublicKey) => PublicKey;
25
+ /**
26
+ * Returns the address of the config account from the Pyth Solana Receiver program.
27
+ */
28
+ export declare const getConfigPda: (receiverProgramId: PublicKey) => PublicKey;
7
29
  //# sourceMappingURL=address.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"address.d.ts","sourceRoot":"","sources":["../src/address.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C,eAAO,MAAM,2BAA2B,WAEvC,CAAC;AACF,eAAO,MAAM,2BAA2B,WAEvC,CAAC;AAEF,eAAO,MAAM,iBAAiB,qBAAsB,MAAM,cAOzD,CAAC;AAEF,eAAO,MAAM,cAAc,eAAgB,MAAM,cAKhD,CAAC;AAEF,eAAO,MAAM,YAAY,iBAKxB,CAAC"}
1
+ {"version":3,"file":"address.d.ts","sourceRoot":"","sources":["../src/address.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAE5C;;;GAGG;AACH,eAAO,MAAM,2BAA2B,WAEvC,CAAC;AACF;;;GAGG;AACH,eAAO,MAAM,2BAA2B,WAEvC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,qBACV,MAAM,qBACL,SAAS,cAQ7B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC;;GAEG;AACH,eAAO,MAAM,cAAc,eACb,MAAM,qBACC,SAAS,cAM7B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY,sBAAuB,SAAS,cAKxD,CAAC"}
package/lib/address.js CHANGED
@@ -1,20 +1,42 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getConfigPda = exports.getTreasuryPda = exports.getGuardianSetPda = exports.DEFAULT_WORMHOLE_PROGRAM_ID = exports.DEFAULT_RECEIVER_PROGRAM_ID = void 0;
3
+ exports.getConfigPda = exports.getTreasuryPda = exports.DEFAULT_TREASURY_ID = exports.getGuardianSetPda = exports.DEFAULT_WORMHOLE_PROGRAM_ID = exports.DEFAULT_RECEIVER_PROGRAM_ID = void 0;
4
4
  const web3_js_1 = require("@solana/web3.js");
5
+ /**
6
+ * The default Pyth Solana Receiver program ID.
7
+ * The program is deployed at this address on all SVM networks.
8
+ */
5
9
  exports.DEFAULT_RECEIVER_PROGRAM_ID = new web3_js_1.PublicKey("rec5EKMGg6MxZYaMdyBfgwp4d5rB9T1VQH5pJv5LtFJ");
10
+ /**
11
+ * The default Wormhole program ID.
12
+ * The program is deployed at this address on all SVM networks.
13
+ */
6
14
  exports.DEFAULT_WORMHOLE_PROGRAM_ID = new web3_js_1.PublicKey("HDwcJBJXjL9FpJ7UBsYBtaDjsBUhuLCUYoz3zr8SWWaQ");
7
- const getGuardianSetPda = (guardianSetIndex) => {
15
+ /**
16
+ * Returns the address of a guardian set account from the Wormhole program.
17
+ */
18
+ const getGuardianSetPda = (guardianSetIndex, wormholeProgramId) => {
8
19
  const guardianSetIndexBuf = Buffer.alloc(4);
9
20
  guardianSetIndexBuf.writeUInt32BE(guardianSetIndex, 0);
10
- return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("GuardianSet"), guardianSetIndexBuf], exports.DEFAULT_WORMHOLE_PROGRAM_ID)[0];
21
+ return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("GuardianSet"), guardianSetIndexBuf], wormholeProgramId)[0];
11
22
  };
12
23
  exports.getGuardianSetPda = getGuardianSetPda;
13
- const getTreasuryPda = (treasuryId) => {
14
- return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("treasury"), Buffer.from([treasuryId])], exports.DEFAULT_RECEIVER_PROGRAM_ID)[0];
24
+ /**
25
+ * The Pyth Solana Receiver has one treasury account for each u8 `treasuryId`.
26
+ * This is meant to avoid write-locks on the treasury account by load-balancing the writes across multiple accounts.
27
+ */
28
+ exports.DEFAULT_TREASURY_ID = 0;
29
+ /**
30
+ * Returns the address of a treasury account from the Pyth Solana Receiver program.
31
+ */
32
+ const getTreasuryPda = (treasuryId, receiverProgramId) => {
33
+ return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("treasury"), Buffer.from([treasuryId])], receiverProgramId)[0];
15
34
  };
16
35
  exports.getTreasuryPda = getTreasuryPda;
17
- const getConfigPda = () => {
18
- return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("config")], exports.DEFAULT_RECEIVER_PROGRAM_ID)[0];
36
+ /**
37
+ * Returns the address of the config account from the Pyth Solana Receiver program.
38
+ */
39
+ const getConfigPda = (receiverProgramId) => {
40
+ return web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("config")], receiverProgramId)[0];
19
41
  };
20
42
  exports.getConfigPda = getConfigPda;
@@ -1,4 +1,13 @@
1
+ /**
2
+ * A hard-coded budget for the compute units required for the `verifyEncodedVaa` instruction in the Wormhole program.
3
+ */
1
4
  export declare const VERIFY_ENCODED_VAA_COMPUTE_BUDGET = 400000;
5
+ /**
6
+ * A hard-coded budget for the compute units required for the `postUpdateAtomic` instruction in the Pyth Solana Receiver program.
7
+ */
2
8
  export declare const POST_UPDATE_ATOMIC_COMPUTE_BUDGET = 400000;
9
+ /**
10
+ * A hard-coded budget for the compute units required for the `postUpdate` instruction in the Pyth Solana Receiver program.
11
+ */
3
12
  export declare const POST_UPDATE_COMPUTE_BUDGET = 200000;
4
13
  //# sourceMappingURL=compute_budget.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"compute_budget.d.ts","sourceRoot":"","sources":["../src/compute_budget.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iCAAiC,SAAS,CAAC;AACxD,eAAO,MAAM,iCAAiC,SAAS,CAAC;AACxD,eAAO,MAAM,0BAA0B,SAAS,CAAC"}
1
+ {"version":3,"file":"compute_budget.d.ts","sourceRoot":"","sources":["../src/compute_budget.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,iCAAiC,SAAS,CAAC;AACxD;;GAEG;AACH,eAAO,MAAM,iCAAiC,SAAS,CAAC;AACxD;;GAEG;AACH,eAAO,MAAM,0BAA0B,SAAS,CAAC"}
@@ -1,6 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.POST_UPDATE_COMPUTE_BUDGET = exports.POST_UPDATE_ATOMIC_COMPUTE_BUDGET = exports.VERIFY_ENCODED_VAA_COMPUTE_BUDGET = void 0;
4
+ /**
5
+ * A hard-coded budget for the compute units required for the `verifyEncodedVaa` instruction in the Wormhole program.
6
+ */
4
7
  exports.VERIFY_ENCODED_VAA_COMPUTE_BUDGET = 400000;
8
+ /**
9
+ * A hard-coded budget for the compute units required for the `postUpdateAtomic` instruction in the Pyth Solana Receiver program.
10
+ */
5
11
  exports.POST_UPDATE_ATOMIC_COMPUTE_BUDGET = 400000;
12
+ /**
13
+ * A hard-coded budget for the compute units required for the `postUpdate` instruction in the Pyth Solana Receiver program.
14
+ */
6
15
  exports.POST_UPDATE_COMPUTE_BUDGET = 200000;
package/lib/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { PythSolanaReceiver } from "./PythSolanaReceiver";
2
- export { TransactionBuilder } from "@pythnetwork/solana-utils";
1
+ export { PythSolanaReceiver, PythTransactionBuilder, } from "./PythSolanaReceiver";
2
+ export { TransactionBuilder, InstructionWithEphemeralSigners, } from "@pythnetwork/solana-utils";
3
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,kBAAkB,EAClB,+BAA+B,GAChC,MAAM,2BAA2B,CAAC"}
package/lib/index.js CHANGED
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TransactionBuilder = exports.PythSolanaReceiver = void 0;
3
+ exports.TransactionBuilder = exports.PythTransactionBuilder = exports.PythSolanaReceiver = void 0;
4
4
  var PythSolanaReceiver_1 = require("./PythSolanaReceiver");
5
5
  Object.defineProperty(exports, "PythSolanaReceiver", { enumerable: true, get: function () { return PythSolanaReceiver_1.PythSolanaReceiver; } });
6
+ Object.defineProperty(exports, "PythTransactionBuilder", { enumerable: true, get: function () { return PythSolanaReceiver_1.PythTransactionBuilder; } });
6
7
  var solana_utils_1 = require("@pythnetwork/solana-utils");
7
8
  Object.defineProperty(exports, "TransactionBuilder", { enumerable: true, get: function () { return solana_utils_1.TransactionBuilder; } });
package/lib/vaa.d.ts CHANGED
@@ -3,15 +3,56 @@ import { Keypair, PublicKey } from "@solana/web3.js";
3
3
  import { WormholeCoreBridgeSolana } from "./idl/wormhole_core_bridge_solana";
4
4
  import { Program } from "@coral-xyz/anchor";
5
5
  import { InstructionWithEphemeralSigners } from "@pythnetwork/solana-utils";
6
- export declare const VAA_START = 46;
7
- export declare const VAA_SIGNATURE_SIZE = 66;
8
- export declare const VAA_SPLIT_INDEX = 792;
9
- export declare const DEFAULT_REDUCED_GUARDIAN_SET_SIZE = 5;
6
+ /**
7
+ * Get the index of the guardian set that signed a VAA
8
+ */
10
9
  export declare function getGuardianSetIndex(vaa: Buffer): number;
10
+ /**
11
+ * The default number of signatures to keep in a VAA when using `trimSignatures`.
12
+ * This number was chosen as the maximum number of signatures so that the VAA's contents can be posted in a single Solana transaction.
13
+ */
14
+ export declare const DEFAULT_REDUCED_GUARDIAN_SET_SIZE = 5;
15
+ /**
16
+ * The size of a guardian signature in a VAA.
17
+ *
18
+ * It is 66 bytes long, the first byte is the guardian index and the next 65 bytes are the signature (including a recovery id).
19
+ */
20
+ export declare const VAA_SIGNATURE_SIZE = 66;
21
+ /**
22
+ * Trim the number of signatures of a VAA.
23
+ *
24
+ * @returns the same VAA as the input, but with `n` signatures instead of the original number of signatures.
25
+ *
26
+ * A Wormhole VAA typically has a number of signatures equal to two thirds of the number of guardians. However,
27
+ * this function is useful to make VAAs smaller to post their contents in a single Solana transaction.
28
+ */
11
29
  export declare function trimSignatures(vaa: Buffer, n?: number): Buffer;
12
- export declare function buildEncodedVaaCreateInstruction(wormhole: Program<WormholeCoreBridgeSolana>, vaa: Buffer, encodedVaaKeypair: Keypair): Promise<{
13
- instruction: import("@solana/web3.js").TransactionInstruction;
14
- signers: Keypair[];
15
- }>;
16
- export declare function buildWriteEncodedVaaWithSplit(wormhole: Program<WormholeCoreBridgeSolana>, vaa: Buffer, draftVaa: PublicKey): Promise<InstructionWithEphemeralSigners[]>;
30
+ /**
31
+ * The start of the VAA bytes in an encoded VAA account. Before this offset, the account contains a header.
32
+ */
33
+ export declare const VAA_START = 46;
34
+ /**
35
+ * Build an instruction to create an encoded VAA account.
36
+ *
37
+ * This is the first step to post a VAA to the Wormhole program.
38
+ */
39
+ export declare function buildEncodedVaaCreateInstruction(wormhole: Program<WormholeCoreBridgeSolana>, vaa: Buffer, encodedVaaKeypair: Keypair): Promise<InstructionWithEphemeralSigners>;
40
+ /**
41
+ * Writing the VAA to to an encoded VAA account is done in 2 instructions.
42
+ *
43
+ * The first one writes the first `VAA_SPLIT_INDEX` bytes and the second one writes the rest.
44
+ *
45
+ * This number was chosen as the biggest number such that one can still call `createInstruction`, `initEncodedVaa` and `writeEncodedVaa` in a single Solana transaction.
46
+ * This way, the packing of the instructions to post an encoded vaa is more efficient.
47
+ */
48
+ export declare const VAA_SPLIT_INDEX = 792;
49
+ /**
50
+ * Build a set of instructions to write a VAA to an encoded VAA account
51
+ * This functions returns 2 instructions and splits the VAA in an opinionated way, so that the whole process of posting a VAA can be efficiently packed in the 2 transactions:
52
+ *
53
+ * TX 1 : `createInstruction` + `initEncodedVaa` + `writeEncodedVaa_1`
54
+ *
55
+ * TX 2 : `writeEncodedVaa_2` + `verifyEncodedVaaV1`
56
+ */
57
+ export declare function buildWriteEncodedVaaWithSplitInstructions(wormhole: Program<WormholeCoreBridgeSolana>, vaa: Buffer, draftVaa: PublicKey): Promise<InstructionWithEphemeralSigners[]>;
17
58
  //# sourceMappingURL=vaa.d.ts.map
package/lib/vaa.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"vaa.d.ts","sourceRoot":"","sources":["../src/vaa.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,+BAA+B,EAAE,MAAM,2BAA2B,CAAC;AAE5E,eAAO,MAAM,SAAS,KAAK,CAAC;AAC5B,eAAO,MAAM,kBAAkB,KAAK,CAAC;AACrC,eAAO,MAAM,eAAe,MAAM,CAAC;AACnC,eAAO,MAAM,iCAAiC,IAAI,CAAC;AAEnD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,UAE9C;AAED,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,CAAC,SAAoC,GACpC,MAAM,CAeR;AAED,wBAAsB,gCAAgC,CACpD,QAAQ,EAAE,OAAO,CAAC,wBAAwB,CAAC,EAC3C,GAAG,EAAE,MAAM,EACX,iBAAiB,EAAE,OAAO;;;GAU3B;AAED,wBAAsB,6BAA6B,CACjD,QAAQ,EAAE,OAAO,CAAC,wBAAwB,CAAC,EAC3C,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,SAAS,GAClB,OAAO,CAAC,+BAA+B,EAAE,CAAC,CA2B5C"}
1
+ {"version":3,"file":"vaa.d.ts","sourceRoot":"","sources":["../src/vaa.ts"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAC5C,OAAO,EAAE,+BAA+B,EAAE,MAAM,2BAA2B,CAAC;AAE5E;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,UAE9C;AAED;;;GAGG;AACH,eAAO,MAAM,iCAAiC,IAAI,CAAC;AAEnD;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,CAAC,SAAoC,GACpC,MAAM,CAeR;AAED;;GAEG;AACH,eAAO,MAAM,SAAS,KAAK,CAAC;AAE5B;;;;GAIG;AACH,wBAAsB,gCAAgC,CACpD,QAAQ,EAAE,OAAO,CAAC,wBAAwB,CAAC,EAC3C,GAAG,EAAE,MAAM,EACX,iBAAiB,EAAE,OAAO,GACzB,OAAO,CAAC,+BAA+B,CAAC,CAS1C;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,eAAe,MAAM,CAAC;AAEnC;;;;;;;GAOG;AACH,wBAAsB,yCAAyC,CAC7D,QAAQ,EAAE,OAAO,CAAC,wBAAwB,CAAC,EAC3C,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,SAAS,GAClB,OAAO,CAAC,+BAA+B,EAAE,CAAC,CA2B5C"}
package/lib/vaa.js CHANGED
@@ -1,14 +1,32 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.buildWriteEncodedVaaWithSplit = exports.buildEncodedVaaCreateInstruction = exports.trimSignatures = exports.getGuardianSetIndex = exports.DEFAULT_REDUCED_GUARDIAN_SET_SIZE = exports.VAA_SPLIT_INDEX = exports.VAA_SIGNATURE_SIZE = exports.VAA_START = void 0;
4
- exports.VAA_START = 46;
5
- exports.VAA_SIGNATURE_SIZE = 66;
6
- exports.VAA_SPLIT_INDEX = 792;
7
- exports.DEFAULT_REDUCED_GUARDIAN_SET_SIZE = 5;
3
+ exports.buildWriteEncodedVaaWithSplitInstructions = exports.VAA_SPLIT_INDEX = exports.buildEncodedVaaCreateInstruction = exports.VAA_START = exports.trimSignatures = exports.VAA_SIGNATURE_SIZE = exports.DEFAULT_REDUCED_GUARDIAN_SET_SIZE = exports.getGuardianSetIndex = void 0;
4
+ /**
5
+ * Get the index of the guardian set that signed a VAA
6
+ */
8
7
  function getGuardianSetIndex(vaa) {
9
8
  return vaa.readUInt32BE(1);
10
9
  }
11
10
  exports.getGuardianSetIndex = getGuardianSetIndex;
11
+ /**
12
+ * The default number of signatures to keep in a VAA when using `trimSignatures`.
13
+ * This number was chosen as the maximum number of signatures so that the VAA's contents can be posted in a single Solana transaction.
14
+ */
15
+ exports.DEFAULT_REDUCED_GUARDIAN_SET_SIZE = 5;
16
+ /**
17
+ * The size of a guardian signature in a VAA.
18
+ *
19
+ * It is 66 bytes long, the first byte is the guardian index and the next 65 bytes are the signature (including a recovery id).
20
+ */
21
+ exports.VAA_SIGNATURE_SIZE = 66;
22
+ /**
23
+ * Trim the number of signatures of a VAA.
24
+ *
25
+ * @returns the same VAA as the input, but with `n` signatures instead of the original number of signatures.
26
+ *
27
+ * A Wormhole VAA typically has a number of signatures equal to two thirds of the number of guardians. However,
28
+ * this function is useful to make VAAs smaller to post their contents in a single Solana transaction.
29
+ */
12
30
  function trimSignatures(vaa, n = exports.DEFAULT_REDUCED_GUARDIAN_SET_SIZE) {
13
31
  const currentNumSignatures = vaa[5];
14
32
  if (n > currentNumSignatures) {
@@ -22,6 +40,15 @@ function trimSignatures(vaa, n = exports.DEFAULT_REDUCED_GUARDIAN_SET_SIZE) {
22
40
  return trimmedVaa;
23
41
  }
24
42
  exports.trimSignatures = trimSignatures;
43
+ /**
44
+ * The start of the VAA bytes in an encoded VAA account. Before this offset, the account contains a header.
45
+ */
46
+ exports.VAA_START = 46;
47
+ /**
48
+ * Build an instruction to create an encoded VAA account.
49
+ *
50
+ * This is the first step to post a VAA to the Wormhole program.
51
+ */
25
52
  async function buildEncodedVaaCreateInstruction(wormhole, vaa, encodedVaaKeypair) {
26
53
  const encodedVaaSize = vaa.length + exports.VAA_START;
27
54
  return {
@@ -30,7 +57,24 @@ async function buildEncodedVaaCreateInstruction(wormhole, vaa, encodedVaaKeypair
30
57
  };
31
58
  }
32
59
  exports.buildEncodedVaaCreateInstruction = buildEncodedVaaCreateInstruction;
33
- async function buildWriteEncodedVaaWithSplit(wormhole, vaa, draftVaa) {
60
+ /**
61
+ * Writing the VAA to to an encoded VAA account is done in 2 instructions.
62
+ *
63
+ * The first one writes the first `VAA_SPLIT_INDEX` bytes and the second one writes the rest.
64
+ *
65
+ * This number was chosen as the biggest number such that one can still call `createInstruction`, `initEncodedVaa` and `writeEncodedVaa` in a single Solana transaction.
66
+ * This way, the packing of the instructions to post an encoded vaa is more efficient.
67
+ */
68
+ exports.VAA_SPLIT_INDEX = 792;
69
+ /**
70
+ * Build a set of instructions to write a VAA to an encoded VAA account
71
+ * This functions returns 2 instructions and splits the VAA in an opinionated way, so that the whole process of posting a VAA can be efficiently packed in the 2 transactions:
72
+ *
73
+ * TX 1 : `createInstruction` + `initEncodedVaa` + `writeEncodedVaa_1`
74
+ *
75
+ * TX 2 : `writeEncodedVaa_2` + `verifyEncodedVaaV1`
76
+ */
77
+ async function buildWriteEncodedVaaWithSplitInstructions(wormhole, vaa, draftVaa) {
34
78
  return [
35
79
  {
36
80
  instruction: await wormhole.methods
@@ -58,4 +102,4 @@ async function buildWriteEncodedVaaWithSplit(wormhole, vaa, draftVaa) {
58
102
  },
59
103
  ];
60
104
  }
61
- exports.buildWriteEncodedVaaWithSplit = buildWriteEncodedVaaWithSplit;
105
+ exports.buildWriteEncodedVaaWithSplitInstructions = buildWriteEncodedVaaWithSplitInstructions;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pythnetwork/pyth-solana-receiver",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Pyth solana receiver SDK",
5
5
  "homepage": "https://pyth.network",
6
6
  "main": "lib/index.js",
@@ -47,5 +47,5 @@
47
47
  "@pythnetwork/solana-utils": "*",
48
48
  "@solana/web3.js": "^1.90.0"
49
49
  },
50
- "gitHead": "47470860bf517a151819a6c86a13c0a5f13aae22"
50
+ "gitHead": "854f804d1828361909012497688516d9d84660e6"
51
51
  }