feat(solana): js sdk (#1307)
* Do it * Remove some duplicate code * Cleanup * Cleanup * Cleanup import * Correct description * Fix path * Cleanup deps * Unique * Works * Continue * Lint * Lint config * Fix ci * Checkpoint * Checkpoint * Gitignore * Cleanup * Cleanup * Continue building the sdk * build function * Remove files * Remove files * Rename * Refactor : make transaction builder * Make commitment * Move * Progress * Checkpoint * Ephemeral signers 2 * Checkpoint * Checkpoint * Fix bug * Cleanup idls * Compute units * Make program addresses configurable * Handle arrays * Handle arrays * Move PythSolanaReceiver * Cleanup constants * Contants * Refactor constants * Gitignore refactor * package lock * Cleanup idl * Add useful static * Add useful static * Add useful static * Lint * Add lint config
This commit is contained in:
parent
38350421f6
commit
e986b69c9a
|
@ -32,4 +32,4 @@ jobs:
|
|||
- name: Run tests
|
||||
run: cargo-test-sbf
|
||||
- name: Run sdk tests
|
||||
run: cargo test --p pyth-solana-sdk
|
||||
run: cargo test --package pyth-solana-receiver-state
|
||||
|
|
|
@ -29,7 +29,8 @@
|
|||
"bigint-buffer": "^1.1.5",
|
||||
"ethers": "^5.7.2",
|
||||
"lodash": "^4.17.21",
|
||||
"typescript": "^4.9.4"
|
||||
"typescript": "^4.9.4",
|
||||
"@pythnetwork/solana-utils": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bn.js": "^5.1.1",
|
||||
|
|
|
@ -15,19 +15,13 @@ import {
|
|||
} from "@solana/web3.js";
|
||||
import {
|
||||
batchIntoExecutorPayload,
|
||||
batchIntoTransactions,
|
||||
getSizeOfCompressedU16,
|
||||
getSizeOfExecutorInstructions,
|
||||
getSizeOfTransaction,
|
||||
MAX_EXECUTOR_PAYLOAD_SIZE,
|
||||
} from "..";
|
||||
|
||||
it("Unit test compressed u16 size", async () => {
|
||||
expect(getSizeOfCompressedU16(127)).toBe(1);
|
||||
expect(getSizeOfCompressedU16(128)).toBe(2);
|
||||
expect(getSizeOfCompressedU16(16383)).toBe(2);
|
||||
expect(getSizeOfCompressedU16(16384)).toBe(3);
|
||||
});
|
||||
import {
|
||||
getSizeOfTransaction,
|
||||
TransactionBuilder,
|
||||
} from "@pythnetwork/solana-utils";
|
||||
|
||||
it("Unit test for getSizeOfTransaction", async () => {
|
||||
jest.setTimeout(60000);
|
||||
|
@ -84,7 +78,7 @@ it("Unit test for getSizeOfTransaction", async () => {
|
|||
transaction.recentBlockhash = "GqdFtdM7zzWw33YyHtBNwPhyBsdYKcfm9gT47bWnbHvs"; // Mock blockhash from devnet
|
||||
transaction.feePayer = payer.publicKey;
|
||||
expect(transaction.serialize({ requireAllSignatures: false }).length).toBe(
|
||||
getSizeOfTransaction(ixsToSend)
|
||||
getSizeOfTransaction(ixsToSend, false)
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -115,13 +109,14 @@ it("Unit test for getSizeOfTransaction", async () => {
|
|||
);
|
||||
}
|
||||
|
||||
const txToSend: Transaction[] = batchIntoTransactions(ixsToSend);
|
||||
const txToSend: Transaction[] =
|
||||
TransactionBuilder.batchIntoLegacyTransactions(ixsToSend);
|
||||
expect(
|
||||
txToSend.map((tx) => tx.instructions.length).reduce((a, b) => a + b)
|
||||
).toBe(ixsToSend.length);
|
||||
expect(
|
||||
txToSend.every(
|
||||
(tx) => getSizeOfTransaction(tx.instructions) <= PACKET_DATA_SIZE
|
||||
(tx) => getSizeOfTransaction(tx.instructions, false) <= PACKET_DATA_SIZE
|
||||
)
|
||||
).toBeTruthy();
|
||||
|
||||
|
@ -129,7 +124,7 @@ it("Unit test for getSizeOfTransaction", async () => {
|
|||
tx.recentBlockhash = "GqdFtdM7zzWw33YyHtBNwPhyBsdYKcfm9gT47bWnbHvs"; // Mock blockhash from devnet
|
||||
tx.feePayer = payer.publicKey;
|
||||
expect(tx.serialize({ requireAllSignatures: false }).length).toBe(
|
||||
getSizeOfTransaction(tx.instructions)
|
||||
getSizeOfTransaction(tx.instructions, false)
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import SquadsMesh, { getIxAuthorityPDA, getTxPDA } from "@sqds/mesh";
|
|||
import { MultisigAccount } from "@sqds/mesh/lib/types";
|
||||
import { mapKey } from "./remote_executor";
|
||||
import { WORMHOLE_ADDRESS } from "./wormhole";
|
||||
import { TransactionBuilder } from "@pythnetwork/solana-utils";
|
||||
|
||||
export const MAX_EXECUTOR_PAYLOAD_SIZE = PACKET_DATA_SIZE - 687; // Bigger payloads won't fit in one addInstruction call when adding to the proposal
|
||||
export const MAX_INSTRUCTIONS_PER_PROPOSAL = 256 - 1;
|
||||
|
@ -256,7 +257,7 @@ export class MultisigVault {
|
|||
ixToSend.push(await this.activateProposalIx(proposalAddress));
|
||||
ixToSend.push(await this.approveProposalIx(proposalAddress));
|
||||
|
||||
const txToSend = batchIntoTransactions(ixToSend);
|
||||
const txToSend = TransactionBuilder.batchIntoLegacyTransactions(ixToSend);
|
||||
await this.sendAllTransactions(txToSend);
|
||||
return proposalAddress;
|
||||
}
|
||||
|
@ -360,7 +361,7 @@ export class MultisigVault {
|
|||
}
|
||||
}
|
||||
|
||||
const txToSend = batchIntoTransactions(ixToSend);
|
||||
const txToSend = TransactionBuilder.batchIntoLegacyTransactions(ixToSend);
|
||||
|
||||
await this.sendAllTransactions(txToSend);
|
||||
return newProposals;
|
||||
|
@ -445,32 +446,6 @@ export function batchIntoExecutorPayload(
|
|||
return batches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch instructions into transactions
|
||||
*/
|
||||
export function batchIntoTransactions(
|
||||
instructions: TransactionInstruction[]
|
||||
): Transaction[] {
|
||||
let i = 0;
|
||||
const txToSend: Transaction[] = [];
|
||||
while (i < instructions.length) {
|
||||
let j = i + 2;
|
||||
while (
|
||||
j < instructions.length &&
|
||||
getSizeOfTransaction(instructions.slice(i, j)) <= PACKET_DATA_SIZE
|
||||
) {
|
||||
j += 1;
|
||||
}
|
||||
const tx = new Transaction();
|
||||
for (let k = i; k < j - 1; k += 1) {
|
||||
tx.add(instructions[k]);
|
||||
}
|
||||
i = j - 1;
|
||||
txToSend.push(tx);
|
||||
}
|
||||
return txToSend;
|
||||
}
|
||||
|
||||
/** Get the size of instructions when serialized as in a remote executor payload */
|
||||
export function getSizeOfExecutorInstructions(
|
||||
instructions: TransactionInstruction[]
|
||||
|
@ -481,54 +456,6 @@ export function getSizeOfExecutorInstructions(
|
|||
})
|
||||
.reduce((a, b) => a + b);
|
||||
}
|
||||
/**
|
||||
* Get the size of a transaction that would contain the provided array of instructions
|
||||
*/
|
||||
export function getSizeOfTransaction(
|
||||
instructions: TransactionInstruction[]
|
||||
): number {
|
||||
const signers = new Set<string>();
|
||||
const accounts = new Set<string>();
|
||||
|
||||
instructions.map((ix) => {
|
||||
accounts.add(ix.programId.toBase58()),
|
||||
ix.keys.map((key) => {
|
||||
if (key.isSigner) {
|
||||
signers.add(key.pubkey.toBase58());
|
||||
}
|
||||
accounts.add(key.pubkey.toBase58());
|
||||
});
|
||||
});
|
||||
|
||||
const instruction_sizes: number = instructions
|
||||
.map(
|
||||
(ix) =>
|
||||
1 +
|
||||
getSizeOfCompressedU16(ix.keys.length) +
|
||||
ix.keys.length +
|
||||
getSizeOfCompressedU16(ix.data.length) +
|
||||
ix.data.length
|
||||
)
|
||||
.reduce((a, b) => a + b, 0);
|
||||
|
||||
return (
|
||||
1 +
|
||||
signers.size * 64 +
|
||||
3 +
|
||||
getSizeOfCompressedU16(accounts.size) +
|
||||
32 * accounts.size +
|
||||
32 +
|
||||
getSizeOfCompressedU16(instructions.length) +
|
||||
instruction_sizes
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of n in bytes when serialized as a CompressedU16
|
||||
*/
|
||||
export function getSizeOfCompressedU16(n: number) {
|
||||
return 1 + Number(n >= 128) + Number(n >= 16384);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap `instruction` in a Wormhole message for remote execution
|
||||
|
|
|
@ -74,9 +74,11 @@ const usePyth = (): PythHookData => {
|
|||
connectionRef.current = connection
|
||||
;(async () => {
|
||||
try {
|
||||
const allPythAccounts = await connection.getProgramAccounts(
|
||||
getPythProgramKeyForCluster(cluster)
|
||||
)
|
||||
const allPythAccounts = [
|
||||
...(await connection.getProgramAccounts(
|
||||
getPythProgramKeyForCluster(cluster)
|
||||
)),
|
||||
]
|
||||
if (cancelled) return
|
||||
const priceRawConfigs: { [key: string]: PriceRawConfig } = {}
|
||||
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
"target_chains/ethereum/examples/oracle_swap/app",
|
||||
"target_chains/sui/sdk/js",
|
||||
"target_chains/sui/cli",
|
||||
"target_chains/solana/sdk/js/solana_utils",
|
||||
"target_chains/solana/sdk/js/pyth_solana_receiver",
|
||||
"contract_manager"
|
||||
],
|
||||
"dependencies": {
|
||||
|
@ -527,36 +529,6 @@
|
|||
"protobufjs": "~6.11.2"
|
||||
}
|
||||
},
|
||||
"contract_manager/node_modules/jayson": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jayson/-/jayson-4.1.0.tgz",
|
||||
"integrity": "sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==",
|
||||
"dependencies": {
|
||||
"@types/connect": "^3.4.33",
|
||||
"@types/node": "^12.12.54",
|
||||
"@types/ws": "^7.4.4",
|
||||
"commander": "^2.20.3",
|
||||
"delay": "^5.0.0",
|
||||
"es6-promisify": "^5.0.0",
|
||||
"eyes": "^0.1.8",
|
||||
"isomorphic-ws": "^4.0.1",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"JSONStream": "^1.3.5",
|
||||
"uuid": "^8.3.2",
|
||||
"ws": "^7.4.5"
|
||||
},
|
||||
"bin": {
|
||||
"jayson": "bin/jayson.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"contract_manager/node_modules/jayson/node_modules/@types/node": {
|
||||
"version": "12.20.55",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
|
||||
"integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="
|
||||
},
|
||||
"contract_manager/node_modules/protobufjs": {
|
||||
"version": "6.11.3",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz",
|
||||
|
@ -595,6 +567,7 @@
|
|||
"version": "7.5.9",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
|
||||
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
},
|
||||
|
@ -3397,6 +3370,7 @@
|
|||
"@certusone/wormhole-sdk": "^0.9.22",
|
||||
"@coral-xyz/anchor": "^0.26.0",
|
||||
"@pythnetwork/client": "^2.17.0",
|
||||
"@pythnetwork/solana-utils": "*",
|
||||
"@solana/buffer-layout": "^4.0.1",
|
||||
"@solana/web3.js": "^1.73.0",
|
||||
"@sqds/mesh": "^1.0.6",
|
||||
|
@ -3821,36 +3795,6 @@
|
|||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"governance/xc_admin/packages/xc_admin_common/node_modules/jayson": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jayson/-/jayson-4.1.0.tgz",
|
||||
"integrity": "sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==",
|
||||
"dependencies": {
|
||||
"@types/connect": "^3.4.33",
|
||||
"@types/node": "^12.12.54",
|
||||
"@types/ws": "^7.4.4",
|
||||
"commander": "^2.20.3",
|
||||
"delay": "^5.0.0",
|
||||
"es6-promisify": "^5.0.0",
|
||||
"eyes": "^0.1.8",
|
||||
"isomorphic-ws": "^4.0.1",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"JSONStream": "^1.3.5",
|
||||
"uuid": "^8.3.2",
|
||||
"ws": "^7.4.5"
|
||||
},
|
||||
"bin": {
|
||||
"jayson": "bin/jayson.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"governance/xc_admin/packages/xc_admin_common/node_modules/jayson/node_modules/@types/node": {
|
||||
"version": "12.20.55",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
|
||||
"integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="
|
||||
},
|
||||
"governance/xc_admin/packages/xc_admin_common/node_modules/prettier": {
|
||||
"version": "2.8.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz",
|
||||
|
@ -3920,6 +3864,7 @@
|
|||
"version": "7.5.9",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
|
||||
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
},
|
||||
|
@ -6471,16 +6416,21 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz",
|
||||
"integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==",
|
||||
"version": "7.23.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz",
|
||||
"integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.13.11"
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime/node_modules/regenerator-runtime": {
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.20.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.20.7.tgz",
|
||||
|
@ -13248,10 +13198,18 @@
|
|||
"resolved": "target_chains/ethereum/sdk/solidity",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@pythnetwork/pyth-solana-receiver": {
|
||||
"resolved": "target_chains/solana/sdk/js/pyth_solana_receiver",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@pythnetwork/pyth-sui-js": {
|
||||
"resolved": "target_chains/sui/sdk/js",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@pythnetwork/solana-utils": {
|
||||
"resolved": "target_chains/solana/sdk/js/solana_utils",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@radix-ui/primitive": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.0.tgz",
|
||||
|
@ -16274,37 +16232,48 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@solana/web3.js": {
|
||||
"version": "1.76.0",
|
||||
"resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.76.0.tgz",
|
||||
"integrity": "sha512-aJtF/nTs+9St+KtTK/wgVJ+SinfjYzn+3w1ygYIPw8ST6LH+qHBn8XkodgDTwlv/xzNkaVz1kkUDOZ8BPXyZWA==",
|
||||
"version": "1.90.0",
|
||||
"resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.90.0.tgz",
|
||||
"integrity": "sha512-p0cb/COXb8NNVSMkGMPwqQ6NvObZgUitN80uOedMB+jbYWOKOeJBuPnzhenkIV9RX0krGwyuY1Ltn5O8MGFsEw==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@noble/curves": "^1.0.0",
|
||||
"@noble/hashes": "^1.3.0",
|
||||
"@solana/buffer-layout": "^4.0.0",
|
||||
"agentkeepalive": "^4.2.1",
|
||||
"@babel/runtime": "^7.23.4",
|
||||
"@noble/curves": "^1.2.0",
|
||||
"@noble/hashes": "^1.3.2",
|
||||
"@solana/buffer-layout": "^4.0.1",
|
||||
"agentkeepalive": "^4.5.0",
|
||||
"bigint-buffer": "^1.1.5",
|
||||
"bn.js": "^5.0.0",
|
||||
"bn.js": "^5.2.1",
|
||||
"borsh": "^0.7.0",
|
||||
"bs58": "^4.0.1",
|
||||
"buffer": "6.0.3",
|
||||
"fast-stable-stringify": "^1.0.0",
|
||||
"jayson": "^3.4.4",
|
||||
"node-fetch": "^2.6.7",
|
||||
"jayson": "^4.1.0",
|
||||
"node-fetch": "^2.7.0",
|
||||
"rpc-websockets": "^7.5.1",
|
||||
"superstruct": "^0.14.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@solana/web3.js/node_modules/@noble/hashes": {
|
||||
"node_modules/@solana/web3.js/node_modules/@noble/curves": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz",
|
||||
"integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
]
|
||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz",
|
||||
"integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==",
|
||||
"dependencies": {
|
||||
"@noble/hashes": "1.3.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@solana/web3.js/node_modules/@noble/hashes": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz",
|
||||
"integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"node_modules/@solana/web3.js/node_modules/buffer": {
|
||||
"version": "6.0.3",
|
||||
|
@ -22006,12 +21975,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/agentkeepalive": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz",
|
||||
"integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==",
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz",
|
||||
"integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==",
|
||||
"dependencies": {
|
||||
"debug": "^4.1.0",
|
||||
"depd": "^1.1.2",
|
||||
"humanize-ms": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -34442,9 +34409,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/jayson": {
|
||||
"version": "3.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jayson/-/jayson-3.7.0.tgz",
|
||||
"integrity": "sha512-tfy39KJMrrXJ+mFcMpxwBvFDetS8LAID93+rycFglIQM4kl3uNR3W4lBLE/FFhsoUCEox5Dt2adVpDm/XtebbQ==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jayson/-/jayson-4.1.0.tgz",
|
||||
"integrity": "sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==",
|
||||
"dependencies": {
|
||||
"@types/connect": "^3.4.33",
|
||||
"@types/node": "^12.12.54",
|
||||
|
@ -34456,7 +34423,6 @@
|
|||
"isomorphic-ws": "^4.0.1",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"JSONStream": "^1.3.5",
|
||||
"lodash": "^4.17.20",
|
||||
"uuid": "^8.3.2",
|
||||
"ws": "^7.4.5"
|
||||
},
|
||||
|
@ -58819,41 +58785,6 @@
|
|||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"target_chains/ethereum/contracts/node_modules/jayson": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jayson/-/jayson-4.1.0.tgz",
|
||||
"integrity": "sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==",
|
||||
"dependencies": {
|
||||
"@types/connect": "^3.4.33",
|
||||
"@types/node": "^12.12.54",
|
||||
"@types/ws": "^7.4.4",
|
||||
"commander": "^2.20.3",
|
||||
"delay": "^5.0.0",
|
||||
"es6-promisify": "^5.0.0",
|
||||
"eyes": "^0.1.8",
|
||||
"isomorphic-ws": "^4.0.1",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"JSONStream": "^1.3.5",
|
||||
"uuid": "^8.3.2",
|
||||
"ws": "^7.4.5"
|
||||
},
|
||||
"bin": {
|
||||
"jayson": "bin/jayson.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"target_chains/ethereum/contracts/node_modules/jayson/node_modules/@types/node": {
|
||||
"version": "12.20.55",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
|
||||
"integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="
|
||||
},
|
||||
"target_chains/ethereum/contracts/node_modules/jayson/node_modules/commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
|
||||
},
|
||||
"target_chains/ethereum/contracts/node_modules/jsonfile": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||
|
@ -58949,6 +58880,7 @@
|
|||
"version": "7.5.9",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
|
||||
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
},
|
||||
|
@ -59598,6 +59530,107 @@
|
|||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"target_chains/solana/sdk/js/pyth_solana_receiver": {
|
||||
"version": "0.1.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@coral-xyz/anchor": "^0.29.0",
|
||||
"@pythnetwork/price-service-sdk": "*",
|
||||
"@pythnetwork/solana-utils": "*",
|
||||
"@solana/web3.js": "^1.90.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.4.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.20.0",
|
||||
"@typescript-eslint/parser": "^5.20.0",
|
||||
"eslint": "^8.13.0",
|
||||
"jest": "^29.4.0",
|
||||
"prettier": "^2.6.2",
|
||||
"quicktype": "^23.0.76",
|
||||
"ts-jest": "^29.0.5",
|
||||
"typescript": "^4.6.3"
|
||||
}
|
||||
},
|
||||
"target_chains/solana/sdk/js/pyth_solana_receiver/node_modules/@coral-xyz/anchor": {
|
||||
"version": "0.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@coral-xyz/anchor/-/anchor-0.29.0.tgz",
|
||||
"integrity": "sha512-eny6QNG0WOwqV0zQ7cs/b1tIuzZGmP7U7EcH+ogt4Gdbl8HDmIYVMh/9aTmYZPaFWjtUaI8qSn73uYEXWfATdA==",
|
||||
"dependencies": {
|
||||
"@coral-xyz/borsh": "^0.29.0",
|
||||
"@noble/hashes": "^1.3.1",
|
||||
"@solana/web3.js": "^1.68.0",
|
||||
"bn.js": "^5.1.2",
|
||||
"bs58": "^4.0.1",
|
||||
"buffer-layout": "^1.2.2",
|
||||
"camelcase": "^6.3.0",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"crypto-hash": "^1.3.0",
|
||||
"eventemitter3": "^4.0.7",
|
||||
"pako": "^2.0.3",
|
||||
"snake-case": "^3.0.4",
|
||||
"superstruct": "^0.15.4",
|
||||
"toml": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=11"
|
||||
}
|
||||
},
|
||||
"target_chains/solana/sdk/js/pyth_solana_receiver/node_modules/@coral-xyz/borsh": {
|
||||
"version": "0.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.29.0.tgz",
|
||||
"integrity": "sha512-s7VFVa3a0oqpkuRloWVPdCK7hMbAMY270geZOGfCnaqexrP5dTIpbEHL33req6IYPPJ0hYa71cdvJ1h6V55/oQ==",
|
||||
"dependencies": {
|
||||
"bn.js": "^5.1.2",
|
||||
"buffer-layout": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@solana/web3.js": "^1.68.0"
|
||||
}
|
||||
},
|
||||
"target_chains/solana/sdk/js/pyth_solana_receiver/node_modules/@noble/hashes": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz",
|
||||
"integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==",
|
||||
"engines": {
|
||||
"node": ">= 16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://paulmillr.com/funding/"
|
||||
}
|
||||
},
|
||||
"target_chains/solana/sdk/js/pyth_solana_receiver/node_modules/camelcase": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
|
||||
"integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"target_chains/solana/sdk/js/solana_utils": {
|
||||
"name": "@pythnetwork/solana-utils",
|
||||
"version": "0.1.0",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@solana/web3.js": "^1.90.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.4.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.20.0",
|
||||
"@typescript-eslint/parser": "^5.20.0",
|
||||
"eslint": "^8.13.0",
|
||||
"jest": "^29.4.0",
|
||||
"prettier": "^2.6.2",
|
||||
"quicktype": "^23.0.76",
|
||||
"ts-jest": "^29.0.5",
|
||||
"typescript": "^4.6.3"
|
||||
}
|
||||
},
|
||||
"target_chains/sui/cli": {
|
||||
"name": "pyth-sui-cli",
|
||||
"version": "0.0.1",
|
||||
|
@ -60047,36 +60080,6 @@
|
|||
"node-fetch": "^2.6.12"
|
||||
}
|
||||
},
|
||||
"target_chains/sui/cli/node_modules/jayson": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jayson/-/jayson-4.1.0.tgz",
|
||||
"integrity": "sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==",
|
||||
"dependencies": {
|
||||
"@types/connect": "^3.4.33",
|
||||
"@types/node": "^12.12.54",
|
||||
"@types/ws": "^7.4.4",
|
||||
"commander": "^2.20.3",
|
||||
"delay": "^5.0.0",
|
||||
"es6-promisify": "^5.0.0",
|
||||
"eyes": "^0.1.8",
|
||||
"isomorphic-ws": "^4.0.1",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"JSONStream": "^1.3.5",
|
||||
"uuid": "^8.3.2",
|
||||
"ws": "^7.4.5"
|
||||
},
|
||||
"bin": {
|
||||
"jayson": "bin/jayson.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"target_chains/sui/cli/node_modules/jayson/node_modules/@types/node": {
|
||||
"version": "12.20.55",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
|
||||
"integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="
|
||||
},
|
||||
"target_chains/sui/cli/node_modules/prettier": {
|
||||
"version": "2.8.8",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
|
||||
|
@ -60141,6 +60144,7 @@
|
|||
"version": "7.5.9",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
|
||||
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
|
||||
"optional": true,
|
||||
"engines": {
|
||||
"node": ">=8.3.0"
|
||||
},
|
||||
|
@ -61783,11 +61787,18 @@
|
|||
}
|
||||
},
|
||||
"@babel/runtime": {
|
||||
"version": "7.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz",
|
||||
"integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==",
|
||||
"version": "7.23.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz",
|
||||
"integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.11"
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"regenerator-runtime": {
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/template": {
|
||||
|
@ -70538,37 +70549,6 @@
|
|||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"jayson": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jayson/-/jayson-4.1.0.tgz",
|
||||
"integrity": "sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==",
|
||||
"requires": {
|
||||
"@types/connect": "^3.4.33",
|
||||
"@types/node": "^12.12.54",
|
||||
"@types/ws": "^7.4.4",
|
||||
"commander": "^2.20.3",
|
||||
"delay": "^5.0.0",
|
||||
"es6-promisify": "^5.0.0",
|
||||
"eyes": "^0.1.8",
|
||||
"isomorphic-ws": "^4.0.1",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"JSONStream": "^1.3.5",
|
||||
"uuid": "^8.3.2",
|
||||
"ws": "^7.4.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "12.20.55",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
|
||||
"integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="
|
||||
},
|
||||
"commander": {
|
||||
"version": "2.20.3",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
|
||||
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"jsonfile": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
|
||||
|
@ -70641,6 +70621,7 @@
|
|||
"version": "7.5.9",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
|
||||
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
|
||||
"optional": true,
|
||||
"requires": {}
|
||||
}
|
||||
}
|
||||
|
@ -71515,6 +71496,66 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"@pythnetwork/pyth-solana-receiver": {
|
||||
"version": "file:target_chains/solana/sdk/js/pyth_solana_receiver",
|
||||
"requires": {
|
||||
"@coral-xyz/anchor": "^0.29.0",
|
||||
"@pythnetwork/price-service-sdk": "*",
|
||||
"@pythnetwork/solana-utils": "*",
|
||||
"@solana/web3.js": "^1.90.0",
|
||||
"@types/jest": "^29.4.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.20.0",
|
||||
"@typescript-eslint/parser": "^5.20.0",
|
||||
"eslint": "^8.13.0",
|
||||
"jest": "^29.4.0",
|
||||
"prettier": "^2.6.2",
|
||||
"quicktype": "^23.0.76",
|
||||
"ts-jest": "^29.0.5",
|
||||
"typescript": "^4.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@coral-xyz/anchor": {
|
||||
"version": "0.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@coral-xyz/anchor/-/anchor-0.29.0.tgz",
|
||||
"integrity": "sha512-eny6QNG0WOwqV0zQ7cs/b1tIuzZGmP7U7EcH+ogt4Gdbl8HDmIYVMh/9aTmYZPaFWjtUaI8qSn73uYEXWfATdA==",
|
||||
"requires": {
|
||||
"@coral-xyz/borsh": "^0.29.0",
|
||||
"@noble/hashes": "^1.3.1",
|
||||
"@solana/web3.js": "^1.68.0",
|
||||
"bn.js": "^5.1.2",
|
||||
"bs58": "^4.0.1",
|
||||
"buffer-layout": "^1.2.2",
|
||||
"camelcase": "^6.3.0",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"crypto-hash": "^1.3.0",
|
||||
"eventemitter3": "^4.0.7",
|
||||
"pako": "^2.0.3",
|
||||
"snake-case": "^3.0.4",
|
||||
"superstruct": "^0.15.4",
|
||||
"toml": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"@coral-xyz/borsh": {
|
||||
"version": "0.29.0",
|
||||
"resolved": "https://registry.npmjs.org/@coral-xyz/borsh/-/borsh-0.29.0.tgz",
|
||||
"integrity": "sha512-s7VFVa3a0oqpkuRloWVPdCK7hMbAMY270geZOGfCnaqexrP5dTIpbEHL33req6IYPPJ0hYa71cdvJ1h6V55/oQ==",
|
||||
"requires": {
|
||||
"bn.js": "^5.1.2",
|
||||
"buffer-layout": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"@noble/hashes": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz",
|
||||
"integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA=="
|
||||
},
|
||||
"camelcase": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
|
||||
"integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@pythnetwork/pyth-sui-js": {
|
||||
"version": "file:target_chains/sui/sdk/js",
|
||||
"requires": {
|
||||
|
@ -71581,6 +71622,21 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"@pythnetwork/solana-utils": {
|
||||
"version": "file:target_chains/solana/sdk/js/solana_utils",
|
||||
"requires": {
|
||||
"@solana/web3.js": "^1.90.0",
|
||||
"@types/jest": "^29.4.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.20.0",
|
||||
"@typescript-eslint/parser": "^5.20.0",
|
||||
"eslint": "^8.13.0",
|
||||
"jest": "^29.4.0",
|
||||
"prettier": "^2.6.2",
|
||||
"quicktype": "^23.0.76",
|
||||
"ts-jest": "^29.0.5",
|
||||
"typescript": "^4.6.3"
|
||||
}
|
||||
},
|
||||
"@radix-ui/primitive": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.0.0.tgz",
|
||||
|
@ -73818,31 +73874,39 @@
|
|||
}
|
||||
},
|
||||
"@solana/web3.js": {
|
||||
"version": "1.76.0",
|
||||
"resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.76.0.tgz",
|
||||
"integrity": "sha512-aJtF/nTs+9St+KtTK/wgVJ+SinfjYzn+3w1ygYIPw8ST6LH+qHBn8XkodgDTwlv/xzNkaVz1kkUDOZ8BPXyZWA==",
|
||||
"version": "1.90.0",
|
||||
"resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.90.0.tgz",
|
||||
"integrity": "sha512-p0cb/COXb8NNVSMkGMPwqQ6NvObZgUitN80uOedMB+jbYWOKOeJBuPnzhenkIV9RX0krGwyuY1Ltn5O8MGFsEw==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"@noble/curves": "^1.0.0",
|
||||
"@noble/hashes": "^1.3.0",
|
||||
"@solana/buffer-layout": "^4.0.0",
|
||||
"agentkeepalive": "^4.2.1",
|
||||
"@babel/runtime": "^7.23.4",
|
||||
"@noble/curves": "^1.2.0",
|
||||
"@noble/hashes": "^1.3.2",
|
||||
"@solana/buffer-layout": "^4.0.1",
|
||||
"agentkeepalive": "^4.5.0",
|
||||
"bigint-buffer": "^1.1.5",
|
||||
"bn.js": "^5.0.0",
|
||||
"bn.js": "^5.2.1",
|
||||
"borsh": "^0.7.0",
|
||||
"bs58": "^4.0.1",
|
||||
"buffer": "6.0.3",
|
||||
"fast-stable-stringify": "^1.0.0",
|
||||
"jayson": "^3.4.4",
|
||||
"node-fetch": "^2.6.7",
|
||||
"jayson": "^4.1.0",
|
||||
"node-fetch": "^2.7.0",
|
||||
"rpc-websockets": "^7.5.1",
|
||||
"superstruct": "^0.14.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@noble/hashes": {
|
||||
"@noble/curves": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.0.tgz",
|
||||
"integrity": "sha512-ilHEACi9DwqJB0pw7kv+Apvh50jiiSyR/cQ3y4W7lOR5mhvn/50FLUfsnfJz0BDZtl/RR16kXvptiv6q1msYZg=="
|
||||
"resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.3.0.tgz",
|
||||
"integrity": "sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==",
|
||||
"requires": {
|
||||
"@noble/hashes": "1.3.3"
|
||||
}
|
||||
},
|
||||
"@noble/hashes": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.3.tgz",
|
||||
"integrity": "sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA=="
|
||||
},
|
||||
"buffer": {
|
||||
"version": "6.0.3",
|
||||
|
@ -78597,12 +78661,10 @@
|
|||
}
|
||||
},
|
||||
"agentkeepalive": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz",
|
||||
"integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==",
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz",
|
||||
"integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==",
|
||||
"requires": {
|
||||
"debug": "^4.1.0",
|
||||
"depd": "^1.1.2",
|
||||
"humanize-ms": "^1.2.1"
|
||||
}
|
||||
},
|
||||
|
@ -81883,32 +81945,6 @@
|
|||
"protobufjs": "~6.11.2"
|
||||
}
|
||||
},
|
||||
"jayson": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jayson/-/jayson-4.1.0.tgz",
|
||||
"integrity": "sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==",
|
||||
"requires": {
|
||||
"@types/connect": "^3.4.33",
|
||||
"@types/node": "^12.12.54",
|
||||
"@types/ws": "^7.4.4",
|
||||
"commander": "^2.20.3",
|
||||
"delay": "^5.0.0",
|
||||
"es6-promisify": "^5.0.0",
|
||||
"eyes": "^0.1.8",
|
||||
"isomorphic-ws": "^4.0.1",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"JSONStream": "^1.3.5",
|
||||
"uuid": "^8.3.2",
|
||||
"ws": "^7.4.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "12.20.55",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
|
||||
"integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"protobufjs": {
|
||||
"version": "6.11.3",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.3.tgz",
|
||||
|
@ -81939,6 +81975,7 @@
|
|||
"version": "7.5.9",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
|
||||
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
|
||||
"optional": true,
|
||||
"requires": {}
|
||||
}
|
||||
}
|
||||
|
@ -88900,9 +88937,9 @@
|
|||
}
|
||||
},
|
||||
"jayson": {
|
||||
"version": "3.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jayson/-/jayson-3.7.0.tgz",
|
||||
"integrity": "sha512-tfy39KJMrrXJ+mFcMpxwBvFDetS8LAID93+rycFglIQM4kl3uNR3W4lBLE/FFhsoUCEox5Dt2adVpDm/XtebbQ==",
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jayson/-/jayson-4.1.0.tgz",
|
||||
"integrity": "sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==",
|
||||
"requires": {
|
||||
"@types/connect": "^3.4.33",
|
||||
"@types/node": "^12.12.54",
|
||||
|
@ -88914,7 +88951,6 @@
|
|||
"isomorphic-ws": "^4.0.1",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"JSONStream": "^1.3.5",
|
||||
"lodash": "^4.17.20",
|
||||
"uuid": "^8.3.2",
|
||||
"ws": "^7.4.5"
|
||||
},
|
||||
|
@ -97094,32 +97130,6 @@
|
|||
"node-fetch": "^2.6.12"
|
||||
}
|
||||
},
|
||||
"jayson": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jayson/-/jayson-4.1.0.tgz",
|
||||
"integrity": "sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==",
|
||||
"requires": {
|
||||
"@types/connect": "^3.4.33",
|
||||
"@types/node": "^12.12.54",
|
||||
"@types/ws": "^7.4.4",
|
||||
"commander": "^2.20.3",
|
||||
"delay": "^5.0.0",
|
||||
"es6-promisify": "^5.0.0",
|
||||
"eyes": "^0.1.8",
|
||||
"isomorphic-ws": "^4.0.1",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"JSONStream": "^1.3.5",
|
||||
"uuid": "^8.3.2",
|
||||
"ws": "^7.4.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "12.20.55",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
|
||||
"integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"prettier": {
|
||||
"version": "2.8.8",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
|
||||
|
@ -97160,6 +97170,7 @@
|
|||
"version": "7.5.9",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
|
||||
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
|
||||
"optional": true,
|
||||
"requires": {}
|
||||
}
|
||||
}
|
||||
|
@ -105908,6 +105919,7 @@
|
|||
"@certusone/wormhole-sdk": "^0.9.22",
|
||||
"@coral-xyz/anchor": "^0.26.0",
|
||||
"@pythnetwork/client": "^2.17.0",
|
||||
"@pythnetwork/solana-utils": "*",
|
||||
"@solana/buffer-layout": "^4.0.1",
|
||||
"@solana/web3.js": "^1.73.0",
|
||||
"@sqds/mesh": "^1.0.6",
|
||||
|
@ -106298,32 +106310,6 @@
|
|||
"pure-rand": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"jayson": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jayson/-/jayson-4.1.0.tgz",
|
||||
"integrity": "sha512-R6JlbyLN53Mjku329XoRT2zJAE6ZgOQ8f91ucYdMCD4nkGCF9kZSrcGXpHIU4jeKj58zUZke2p+cdQchU7Ly7A==",
|
||||
"requires": {
|
||||
"@types/connect": "^3.4.33",
|
||||
"@types/node": "^12.12.54",
|
||||
"@types/ws": "^7.4.4",
|
||||
"commander": "^2.20.3",
|
||||
"delay": "^5.0.0",
|
||||
"es6-promisify": "^5.0.0",
|
||||
"eyes": "^0.1.8",
|
||||
"isomorphic-ws": "^4.0.1",
|
||||
"json-stringify-safe": "^5.0.1",
|
||||
"JSONStream": "^1.3.5",
|
||||
"uuid": "^8.3.2",
|
||||
"ws": "^7.4.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/node": {
|
||||
"version": "12.20.55",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz",
|
||||
"integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"prettier": {
|
||||
"version": "2.8.3",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.3.tgz",
|
||||
|
@ -106366,6 +106352,7 @@
|
|||
"version": "7.5.9",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz",
|
||||
"integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
|
||||
"optional": true,
|
||||
"requires": {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
"target_chains/ethereum/examples/oracle_swap/app",
|
||||
"target_chains/sui/sdk/js",
|
||||
"target_chains/sui/cli",
|
||||
"target_chains/solana/sdk/js/solana_utils",
|
||||
"target_chains/solana/sdk/js/pyth_solana_receiver",
|
||||
"contract_manager"
|
||||
],
|
||||
"dependencies": {
|
||||
|
|
|
@ -1,11 +1,25 @@
|
|||
import BN from "bn.js";
|
||||
|
||||
const ACCUMULATOR_MAGIC = "504e4155";
|
||||
const MAJOR_VERSION = 1;
|
||||
const MINOR_VERSION = 0;
|
||||
const KECCAK160_HASH_SIZE = 20;
|
||||
const PRICE_FEED_MESSAGE_VARIANT = 0;
|
||||
|
||||
export type AccumulatorUpdateData = {
|
||||
vaa: Buffer;
|
||||
updates: { message: Buffer; proof: Buffer[] }[];
|
||||
updates: { message: Buffer; proof: number[][] }[];
|
||||
};
|
||||
|
||||
export type PriceFeedMessage = {
|
||||
feedId: Buffer;
|
||||
price: BN;
|
||||
confidence: BN;
|
||||
exponent: number;
|
||||
publishTime: BN;
|
||||
prevPublishTime: BN;
|
||||
emaPrice: BN;
|
||||
emaConf: BN;
|
||||
};
|
||||
|
||||
export function isAccumulatorUpdateData(updateBytes: Buffer): boolean {
|
||||
|
@ -15,6 +29,40 @@ export function isAccumulatorUpdateData(updateBytes: Buffer): boolean {
|
|||
updateBytes[5] === MINOR_VERSION
|
||||
);
|
||||
}
|
||||
export function parsePriceFeedMessage(message: Buffer): PriceFeedMessage {
|
||||
let cursor = 0;
|
||||
const variant = message.readUInt8(cursor);
|
||||
if (variant !== PRICE_FEED_MESSAGE_VARIANT) {
|
||||
throw new Error("Not a price feed message");
|
||||
}
|
||||
cursor += 1;
|
||||
const feedId = message.subarray(cursor, cursor + 32);
|
||||
cursor += 32;
|
||||
const price = new BN(message.subarray(cursor, cursor + 8), "be");
|
||||
cursor += 8;
|
||||
const confidence = new BN(message.subarray(cursor, cursor + 8), "be");
|
||||
cursor += 8;
|
||||
const exponent = message.readInt32BE(cursor);
|
||||
cursor += 4;
|
||||
const publishTime = new BN(message.subarray(cursor, cursor + 8), "be");
|
||||
cursor += 8;
|
||||
const prevPublishTime = new BN(message.subarray(cursor, cursor + 8), "be");
|
||||
cursor += 8;
|
||||
const emaPrice = new BN(message.subarray(cursor, cursor + 8), "be");
|
||||
cursor += 8;
|
||||
const emaConf = new BN(message.subarray(cursor, cursor + 8), "be");
|
||||
cursor += 8;
|
||||
return {
|
||||
feedId,
|
||||
price,
|
||||
confidence,
|
||||
exponent,
|
||||
publishTime,
|
||||
prevPublishTime,
|
||||
emaPrice,
|
||||
emaConf,
|
||||
};
|
||||
}
|
||||
|
||||
export function parseAccumulatorUpdateData(
|
||||
data: Buffer
|
||||
|
@ -50,7 +98,9 @@ export function parseAccumulatorUpdateData(
|
|||
cursor += 1;
|
||||
const proof = [];
|
||||
for (let j = 0; j < numProofs; j++) {
|
||||
proof.push(data.subarray(cursor, cursor + KECCAK160_HASH_SIZE));
|
||||
proof.push(
|
||||
Array.from(data.subarray(cursor, cursor + KECCAK160_HASH_SIZE))
|
||||
);
|
||||
cursor += KECCAK160_HASH_SIZE;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,28 @@
|
|||
import { parseAccumulatorUpdateData } from "../AccumulatorUpdateData";
|
||||
import {
|
||||
parseAccumulatorUpdateData,
|
||||
parsePriceFeedMessage,
|
||||
} from "../AccumulatorUpdateData";
|
||||
|
||||
// This is just a sample update data from hermes
|
||||
const TEST_ACCUMULATOR_UPDATE_DATA =
|
||||
"UE5BVQEAAAADuAEAAAADDQCRWcud7VE0FQkptV7iZh1Ls8O4dszQ4HmdOZLNDVQiQnMB5Q9jVd52Y9IMI8k1QhOhT2xn82hveqZ6AIn+c6vPAQL4nN+ynbzaJnbGFpWW5ysEA811BblKr+DXO5I5tD3SgEjPmByPIEVPHRqdgnq7M8r6AG4q8qfbmeonROr67i4eAAPVteKrUXD6f13GG/Qj0xHcJ/NuR+xwrbs6KGmYZHq0fHv4m0C3LPIOgVo9iy2ednK5IB/pAEMoaGK/fwoL2ouSAQSulF0XQGZ0J5oFKvCwPBSZOYtITwEQSicnwIWu9a+j7SjMh/zF4vtqWFAqfkFatVMZI6/dkQkmwlcMkEkGHvN5AQZYiYD8teZVpmCzn9jxZo/qTF4qrWgrHWv3/i4kZsXmkDSq1QTiYd7ikQQVWVxgH3PKl03SPFvqoc7SmwKIZKyyAQjfPTwpqeTTi0zFRyyb9HKMYjcbXEcuRXn7uOaNF83ry1s+cudCcWsiaCNYEPzv1BvHxgYYXcx2MkNxUbXiLlmoAQpQSpOkNb9780k2EsrUjZd/ieD+sTQA6P0iZWL5jA8ONEi46mAufCfRlAO2a5jfUvjuN4Z/ZOklgT9eZ7v3JoleAAv/2wkZ5rQx+cl/jlL9k6rbzrDU8sYLTJnlFTsuOr66/iVUqCe0Clwv682NgvH8yLbtw9He/vdn3OeLn19eDU0qAQxk47DIhc9EAtNrdhFSyAoEBtQtgcxRvSnjIIMPTGIhIzv52WFY/I2CwyKcQLhERdjjfh7EhZvBUXHTFRk2xjc2AA3wNaGbjUXsJqL8VyBQg7t0dILbUQ8AiOZJQVfx+L+1mFVZAc4v8/0BWsIF5b7+YmoN6psArWCvZcd9Hkjuxda4AQ5Rxgs32U2Jm43W4voTk42MibgvPMas3xQbuCW88pH1skdSTfvtgoIOa6BdoS3YEUJu78a0X3AiIUem1fDOdOs7AA/lHyqNz4vwuTNs8U6G51VqO2g1yEJyRwrMqsjEvK9VC0EjieacqPBwPL9/DMssbHU01bL+YzEY5XTxi1QiBeyFABJuE+6jHgEh9WvwaPDZe7me9sl5EiPDUxAAryErsB0LDTrnzls7qgDymCp+MSJur8U4I08ul/mL1rVesK3uUqqtAGV20Z0AAAAAABrhAfrtrFhR4yubI7X5QRqMK6xKrj7U3XuBHdGnLqSqcQAAAAAB1IYyAUFVV1YAAAAAAAbbm1gAACcQralfDHbB9c321F6ngWz+RspcmdsBAFUAyWRY05P+net6fWOgrEHiiYpnp3UNvRZmcyeeBsho3woAAAAAAFEIJAAAAAAAAAxT////+AAAAABldtGcAAAAAGV20ZsAAAAAAFEWJQAAAAAAAAu6CqMvc3++cZquwewCu6kJe8aPB1SFPI41uwi10MgNwqRbCue30EvorUjF4mKpFB+Cwx8KH5bFnAAX13DPmu7OCbX7k0LdKtr9pb8zPVsXwlx+BteFyBWNtJmeLIx7tG88H2uARL/B+MJw2GcVujs6qdnIQkIjjBdDIR3XRtY2zMfK58eeXuiAkJDHIQ3H41GmYRAVe8FtPvtMWTY51Q63Tkmfq60qsB1yy4Srd5QI/x60eBnOlAYC67+gjB0sGHLrjSapbXzGUf//";
|
||||
|
||||
"UE5BVQEAAAADuAEAAAADDQILBDsleD7xENCN7O7KjXAe15OQQ8FvD0E6L+bIemppBjNEZEOF0XuxCPQ/ti4gWUplKpieCWYiibuaDvXOeSFFAQOuYekUPV6yqK2BRWo/KRmJ00SV01OJDRhNzc1YnzUi+j8dt1tNBkVY99NIs5leyixUmeq4Dn1n8y37xN4JtAacAQSxJzGeZ6tejBD1YlPBlVwaCX8crkp19R1es7rDBhX/iit2plz5grz66fPj/mpoffZqKo95Fq/0sxWHIvn4nhgXAQYLl99cpa6KlaA8q1Pj7sN6TXNrXtmTBlzRU6dZ0ptO8VKp4K3zkVqbWkB5mbHCeuYNgOGMCnVsS7Ce9J7NganNAQf0nyez/5yR/U2zu+XRbi8eNzI1yJ9Hc4lmMl8pTPPQRgrs9HyiVCliCOcHdLzLio3JoLBhmFxQ3ygYj2eB+k3UAQgHX1e/+vbCjBNnmx/UQV8m0y/wifKAMfYpK4mR8voG3wgxo5MIFUvvCZ9/Gt1GizTX5CuoQD9J4ioxjoCFghVtAQqG5lFSpVRpC0dQlMv2ju2K89Ph0tJGsX7LGRXRnh9lEkkM8W+Uxf1R50HFsZHiXU08Grz0mKRPavesrzD+1xYGAQuYL6q5SagvBS7TfZJYS4kUMw74TvMiHLWx2ps3EdEJbh3WCWGfOM3amrplQBnqctDYh3StqspyTdaU5QTxfyYvAQwNWdPBEtAR6yIHB8KYrEDGGUH91uqD768NGigW6ziLwnNw2un+gcDUiafL3pZpqC4yIDhmnEz26PmQs4cAI5nkAA3/Zl3Pt7fLG3E5xBa/lbdrBUT3J+znFExbuFZuZipvbBwnQq/yyBSXqyfuHG3GTQZ/wXBto5zUEyex9889XYzaAQ52EUUCG0X4i0nWHeAf00+s6cODkW6hanQ1MHfTdvvVMXqK9nfvicz8pBna/NVp1wiTN5zR9rWjQuAf0g0c6TRLARCPT46a0/3xER/tV7WLQ6JQUWHMbV0G6cXKmdFT0Qg3/m08Dlabic+EHW9u2ugZA5sJ/Jl4oGk/lWLJoNoxDFMZARJgsgN2RdhjvJMRmf/Kj0d5PSvI7kE+J7ShlJd5058+ETZVPR15fJpT3BWUJ8i/vdGmU90A6iGyXRmNRBFx21qqAGXeGOEAAAAAABrhAfrtrFhR4yubI7X5QRqMK6xKrj7U3XuBHdGnLqSqcQAAAAACjFEsAUFVV1YAAAAAAAeUpesAACcQy5naBQ5EEmAnr2RvKBD1SUJH9zwBAFUA7w2Lb9os66QdoV1AldHaOSoNL47Qxse8D0z6yMKAtW0AAAACgAWPuQAAAAAAX+C0////+AAAAABl3hjhAAAAAGXeGOEAAAACgrM/OAAAAAAAaAvYCuE9uKnM0BlXJ1E/fLYtWytcVLhkSCzHW9p1ReYbPMt07qbcN5KykfYDlCJxjBT3UmyTbhTT30PmOLkZu9zraLg22Wysdg1W67WoQZi654djZPBpAiHRU2KQXbDSGqJcekD0W+TqKO+QagPAoXksP0iMGEYpBdVZKGhSvw0NpXv/5Qb5NHO/+dTahPFBgXJH+9geJaxYru9ZRMg5o+YjIvkwuom/2NP0mTbfp4syeDQBs+fmcdGmAjgcdF0wt1gR6kYbsWQ/CZ08";
|
||||
describe("Test parse accumulator update", () => {
|
||||
test("Happy path", async () => {
|
||||
parseAccumulatorUpdateData(
|
||||
const { vaa, updates } = parseAccumulatorUpdateData(
|
||||
Buffer.from(TEST_ACCUMULATOR_UPDATE_DATA, "base64")
|
||||
);
|
||||
|
||||
const priceMessage = parsePriceFeedMessage(updates[0].message);
|
||||
expect(priceMessage.feedId.toString("hex")).toBe(
|
||||
"ef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d"
|
||||
);
|
||||
expect(priceMessage.price.toString()).toBe("10737782713");
|
||||
expect(priceMessage.confidence.toString()).toBe("6283444");
|
||||
expect(priceMessage.exponent).toBe(-8);
|
||||
expect(priceMessage.publishTime.toString()).toBe("1709054177");
|
||||
expect(priceMessage.prevPublishTime.toString()).toBe("1709054177");
|
||||
expect(priceMessage.emaPrice.toString()).toBe("10782719800");
|
||||
expect(priceMessage.emaConf.toString()).toBe("6818776");
|
||||
});
|
||||
|
||||
test("Wrong magic number", async () => {
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import { isAccumulatorUpdateData } from "./AccumulatorUpdateData";
|
||||
import {
|
||||
Convert,
|
||||
Price as JsonPrice,
|
||||
|
@ -14,6 +13,7 @@ export {
|
|||
isAccumulatorUpdateData,
|
||||
parseAccumulatorUpdateData,
|
||||
AccumulatorUpdateData,
|
||||
parsePriceFeedMessage,
|
||||
} from "./AccumulatorUpdateData";
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,4 +4,5 @@
|
|||
target
|
||||
**/*.rs.bk
|
||||
node_modules
|
||||
lib
|
||||
test-ledger
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
parser: "@typescript-eslint/parser",
|
||||
plugins: ["@typescript-eslint"],
|
||||
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
||||
rules: {
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
},
|
||||
};
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"name": "@pythnetwork/pyth-solana-receiver",
|
||||
"version": "0.1.0",
|
||||
"description": "Pyth solana receiver SDK",
|
||||
"homepage": "https://pyth.network",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"files": [
|
||||
"lib/**/*"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pyth-network/pyth-crosschain.git",
|
||||
"directory": "target_chains/solana/sdk/js"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"format": "prettier --write \"src/**/*.ts\"",
|
||||
"lint": "eslint src/",
|
||||
"prepublishOnly": "npm run build && npm test && npm run lint",
|
||||
"preversion": "npm run lint",
|
||||
"version": "npm run format && git add -A src"
|
||||
},
|
||||
"keywords": [
|
||||
"pyth",
|
||||
"oracle"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.4.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.20.0",
|
||||
"@typescript-eslint/parser": "^5.20.0",
|
||||
"eslint": "^8.13.0",
|
||||
"jest": "^29.4.0",
|
||||
"prettier": "^2.6.2",
|
||||
"quicktype": "^23.0.76",
|
||||
"ts-jest": "^29.0.5",
|
||||
"typescript": "^4.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@coral-xyz/anchor": "^0.29.0",
|
||||
"@pythnetwork/price-service-sdk": "*",
|
||||
"@pythnetwork/solana-utils": "*",
|
||||
"@solana/web3.js": "^1.90.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,319 @@
|
|||
import { AnchorProvider, Program } from "@coral-xyz/anchor";
|
||||
import { Connection, Signer, VersionedTransaction } from "@solana/web3.js";
|
||||
import {
|
||||
PythSolanaReceiver as PythSolanaReceiverProgram,
|
||||
IDL as Idl,
|
||||
} from "./idl/pyth_solana_receiver";
|
||||
import {
|
||||
WormholeCoreBridgeSolana,
|
||||
IDL as WormholeCoreBridgeSolanaIdl,
|
||||
} from "./idl/wormhole_core_bridge_solana";
|
||||
import {
|
||||
DEFAULT_RECEIVER_PROGRAM_ID,
|
||||
DEFAULT_WORMHOLE_PROGRAM_ID,
|
||||
getConfigPda,
|
||||
getGuardianSetPda,
|
||||
getTreasuryPda,
|
||||
} from "./address";
|
||||
import { PublicKey, Keypair } from "@solana/web3.js";
|
||||
import {
|
||||
parseAccumulatorUpdateData,
|
||||
parsePriceFeedMessage,
|
||||
} from "@pythnetwork/price-service-sdk";
|
||||
import {
|
||||
POST_UPDATE_ATOMIC_COMPUTE_BUDGET,
|
||||
POST_UPDATE_COMPUTE_BUDGET,
|
||||
VERIFY_ENCODED_VAA_COMPUTE_BUDGET,
|
||||
} from "./compute_budget";
|
||||
import { Wallet } from "@coral-xyz/anchor/dist/cjs/provider";
|
||||
import {
|
||||
buildEncodedVaaCreateInstruction,
|
||||
buildWriteEncodedVaaWithSplit,
|
||||
getGuardianSetIndex,
|
||||
trimSignatures,
|
||||
} from "./vaa";
|
||||
import {
|
||||
TransactionBuilder,
|
||||
InstructionWithEphemeralSigners,
|
||||
} from "@pythnetwork/solana-utils";
|
||||
import { PriorityFeeConfig } from "@pythnetwork/solana-utils/lib/transaction";
|
||||
|
||||
export const DEFAULT_TREASURY_ID = 0;
|
||||
|
||||
export class PythSolanaReceiver {
|
||||
readonly connection: Connection;
|
||||
readonly wallet: Wallet;
|
||||
readonly provider: AnchorProvider;
|
||||
readonly receiver: Program<PythSolanaReceiverProgram>;
|
||||
readonly wormhole: Program<WormholeCoreBridgeSolana>;
|
||||
|
||||
constructor({
|
||||
connection,
|
||||
wallet,
|
||||
wormholeProgramId = DEFAULT_WORMHOLE_PROGRAM_ID,
|
||||
receiverProgramId = DEFAULT_RECEIVER_PROGRAM_ID,
|
||||
}: {
|
||||
connection: Connection;
|
||||
wallet: Wallet;
|
||||
wormholeProgramId?: PublicKey;
|
||||
receiverProgramId?: PublicKey;
|
||||
}) {
|
||||
this.connection = connection;
|
||||
this.wallet = wallet;
|
||||
this.provider = new AnchorProvider(this.connection, this.wallet, {
|
||||
commitment: connection.commitment,
|
||||
});
|
||||
this.receiver = new Program<PythSolanaReceiverProgram>(
|
||||
Idl as PythSolanaReceiverProgram,
|
||||
receiverProgramId,
|
||||
this.provider
|
||||
);
|
||||
this.wormhole = new Program<WormholeCoreBridgeSolana>(
|
||||
WormholeCoreBridgeSolanaIdl as WormholeCoreBridgeSolana,
|
||||
wormholeProgramId,
|
||||
this.provider
|
||||
);
|
||||
}
|
||||
|
||||
async withPriceUpdate(
|
||||
priceUpdateDataArray: string[],
|
||||
getInstructions: (
|
||||
priceFeedIdToPriceUpdateAccount: Record<string, PublicKey>
|
||||
) => Promise<InstructionWithEphemeralSigners[]>,
|
||||
priorityFeeConfig?: PriorityFeeConfig
|
||||
): Promise<{ tx: VersionedTransaction; signers: Signer[] }[]> {
|
||||
const {
|
||||
postInstructions,
|
||||
priceFeedIdToPriceUpdateAccount: priceFeedIdToPriceUpdateAccount,
|
||||
cleanupInstructions,
|
||||
} = await this.buildPostPriceUpdateInstructions(priceUpdateDataArray);
|
||||
return TransactionBuilder.batchIntoVersionedTransactions(
|
||||
this.wallet.publicKey,
|
||||
this.connection,
|
||||
[
|
||||
...postInstructions,
|
||||
...(await getInstructions(priceFeedIdToPriceUpdateAccount)),
|
||||
...cleanupInstructions,
|
||||
],
|
||||
priorityFeeConfig ?? {}
|
||||
);
|
||||
}
|
||||
|
||||
async withPartiallyVerifiedPriceUpdate(
|
||||
priceUpdateDataArray: string[],
|
||||
getInstructions: (
|
||||
priceFeedIdToPriceUpdateAccount: Record<string, PublicKey>
|
||||
) => Promise<InstructionWithEphemeralSigners[]>,
|
||||
priorityFeeConfig?: PriorityFeeConfig
|
||||
): Promise<{ tx: VersionedTransaction; signers: Signer[] }[]> {
|
||||
const {
|
||||
postInstructions,
|
||||
priceFeedIdToPriceUpdateAccount,
|
||||
cleanupInstructions,
|
||||
} = await this.buildPostPriceUpdateAtomicInstructions(priceUpdateDataArray);
|
||||
return TransactionBuilder.batchIntoVersionedTransactions(
|
||||
this.wallet.publicKey,
|
||||
this.connection,
|
||||
[
|
||||
...postInstructions,
|
||||
...(await getInstructions(priceFeedIdToPriceUpdateAccount)),
|
||||
...cleanupInstructions,
|
||||
],
|
||||
priorityFeeConfig ?? {}
|
||||
);
|
||||
}
|
||||
|
||||
async buildPostPriceUpdateAtomicInstructions(
|
||||
priceUpdateDataArray: string[]
|
||||
): Promise<{
|
||||
postInstructions: InstructionWithEphemeralSigners[];
|
||||
priceFeedIdToPriceUpdateAccount: Record<string, PublicKey>;
|
||||
cleanupInstructions: InstructionWithEphemeralSigners[];
|
||||
}> {
|
||||
const postInstructions: InstructionWithEphemeralSigners[] = [];
|
||||
const priceFeedIdToPriceUpdateAccount: Record<string, PublicKey> = {};
|
||||
const cleanupInstructions: InstructionWithEphemeralSigners[] = [];
|
||||
|
||||
for (const priceUpdateData of priceUpdateDataArray) {
|
||||
const accumulatorUpdateData = parseAccumulatorUpdateData(
|
||||
Buffer.from(priceUpdateData, "base64")
|
||||
);
|
||||
const guardianSetIndex = getGuardianSetIndex(accumulatorUpdateData.vaa);
|
||||
const trimmedVaa = trimSignatures(accumulatorUpdateData.vaa);
|
||||
|
||||
for (const update of accumulatorUpdateData.updates) {
|
||||
const priceUpdateKeypair = new Keypair();
|
||||
postInstructions.push({
|
||||
instruction: await this.receiver.methods
|
||||
.postUpdateAtomic({
|
||||
vaa: trimmedVaa,
|
||||
merklePriceUpdate: update,
|
||||
treasuryId: DEFAULT_TREASURY_ID,
|
||||
})
|
||||
.accounts({
|
||||
priceUpdateAccount: priceUpdateKeypair.publicKey,
|
||||
treasury: getTreasuryPda(DEFAULT_TREASURY_ID),
|
||||
config: getConfigPda(),
|
||||
guardianSet: getGuardianSetPda(guardianSetIndex),
|
||||
})
|
||||
.instruction(),
|
||||
signers: [priceUpdateKeypair],
|
||||
computeUnits: POST_UPDATE_ATOMIC_COMPUTE_BUDGET,
|
||||
});
|
||||
priceFeedIdToPriceUpdateAccount[
|
||||
"0x" + parsePriceFeedMessage(update.message).feedId.toString("hex")
|
||||
] = priceUpdateKeypair.publicKey;
|
||||
|
||||
cleanupInstructions.push(
|
||||
await this.buildClosePriceUpdateInstruction(
|
||||
priceUpdateKeypair.publicKey
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
return {
|
||||
postInstructions,
|
||||
priceFeedIdToPriceUpdateAccount,
|
||||
cleanupInstructions,
|
||||
};
|
||||
}
|
||||
|
||||
async buildPostEncodedVaaInstructions(vaa: Buffer): Promise<{
|
||||
postInstructions: InstructionWithEphemeralSigners[];
|
||||
encodedVaaAddress: PublicKey;
|
||||
cleanupInstructions: InstructionWithEphemeralSigners[];
|
||||
}> {
|
||||
const postInstructions: InstructionWithEphemeralSigners[] = [];
|
||||
const cleanupInstructions: InstructionWithEphemeralSigners[] = [];
|
||||
const encodedVaaKeypair = new Keypair();
|
||||
const guardianSetIndex = getGuardianSetIndex(vaa);
|
||||
|
||||
postInstructions.push(
|
||||
await buildEncodedVaaCreateInstruction(
|
||||
this.wormhole,
|
||||
vaa,
|
||||
encodedVaaKeypair
|
||||
)
|
||||
);
|
||||
postInstructions.push({
|
||||
instruction: await this.wormhole.methods
|
||||
.initEncodedVaa()
|
||||
.accounts({
|
||||
encodedVaa: encodedVaaKeypair.publicKey,
|
||||
})
|
||||
.instruction(),
|
||||
signers: [],
|
||||
});
|
||||
|
||||
postInstructions.push(
|
||||
...(await buildWriteEncodedVaaWithSplit(
|
||||
this.wormhole,
|
||||
vaa,
|
||||
encodedVaaKeypair.publicKey
|
||||
))
|
||||
);
|
||||
|
||||
postInstructions.push({
|
||||
instruction: await this.wormhole.methods
|
||||
.verifyEncodedVaaV1()
|
||||
.accounts({
|
||||
guardianSet: getGuardianSetPda(guardianSetIndex),
|
||||
draftVaa: encodedVaaKeypair.publicKey,
|
||||
})
|
||||
.instruction(),
|
||||
signers: [],
|
||||
computeUnits: VERIFY_ENCODED_VAA_COMPUTE_BUDGET,
|
||||
});
|
||||
|
||||
cleanupInstructions.push(
|
||||
await this.buildCloseEncodedVaaInstruction(encodedVaaKeypair.publicKey)
|
||||
);
|
||||
|
||||
return {
|
||||
postInstructions,
|
||||
encodedVaaAddress: encodedVaaKeypair.publicKey,
|
||||
cleanupInstructions,
|
||||
};
|
||||
}
|
||||
|
||||
async buildPostPriceUpdateInstructions(
|
||||
priceUpdateDataArray: string[]
|
||||
): Promise<{
|
||||
postInstructions: InstructionWithEphemeralSigners[];
|
||||
priceFeedIdToPriceUpdateAccount: Record<string, PublicKey>;
|
||||
cleanupInstructions: InstructionWithEphemeralSigners[];
|
||||
}> {
|
||||
const postInstructions: InstructionWithEphemeralSigners[] = [];
|
||||
const priceFeedIdToPriceUpdateAccount: Record<string, PublicKey> = {};
|
||||
const cleanupInstructions: InstructionWithEphemeralSigners[] = [];
|
||||
|
||||
for (const priceUpdateData of priceUpdateDataArray) {
|
||||
const accumulatorUpdateData = parseAccumulatorUpdateData(
|
||||
Buffer.from(priceUpdateData, "base64")
|
||||
);
|
||||
|
||||
const {
|
||||
postInstructions: postEncodedVaaInstructions,
|
||||
encodedVaaAddress: encodedVaa,
|
||||
cleanupInstructions: postEncodedVaaCleanupInstructions,
|
||||
} = await this.buildPostEncodedVaaInstructions(accumulatorUpdateData.vaa);
|
||||
postInstructions.push(...postEncodedVaaInstructions);
|
||||
cleanupInstructions.push(...postEncodedVaaCleanupInstructions);
|
||||
|
||||
for (const update of accumulatorUpdateData.updates) {
|
||||
const priceUpdateKeypair = new Keypair();
|
||||
postInstructions.push({
|
||||
instruction: await this.receiver.methods
|
||||
.postUpdate({
|
||||
merklePriceUpdate: update,
|
||||
treasuryId: DEFAULT_TREASURY_ID,
|
||||
})
|
||||
.accounts({
|
||||
encodedVaa,
|
||||
priceUpdateAccount: priceUpdateKeypair.publicKey,
|
||||
treasury: getTreasuryPda(DEFAULT_TREASURY_ID),
|
||||
config: getConfigPda(),
|
||||
})
|
||||
.instruction(),
|
||||
signers: [priceUpdateKeypair],
|
||||
computeUnits: POST_UPDATE_COMPUTE_BUDGET,
|
||||
});
|
||||
|
||||
priceFeedIdToPriceUpdateAccount[
|
||||
"0x" + parsePriceFeedMessage(update.message).feedId.toString("hex")
|
||||
] = priceUpdateKeypair.publicKey;
|
||||
cleanupInstructions.push(
|
||||
await this.buildClosePriceUpdateInstruction(
|
||||
priceUpdateKeypair.publicKey
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
postInstructions,
|
||||
priceFeedIdToPriceUpdateAccount,
|
||||
cleanupInstructions,
|
||||
};
|
||||
}
|
||||
|
||||
async buildCloseEncodedVaaInstruction(
|
||||
encodedVaa: PublicKey
|
||||
): Promise<InstructionWithEphemeralSigners> {
|
||||
const instruction = await this.wormhole.methods
|
||||
.closeEncodedVaa()
|
||||
.accounts({ encodedVaa })
|
||||
.instruction();
|
||||
return { instruction, signers: [] };
|
||||
}
|
||||
|
||||
async buildClosePriceUpdateInstruction(
|
||||
priceUpdateAccount: PublicKey
|
||||
): Promise<InstructionWithEphemeralSigners> {
|
||||
const instruction = await this.receiver.methods
|
||||
.reclaimRent()
|
||||
.accounts({ priceUpdateAccount })
|
||||
.instruction();
|
||||
return { instruction, signers: [] };
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
import { PublicKey } from "@solana/web3.js";
|
||||
|
||||
export const DEFAULT_RECEIVER_PROGRAM_ID = new PublicKey(
|
||||
"rec5EKMGg6MxZYaMdyBfgwp4d5rB9T1VQH5pJv5LtFJ"
|
||||
);
|
||||
export const DEFAULT_WORMHOLE_PROGRAM_ID = new PublicKey(
|
||||
"HDwcJBJXjL9FpJ7UBsYBtaDjsBUhuLCUYoz3zr8SWWaQ"
|
||||
);
|
||||
|
||||
export const getGuardianSetPda = (guardianSetIndex: number) => {
|
||||
const guardianSetIndexBuf = Buffer.alloc(4);
|
||||
guardianSetIndexBuf.writeUInt32BE(guardianSetIndex, 0);
|
||||
return PublicKey.findProgramAddressSync(
|
||||
[Buffer.from("GuardianSet"), guardianSetIndexBuf],
|
||||
DEFAULT_WORMHOLE_PROGRAM_ID
|
||||
)[0];
|
||||
};
|
||||
|
||||
export const getTreasuryPda = (treasuryId: number) => {
|
||||
return PublicKey.findProgramAddressSync(
|
||||
[Buffer.from("treasury"), Buffer.from([treasuryId])],
|
||||
DEFAULT_RECEIVER_PROGRAM_ID
|
||||
)[0];
|
||||
};
|
||||
|
||||
export const getConfigPda = () => {
|
||||
return PublicKey.findProgramAddressSync(
|
||||
[Buffer.from("config")],
|
||||
DEFAULT_RECEIVER_PROGRAM_ID
|
||||
)[0];
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
export const VERIFY_ENCODED_VAA_COMPUTE_BUDGET = 400000;
|
||||
export const POST_UPDATE_ATOMIC_COMPUTE_BUDGET = 400000;
|
||||
export const POST_UPDATE_COMPUTE_BUDGET = 200000;
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,2 @@
|
|||
export { PythSolanaReceiver } from "./PythSolanaReceiver";
|
||||
export { TransactionBuilder } from "@pythnetwork/solana-utils";
|
|
@ -0,0 +1,81 @@
|
|||
import { Keypair, PublicKey } from "@solana/web3.js";
|
||||
import { WormholeCoreBridgeSolana } from "./idl/wormhole_core_bridge_solana";
|
||||
import { Program } from "@coral-xyz/anchor";
|
||||
import { InstructionWithEphemeralSigners } from "@pythnetwork/solana-utils";
|
||||
|
||||
export const VAA_START = 46;
|
||||
export const VAA_SIGNATURE_SIZE = 66;
|
||||
export const VAA_SPLIT_INDEX = 792;
|
||||
export const DEFAULT_REDUCED_GUARDIAN_SET_SIZE = 5;
|
||||
|
||||
export function getGuardianSetIndex(vaa: Buffer) {
|
||||
return vaa.readUInt32BE(1);
|
||||
}
|
||||
|
||||
export function trimSignatures(
|
||||
vaa: Buffer,
|
||||
n = DEFAULT_REDUCED_GUARDIAN_SET_SIZE
|
||||
): Buffer {
|
||||
const currentNumSignatures = vaa[5];
|
||||
if (n > currentNumSignatures) {
|
||||
throw new Error(
|
||||
"Resulting VAA can't have more signatures than the original VAA"
|
||||
);
|
||||
}
|
||||
|
||||
const trimmedVaa = Buffer.concat([
|
||||
vaa.subarray(0, 6 + n * VAA_SIGNATURE_SIZE),
|
||||
vaa.subarray(6 + currentNumSignatures * VAA_SIGNATURE_SIZE),
|
||||
]);
|
||||
|
||||
trimmedVaa[5] = n;
|
||||
return trimmedVaa;
|
||||
}
|
||||
|
||||
export async function buildEncodedVaaCreateInstruction(
|
||||
wormhole: Program<WormholeCoreBridgeSolana>,
|
||||
vaa: Buffer,
|
||||
encodedVaaKeypair: Keypair
|
||||
) {
|
||||
const encodedVaaSize = vaa.length + VAA_START;
|
||||
return {
|
||||
instruction: await wormhole.account.encodedVaa.createInstruction(
|
||||
encodedVaaKeypair,
|
||||
encodedVaaSize
|
||||
),
|
||||
signers: [encodedVaaKeypair],
|
||||
};
|
||||
}
|
||||
|
||||
export async function buildWriteEncodedVaaWithSplit(
|
||||
wormhole: Program<WormholeCoreBridgeSolana>,
|
||||
vaa: Buffer,
|
||||
draftVaa: PublicKey
|
||||
): Promise<InstructionWithEphemeralSigners[]> {
|
||||
return [
|
||||
{
|
||||
instruction: await wormhole.methods
|
||||
.writeEncodedVaa({
|
||||
index: 0,
|
||||
data: vaa.subarray(0, VAA_SPLIT_INDEX),
|
||||
})
|
||||
.accounts({
|
||||
draftVaa,
|
||||
})
|
||||
.instruction(),
|
||||
signers: [],
|
||||
},
|
||||
{
|
||||
instruction: await wormhole.methods
|
||||
.writeEncodedVaa({
|
||||
index: VAA_SPLIT_INDEX,
|
||||
data: vaa.subarray(VAA_SPLIT_INDEX),
|
||||
})
|
||||
.accounts({
|
||||
draftVaa,
|
||||
})
|
||||
.instruction(),
|
||||
signers: [],
|
||||
},
|
||||
];
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"extends": "../../../../../tsconfig.base.json",
|
||||
"include": ["src/**/*.ts", "src/**/*.json"],
|
||||
"exclude": ["node_modules", "**/__tests__/*"],
|
||||
"compilerOptions": {
|
||||
"rootDir": "src/",
|
||||
"outDir": "./lib"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
module.exports = {
|
||||
root: true,
|
||||
parser: "@typescript-eslint/parser",
|
||||
plugins: ["@typescript-eslint"],
|
||||
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
|
||||
rules: {
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
"@typescript-eslint/ban-ts-comment": "off",
|
||||
},
|
||||
};
|
|
@ -0,0 +1,5 @@
|
|||
/** @type {import('ts-jest').JestConfigWithTsJest} */
|
||||
module.exports = {
|
||||
preset: "ts-jest",
|
||||
testEnvironment: "node",
|
||||
};
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"name": "@pythnetwork/solana-utils",
|
||||
"version": "0.1.0",
|
||||
"description": "Utility functions for Solana",
|
||||
"homepage": "https://pyth.network",
|
||||
"main": "lib/index.js",
|
||||
"types": "lib/index.d.ts",
|
||||
"files": [
|
||||
"lib/**/*"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pyth-network/pyth-crosschain.git",
|
||||
"directory": "target_chains/solana/sdk/js/solana_utils"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "jest",
|
||||
"build": "tsc",
|
||||
"format": "prettier --write \"src/**/*.ts\"",
|
||||
"lint": "eslint src/",
|
||||
"prepublishOnly": "npm run build && npm test && npm run lint",
|
||||
"preversion": "npm run lint",
|
||||
"version": "npm run format && git add -A src"
|
||||
},
|
||||
"keywords": [
|
||||
"pyth",
|
||||
"oracle"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.4.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.20.0",
|
||||
"@typescript-eslint/parser": "^5.20.0",
|
||||
"eslint": "^8.13.0",
|
||||
"jest": "^29.4.0",
|
||||
"prettier": "^2.6.2",
|
||||
"quicktype": "^23.0.76",
|
||||
"ts-jest": "^29.0.5",
|
||||
"typescript": "^4.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"@solana/web3.js": "^1.90.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
import {
|
||||
ComputeBudgetProgram,
|
||||
Keypair,
|
||||
PublicKey,
|
||||
SystemProgram,
|
||||
Transaction,
|
||||
TransactionInstruction,
|
||||
TransactionMessage,
|
||||
VersionedTransaction,
|
||||
} from "@solana/web3.js";
|
||||
import { getSizeOfCompressedU16, getSizeOfTransaction } from "..";
|
||||
|
||||
it("Unit test compressed u16 size", async () => {
|
||||
expect(getSizeOfCompressedU16(127)).toBe(1);
|
||||
expect(getSizeOfCompressedU16(128)).toBe(2);
|
||||
expect(getSizeOfCompressedU16(16383)).toBe(2);
|
||||
expect(getSizeOfCompressedU16(16384)).toBe(3);
|
||||
});
|
||||
|
||||
it("Unit test for getSizeOfTransaction", async () => {
|
||||
jest.setTimeout(60000);
|
||||
|
||||
const payer = new Keypair();
|
||||
|
||||
const ixsToSend: TransactionInstruction[] = [];
|
||||
|
||||
ixsToSend.push(
|
||||
SystemProgram.createAccount({
|
||||
fromPubkey: payer.publicKey,
|
||||
newAccountPubkey: PublicKey.unique(),
|
||||
space: 100,
|
||||
lamports: 1000000000,
|
||||
programId: SystemProgram.programId,
|
||||
})
|
||||
);
|
||||
|
||||
ixsToSend.push(
|
||||
SystemProgram.createAccountWithSeed({
|
||||
fromPubkey: PublicKey.unique(),
|
||||
basePubkey: PublicKey.unique(),
|
||||
seed: "seed",
|
||||
newAccountPubkey: PublicKey.unique(),
|
||||
space: 100,
|
||||
lamports: 1000000000,
|
||||
programId: SystemProgram.programId,
|
||||
})
|
||||
);
|
||||
|
||||
ixsToSend.push(
|
||||
new TransactionInstruction({
|
||||
keys: [{ pubkey: PublicKey.unique(), isSigner: true, isWritable: true }],
|
||||
programId: PublicKey.unique(),
|
||||
data: Buffer.from([1, 2, 3]),
|
||||
})
|
||||
);
|
||||
|
||||
ixsToSend.push(ComputeBudgetProgram.setComputeUnitLimit({ units: 69 }));
|
||||
|
||||
ixsToSend.push(
|
||||
ComputeBudgetProgram.setComputeUnitPrice({ microLamports: 1000000 })
|
||||
);
|
||||
|
||||
const transaction = new Transaction();
|
||||
for (const ix of ixsToSend) {
|
||||
transaction.add(ix);
|
||||
}
|
||||
|
||||
transaction.recentBlockhash = "GqdFtdM7zzWw33YyHtBNwPhyBsdYKcfm9gT47bWnbHvs"; // Mock blockhash from devnet
|
||||
transaction.feePayer = payer.publicKey;
|
||||
expect(transaction.serialize({ requireAllSignatures: false }).length).toBe(
|
||||
getSizeOfTransaction(ixsToSend, false)
|
||||
);
|
||||
|
||||
const versionedTransaction = new VersionedTransaction(
|
||||
new TransactionMessage({
|
||||
recentBlockhash: transaction.recentBlockhash,
|
||||
payerKey: payer.publicKey,
|
||||
instructions: ixsToSend,
|
||||
}).compileToV0Message()
|
||||
);
|
||||
expect(versionedTransaction.serialize().length).toBe(
|
||||
getSizeOfTransaction(ixsToSend)
|
||||
);
|
||||
});
|
|
@ -0,0 +1,6 @@
|
|||
export {
|
||||
getSizeOfTransaction,
|
||||
getSizeOfCompressedU16,
|
||||
TransactionBuilder,
|
||||
InstructionWithEphemeralSigners,
|
||||
} from "./transaction";
|
|
@ -0,0 +1,226 @@
|
|||
import {
|
||||
ComputeBudgetProgram,
|
||||
Connection,
|
||||
PACKET_DATA_SIZE,
|
||||
PublicKey,
|
||||
Signer,
|
||||
Transaction,
|
||||
TransactionInstruction,
|
||||
TransactionMessage,
|
||||
VersionedTransaction,
|
||||
} from "@solana/web3.js";
|
||||
|
||||
export const DEFAULT_COMPUTE_BUDGET_UNITS = 200000;
|
||||
export const PACKET_DATA_SIZE_WITH_ROOM_FOR_COMPUTE_BUDGET =
|
||||
PACKET_DATA_SIZE - 52;
|
||||
|
||||
export type InstructionWithEphemeralSigners = {
|
||||
instruction: TransactionInstruction;
|
||||
signers: Signer[];
|
||||
computeUnits?: number;
|
||||
};
|
||||
|
||||
export type PriorityFeeConfig = {
|
||||
computeUnitPriceMicroLamports?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the size of a transaction that would contain the provided array of instructions
|
||||
*/
|
||||
export function getSizeOfTransaction(
|
||||
instructions: TransactionInstruction[],
|
||||
versionedTransaction = true
|
||||
): number {
|
||||
const signers = new Set<string>();
|
||||
const accounts = new Set<string>();
|
||||
|
||||
instructions.map((ix) => {
|
||||
accounts.add(ix.programId.toBase58()),
|
||||
ix.keys.map((key) => {
|
||||
if (key.isSigner) {
|
||||
signers.add(key.pubkey.toBase58());
|
||||
}
|
||||
accounts.add(key.pubkey.toBase58());
|
||||
});
|
||||
});
|
||||
|
||||
const instruction_sizes: number = instructions
|
||||
.map(
|
||||
(ix) =>
|
||||
1 +
|
||||
getSizeOfCompressedU16(ix.keys.length) +
|
||||
ix.keys.length +
|
||||
getSizeOfCompressedU16(ix.data.length) +
|
||||
ix.data.length
|
||||
)
|
||||
.reduce((a, b) => a + b, 0);
|
||||
|
||||
return (
|
||||
1 +
|
||||
signers.size * 64 +
|
||||
3 +
|
||||
getSizeOfCompressedU16(accounts.size) +
|
||||
32 * accounts.size +
|
||||
32 +
|
||||
getSizeOfCompressedU16(instructions.length) +
|
||||
instruction_sizes +
|
||||
(versionedTransaction ? 1 + getSizeOfCompressedU16(0) : 0)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of n in bytes when serialized as a CompressedU16
|
||||
*/
|
||||
export function getSizeOfCompressedU16(n: number) {
|
||||
return 1 + Number(n >= 128) + Number(n >= 16384);
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is helpful for batching instructions into transactions in an efficient way.
|
||||
* As you add instructions, it adds them to the current transactions until it's full, then it starts a new transaction.
|
||||
*/
|
||||
export class TransactionBuilder {
|
||||
readonly transactionInstructions: {
|
||||
instructions: TransactionInstruction[];
|
||||
signers: Signer[];
|
||||
computeUnits: number;
|
||||
}[] = [];
|
||||
readonly payer: PublicKey;
|
||||
readonly connection: Connection;
|
||||
|
||||
constructor(payer: PublicKey, connection: Connection) {
|
||||
this.payer = payer;
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an instruction to the builder, the signers argument can be used to specify ephemeral signers that need to sign the transaction
|
||||
* where this instruction appears
|
||||
*/
|
||||
addInstruction(args: InstructionWithEphemeralSigners) {
|
||||
const { instruction, signers, computeUnits } = args;
|
||||
if (this.transactionInstructions.length === 0) {
|
||||
this.transactionInstructions.push({
|
||||
instructions: [instruction],
|
||||
signers: signers,
|
||||
computeUnits: computeUnits ?? 0,
|
||||
});
|
||||
} else if (
|
||||
getSizeOfTransaction([
|
||||
...this.transactionInstructions[this.transactionInstructions.length - 1]
|
||||
.instructions,
|
||||
instruction,
|
||||
]) <= PACKET_DATA_SIZE_WITH_ROOM_FOR_COMPUTE_BUDGET
|
||||
) {
|
||||
this.transactionInstructions[
|
||||
this.transactionInstructions.length - 1
|
||||
].instructions.push(instruction);
|
||||
this.transactionInstructions[
|
||||
this.transactionInstructions.length - 1
|
||||
].signers.push(...signers);
|
||||
this.transactionInstructions[
|
||||
this.transactionInstructions.length - 1
|
||||
].computeUnits += computeUnits ?? 0;
|
||||
} else
|
||||
this.transactionInstructions.push({
|
||||
instructions: [instruction],
|
||||
signers: signers,
|
||||
computeUnits: computeUnits ?? 0,
|
||||
});
|
||||
}
|
||||
|
||||
addInstructions(instructions: InstructionWithEphemeralSigners[]) {
|
||||
for (const { instruction, signers, computeUnits } of instructions) {
|
||||
this.addInstruction({ instruction, signers, computeUnits });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the added instructions batched into transactions, plus for each transaction the ephemeral signers that need to sign it
|
||||
*/
|
||||
async getVersionedTransactions(
|
||||
args: PriorityFeeConfig
|
||||
): Promise<{ tx: VersionedTransaction; signers: Signer[] }[]> {
|
||||
const blockhash = (await this.connection.getLatestBlockhash()).blockhash;
|
||||
|
||||
return this.transactionInstructions.map(
|
||||
({ instructions, signers, computeUnits }) => {
|
||||
const instructionsWithComputeBudget: TransactionInstruction[] = [
|
||||
...instructions,
|
||||
];
|
||||
if (computeUnits > DEFAULT_COMPUTE_BUDGET_UNITS * instructions.length) {
|
||||
instructionsWithComputeBudget.push(
|
||||
ComputeBudgetProgram.setComputeUnitLimit({ units: computeUnits })
|
||||
);
|
||||
}
|
||||
if (args.computeUnitPriceMicroLamports) {
|
||||
instructionsWithComputeBudget.push(
|
||||
ComputeBudgetProgram.setComputeUnitPrice({
|
||||
microLamports: args.computeUnitPriceMicroLamports,
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
tx: new VersionedTransaction(
|
||||
new TransactionMessage({
|
||||
recentBlockhash: blockhash,
|
||||
instructions: instructionsWithComputeBudget,
|
||||
payerKey: this.payer,
|
||||
}).compileToV0Message()
|
||||
),
|
||||
signers: signers,
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the added instructions batched into transactions, plus for each transaction the ephemeral signers that need to sign it
|
||||
*/
|
||||
getLegacyTransactions(
|
||||
args: PriorityFeeConfig
|
||||
): { tx: Transaction; signers: Signer[] }[] {
|
||||
return this.transactionInstructions.map(({ instructions, signers }) => {
|
||||
const instructionsWithComputeBudget = args.computeUnitPriceMicroLamports
|
||||
? [
|
||||
...instructions,
|
||||
ComputeBudgetProgram.setComputeUnitPrice({
|
||||
microLamports: args.computeUnitPriceMicroLamports,
|
||||
}),
|
||||
]
|
||||
: instructions;
|
||||
|
||||
return {
|
||||
tx: new Transaction().add(...instructionsWithComputeBudget),
|
||||
signers: signers,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
static batchIntoLegacyTransactions(
|
||||
instructions: TransactionInstruction[]
|
||||
): Transaction[] {
|
||||
const transactionBuilder = new TransactionBuilder(
|
||||
PublicKey.unique(),
|
||||
new Connection("http://placeholder.placeholder")
|
||||
); // We only need wallet and connection for `VersionedTransaction` so we can put placeholders here
|
||||
for (const instruction of instructions) {
|
||||
transactionBuilder.addInstruction({ instruction, signers: [] });
|
||||
}
|
||||
return transactionBuilder.getLegacyTransactions({}).map(({ tx }) => {
|
||||
return tx;
|
||||
});
|
||||
}
|
||||
|
||||
static async batchIntoVersionedTransactions(
|
||||
payer: PublicKey,
|
||||
connection: Connection,
|
||||
instructions: InstructionWithEphemeralSigners[],
|
||||
priorityFeeConfig: PriorityFeeConfig
|
||||
): Promise<{ tx: VersionedTransaction; signers: Signer[] }[]> {
|
||||
const transactionBuilder = new TransactionBuilder(payer, connection);
|
||||
transactionBuilder.addInstructions(instructions);
|
||||
return transactionBuilder.getVersionedTransactions(priorityFeeConfig);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"extends": "../../../../../tsconfig.base.json",
|
||||
"include": ["src/**/*.ts", "src/**/*.json"],
|
||||
"exclude": ["node_modules", "**/__tests__/*"],
|
||||
"compilerOptions": {
|
||||
"rootDir": "src/",
|
||||
"outDir": "./lib"
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue