[wormhole-attester] Remove wasm in js sdk (#537)
* [wormhole-attester] Remove wasm in js sdk * Cleanup dependencies and unused codes * Cleanup rust * Add tests + minor improvements * Add ignored price status * Revert cargo lock
This commit is contained in:
parent
277599b458
commit
f9e0145352
18
Tiltfile
18
Tiltfile
|
@ -62,18 +62,6 @@ if not ci:
|
|||
def k8s_yaml_with_ns(objects):
|
||||
return k8s_yaml(namespace_inject(objects, namespace))
|
||||
|
||||
# wasm
|
||||
|
||||
local_resource(
|
||||
name = "wasm-gen",
|
||||
cmd = "tilt docker build -- -f tilt_devnet/docker_images/Dockerfile.wasm -o type=local,dest=. .",
|
||||
env = {"DOCKER_BUILDKIT": "1"},
|
||||
deps = "./wormhole_attester",
|
||||
labels = ["wasm"],
|
||||
allow_parallel=True,
|
||||
trigger_mode = trigger_mode,
|
||||
)
|
||||
|
||||
def build_node_yaml():
|
||||
node_yaml = read_yaml_stream("tilt_devnet/k8s/node.yaml")
|
||||
|
||||
|
@ -220,7 +208,7 @@ docker_build(
|
|||
k8s_yaml_with_ns("tilt_devnet/k8s/p2w-terra-relay.yaml")
|
||||
k8s_resource(
|
||||
"p2w-terra-relay",
|
||||
resource_deps = ["pyth", "p2w-attest", "spy", "terra-terrad", "wasm-gen"],
|
||||
resource_deps = ["pyth", "p2w-attest", "spy", "terra-terrad"],
|
||||
port_forwards = [
|
||||
port_forward(4200, name = "Rest API (Status + Query) [:4200]", host = webHost),
|
||||
port_forward(8081, name = "Prometheus [:8081]", host = webHost)],
|
||||
|
@ -230,7 +218,7 @@ k8s_resource(
|
|||
k8s_yaml_with_ns("tilt_devnet/k8s/p2w-evm-relay.yaml")
|
||||
k8s_resource(
|
||||
"p2w-evm-relay",
|
||||
resource_deps = ["pyth", "p2w-attest", "spy", "eth-devnet", "wasm-gen"],
|
||||
resource_deps = ["pyth", "p2w-attest", "spy", "eth-devnet"],
|
||||
port_forwards = [
|
||||
port_forward(4201, container_port = 4200, name = "Rest API (Status + Query) [:4201]", host = webHost),
|
||||
port_forward(8082, container_port = 8081, name = "Prometheus [:8082]", host = webHost)],
|
||||
|
@ -246,7 +234,7 @@ docker_build(
|
|||
k8s_yaml_with_ns("tilt_devnet/k8s/pyth-price-server.yaml")
|
||||
k8s_resource(
|
||||
"pyth-price-server",
|
||||
resource_deps = ["pyth", "p2w-attest", "spy", "eth-devnet", "wasm-gen"],
|
||||
resource_deps = ["pyth", "p2w-attest", "spy", "eth-devnet"],
|
||||
port_forwards = [
|
||||
port_forward(4202, container_port = 4200, name = "Rest API (Status + Query) [:4202]", host = webHost),
|
||||
port_forward(8083, container_port = 8081, name = "Prometheus [:8083]", host = webHost)],
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
# syntax=docker.io/docker/dockerfile:1.3@sha256:42399d4635eddd7a9b8a24be879d2f9a930d0ed040a61324cfdf59ef1357b3b2
|
||||
FROM docker.io/library/rust:1.49@sha256:a50165ea96983c21832578afb1c8c028674c965bc1ed43b607871b1f362e06a5 AS build
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y libssl-dev libudev-dev pkg-config zlib1g-dev llvm clang && \
|
||||
apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# We default an older nightly since current rust-toolchain makes the
|
||||
# wasm-pack build unhappy, we introduce it later for our code
|
||||
RUN rustup component add rustfmt && \
|
||||
rustup default nightly-2022-01-02
|
||||
|
||||
WORKDIR /usr/src
|
||||
RUN cargo install wasm-pack --vers 0.9.1
|
||||
ENV RUST_LOG="solana_runtime::system_instruction_processor=trace,solana_runtime::message_processor=trace,solana_bpf_loader=debug,solana_rbpf=debug"
|
||||
ENV EMITTER_ADDRESS="11111111111111111111111111111115"
|
||||
ENV BRIDGE_ADDRESS="Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o"
|
||||
|
||||
COPY wormhole_attester wormhole_attester
|
||||
COPY governance/remote_executor governance/remote_executor
|
||||
|
||||
# wasm-bindgen 0.2.74 generates JavaScript bindings for SystemInstruction exported from solana-program 1.9.4.
|
||||
# The generated JavaScript references a non-existent function (wasm.__wbg_systeminstruction_free) that leads
|
||||
# to an attempted import error when importing the wasm packed for bundler. SystemInstruction isn't used in the sdk,
|
||||
# so we remove the non-existent function reference as a workaround.
|
||||
ARG SED_REMOVE_INVALID_REFERENCE="/^\s*wasm.__wbg_systeminstruction_free(ptr);$/d"
|
||||
|
||||
# TODO: it appears that wasm-pack ignores our lockfiles even with --locked
|
||||
|
||||
# Run unit tests
|
||||
WORKDIR /usr/src/wormhole_attester/sdk/rust
|
||||
RUN cargo test --locked && \
|
||||
/usr/local/cargo/bin/wasm-pack build --target bundler -d bundler -- --features wasm --locked && \
|
||||
/usr/local/cargo/bin/wasm-pack build --target nodejs -d nodejs -- --features wasm --locked
|
||||
|
||||
FROM scratch AS export
|
||||
|
||||
COPY --from=build /usr/src/wormhole_attester/sdk/rust/bundler wormhole_attester/sdk/js/src/wasm/bundler
|
||||
COPY --from=build /usr/src/wormhole_attester/sdk/rust/nodejs wormhole_attester/sdk/js/src/wasm/nodejs
|
|
@ -2751,7 +2751,6 @@ dependencies = [
|
|||
"serde",
|
||||
"solana-program",
|
||||
"solitaire",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -5177,8 +5176,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
FROM node:16-alpine@sha256:004dbac84fed48e20f9888a23e32fa7cf83c2995e174a78d41d9a9dd1e051a20
|
||||
|
||||
# Build ETH
|
||||
WORKDIR /usr/src/ethereum
|
||||
COPY ethereum/package.json ethereum/package-lock.json ./
|
||||
RUN --mount=type=cache,uid=1000,gid=1000,target=/home/node/.npm \
|
||||
npm ci
|
||||
COPY ethereum .
|
||||
|
||||
# Build Wormhole SDK
|
||||
WORKDIR /usr/src/sdk/js
|
||||
COPY sdk/js/package.json sdk/js/package-lock.json ./
|
||||
RUN --mount=type=cache,uid=1000,gid=1000,target=/home/node/.npm \
|
||||
npm ci
|
||||
COPY sdk/js .
|
||||
|
||||
# Build wormhole sdk attester in dir preserving directory structure
|
||||
WORKDIR /usr/src/wormhole_attester/sdk/js/
|
||||
COPY wormhole_attester/sdk/js/package.json wormhole_attester/sdk/js/package-lock.json ./
|
||||
RUN --mount=type=cache,uid=1000,gid=1000,target=/home/node/.npm \
|
||||
npm ci
|
||||
COPY wormhole_attester/sdk .
|
||||
RUN npm run build-test
|
|
@ -0,0 +1,5 @@
|
|||
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
|
||||
module.exports = {
|
||||
preset: "ts-jest",
|
||||
testEnvironment: "node",
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -1,19 +1,17 @@
|
|||
{
|
||||
"name": "@pythnetwork/wormhole-attester-sdk",
|
||||
"version": "1.0.0",
|
||||
"description": "TypeScript library for interacting with Pyth2Wormhole",
|
||||
"description": "Pyth Wormhole Attester SDk",
|
||||
"types": "lib/index.d.ts",
|
||||
"main": "lib/index.js",
|
||||
"files": [
|
||||
"lib/**/*"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "npm run build-lib",
|
||||
"build-lib": "npm run copy-artifacts && tsc",
|
||||
"build-watch": "npm run copy-artifacts && tsc --watch",
|
||||
"build": "tsc",
|
||||
"format": "prettier --write \"src/**/*.ts\"",
|
||||
"copy-artifacts": "node scripts/copyWasm.cjs",
|
||||
"lint": "tslint -p tsconfig.json",
|
||||
"test": "jest src/",
|
||||
"postversion": "git push && git push --tags",
|
||||
"preversion": "npm run lint",
|
||||
"version": "npm run format && git add -A src"
|
||||
|
@ -22,26 +20,22 @@
|
|||
"type": "git",
|
||||
"url": "git+https://github.com/pyth-network/pyth-crosschain.git"
|
||||
},
|
||||
"author": "https://certus.one",
|
||||
"author": "Pyth Data Association",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@openzeppelin/contracts": "^4.2.0",
|
||||
"@typechain/ethers-v5": "^7.1.2",
|
||||
"@types/jest": "^29.4.0",
|
||||
"@types/long": "^4.0.1",
|
||||
"@types/node": "^16.6.1",
|
||||
"copy-dir": "^1.3.0",
|
||||
"find": "^0.3.0",
|
||||
"jest": "^29.4.1",
|
||||
"prettier": "^2.3.2",
|
||||
"ts-jest": "^29.0.5",
|
||||
"tslint": "^6.1.3",
|
||||
"tslint-config-prettier": "^1.18.0",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@solana/web3.js": "^1.24.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@certusone/wormhole-sdk": "0.2.1",
|
||||
"@improbable-eng/grpc-web-node-http-transport": "^0.14.1",
|
||||
"@pythnetwork/pyth-sdk-js": "^1.1.0"
|
||||
},
|
||||
"bugs": {
|
||||
|
|
|
@ -1,16 +0,0 @@
|
|||
const find = require("find");
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
const SOURCE_ROOT = "src";
|
||||
const TARGET_ROOT = "lib";
|
||||
|
||||
find.eachfile(/\.wasm(\..*)?/, SOURCE_ROOT, (fname) => {
|
||||
fname_copy = fname.replace(SOURCE_ROOT, TARGET_ROOT);
|
||||
|
||||
console.log("copyWasm:", fname, "to", fname_copy);
|
||||
|
||||
fs.mkdirSync(path.dirname(fname_copy), { recursive: true });
|
||||
|
||||
fs.copyFileSync(fname, fname_copy);
|
||||
});
|
|
@ -0,0 +1,133 @@
|
|||
import {
|
||||
parseBatchPriceAttestation,
|
||||
Price,
|
||||
PriceFeed,
|
||||
PriceAttestation,
|
||||
PriceAttestationStatus,
|
||||
priceAttestationToPriceFeed,
|
||||
} from "../index";
|
||||
|
||||
describe("Deserializing Batch Price Attestation works", () => {
|
||||
test("when batch has 3 price feeds", () => {
|
||||
// Generated from the rust sdk test_batch_serde
|
||||
const fixture =
|
||||
"50325748000300010001020003009D01010101010101010101010101010101010101010101010101010" +
|
||||
"10101010101FEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFEFE0000002B" +
|
||||
"AD2FEED70000000000000065FFFFFFFDFFFFFFFFFFFFFFD6000000000000002A010001E14C0004E6D00" +
|
||||
"000DEADBEEFFADE00000000DADEBEEF00000000DEADBABE0000DEADFACEBEEF000000BADBADBEEFDEAD" +
|
||||
"BEEFFADEDEAF0202020202020202020202020202020202020202020202020202020202020202FDFDFDF" +
|
||||
"DFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFDFD0000002BAD2FEED70000000000" +
|
||||
"000065FFFFFFFDFFFFFFFFFFFFFFD6000000000000002A010001E14C0004E6D00000DEADBEEFFADE000" +
|
||||
"00000DADEBEEF00000000DEADBABE0000DEADFACEBEEF000000BADBADBEEFDEADBEEFFADEDEAF030303" +
|
||||
"0303030303030303030303030303030303030303030303030303030303FCFCFCFCFCFCFCFCFCFCFCFCF" +
|
||||
"CFCFCFCFCFCFCFCFCFCFCFCFCFCFCFCFCFCFCFC0000002BAD2FEED70000000000000065FFFFFFFDFFFF" +
|
||||
"FFFFFFFFFFD6000000000000002A010001E14C0004E6D00000DEADBEEFFADE00000000DADEBEEF00000" +
|
||||
"000DEADBABE0000DEADFACEBEEF000000BADBADBEEFDEADBEEFFADEDEAF";
|
||||
|
||||
const data = Buffer.from(fixture, "hex");
|
||||
const batchPriceAttestation = parseBatchPriceAttestation(data);
|
||||
|
||||
expect(batchPriceAttestation.priceAttestations.length).toBe(3);
|
||||
|
||||
// values are from the rust sdk mock_attestation
|
||||
batchPriceAttestation.priceAttestations.forEach((pa, idx) => {
|
||||
expect(pa).toEqual<PriceAttestation>({
|
||||
productId: Buffer.from(Array(32).fill(idx + 1)).toString("hex"),
|
||||
priceId: Buffer.from(Array(32).fill(255 - idx - 1)).toString("hex"),
|
||||
price: (0x2bad2feed7).toString(),
|
||||
conf: "101",
|
||||
emaPrice: "-42",
|
||||
emaConf: "42",
|
||||
expo: -3,
|
||||
status: PriceAttestationStatus.Trading,
|
||||
numPublishers: 123212,
|
||||
maxNumPublishers: 321232,
|
||||
attestationTime: 0xdeadbeeffade,
|
||||
publishTime: 0xdadebeef,
|
||||
prevPublishTime: 0xdeadbabe,
|
||||
prevPrice: (0xdeadfacebeef).toString(),
|
||||
prevConf: (0xbadbadbeef).toString(),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Price Attestation to Price Feed works", () => {
|
||||
test("when status is trading", () => {
|
||||
const priceAttestation = {
|
||||
productId: "012345",
|
||||
priceId: "abcde",
|
||||
price: "100",
|
||||
conf: "5",
|
||||
emaPrice: "103",
|
||||
emaConf: "3",
|
||||
expo: -3,
|
||||
status: PriceAttestationStatus.Trading,
|
||||
numPublishers: 1,
|
||||
maxNumPublishers: 2,
|
||||
attestationTime: 1000,
|
||||
publishTime: 1000,
|
||||
prevPublishTime: 998,
|
||||
prevPrice: "101",
|
||||
prevConf: "6",
|
||||
};
|
||||
|
||||
const priceFeed = priceAttestationToPriceFeed(priceAttestation);
|
||||
expect(priceFeed).toEqual(
|
||||
new PriceFeed({
|
||||
id: "abcde",
|
||||
price: new Price({
|
||||
price: "100",
|
||||
conf: "5",
|
||||
expo: -3,
|
||||
publishTime: 1000,
|
||||
}),
|
||||
emaPrice: new Price({
|
||||
price: "103",
|
||||
conf: "3",
|
||||
expo: -3,
|
||||
publishTime: 1000,
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
test("when status is not trading", () => {
|
||||
const priceAttestation = {
|
||||
productId: "012345",
|
||||
priceId: "abcde",
|
||||
price: "100",
|
||||
conf: "5",
|
||||
emaPrice: "103",
|
||||
emaConf: "3",
|
||||
expo: -3,
|
||||
status: PriceAttestationStatus.Unknown,
|
||||
numPublishers: 1,
|
||||
maxNumPublishers: 2,
|
||||
attestationTime: 1000,
|
||||
publishTime: 1000,
|
||||
prevPublishTime: 998,
|
||||
prevPrice: "101",
|
||||
prevConf: "6",
|
||||
};
|
||||
|
||||
const priceFeed = priceAttestationToPriceFeed(priceAttestation);
|
||||
expect(priceFeed).toEqual(
|
||||
new PriceFeed({
|
||||
id: "abcde",
|
||||
price: new Price({
|
||||
price: "101",
|
||||
conf: "6",
|
||||
expo: -3,
|
||||
publishTime: 998,
|
||||
}),
|
||||
emaPrice: new Price({
|
||||
price: "103",
|
||||
conf: "3",
|
||||
expo: -3,
|
||||
publishTime: 998,
|
||||
}),
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
|
@ -1,19 +1,12 @@
|
|||
import { getSignedVAA, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
|
||||
import { zeroPad } from "ethers/lib/utils";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { PriceFeed, Price, UnixTimestamp } from "@pythnetwork/pyth-sdk-js";
|
||||
export { PriceFeed, Price, UnixTimestamp } from "@pythnetwork/pyth-sdk-js";
|
||||
|
||||
let _P2W_WASM: any;
|
||||
|
||||
async function importWasm() {
|
||||
if (!_P2W_WASM) {
|
||||
if (typeof window === "undefined") {
|
||||
_P2W_WASM = await import("./wasm/nodejs/pyth_wormhole_attester_sdk");
|
||||
} else {
|
||||
_P2W_WASM = await import("./wasm/bundler/pyth_wormhole_attester_sdk");
|
||||
}
|
||||
}
|
||||
return _P2W_WASM;
|
||||
export enum PriceAttestationStatus {
|
||||
Unknown = 0,
|
||||
Trading = 1,
|
||||
Halted = 2,
|
||||
Auction = 3,
|
||||
IGNORED = 4,
|
||||
}
|
||||
|
||||
export type PriceAttestation = {
|
||||
|
@ -24,7 +17,7 @@ export type PriceAttestation = {
|
|||
expo: number;
|
||||
emaPrice: string;
|
||||
emaConf: string;
|
||||
status: number;
|
||||
status: PriceAttestationStatus;
|
||||
numPublishers: number;
|
||||
maxNumPublishers: number;
|
||||
attestationTime: UnixTimestamp;
|
||||
|
@ -38,13 +31,146 @@ export type BatchPriceAttestation = {
|
|||
priceAttestations: PriceAttestation[];
|
||||
};
|
||||
|
||||
export async function parseBatchPriceAttestation(
|
||||
arr: Buffer
|
||||
): Promise<BatchPriceAttestation> {
|
||||
const wasm = await importWasm();
|
||||
const rawVal = await wasm.parse_batch_attestation(arr);
|
||||
/// Precedes every message implementing the wormhole attester serialization format
|
||||
const P2W_FORMAT_MAGIC: string = "P2WH";
|
||||
const P2W_FORMAT_VER_MAJOR = 3;
|
||||
const P2W_FORMAT_VER_MINOR = 0;
|
||||
const P2W_FORMAT_PAYLOAD_ID = 2;
|
||||
|
||||
return rawVal;
|
||||
export function parsePriceAttestation(bytes: Buffer): PriceAttestation {
|
||||
let offset = 0;
|
||||
|
||||
const productId = bytes.slice(offset, offset + 32).toString("hex");
|
||||
offset += 32;
|
||||
|
||||
const priceId = bytes.slice(offset, offset + 32).toString("hex");
|
||||
offset += 32;
|
||||
|
||||
const price = bytes.readBigInt64BE(offset).toString();
|
||||
offset += 8;
|
||||
|
||||
const conf = bytes.readBigUint64BE(offset).toString();
|
||||
offset += 8;
|
||||
|
||||
const expo = bytes.readInt32BE(offset);
|
||||
offset += 4;
|
||||
|
||||
const emaPrice = bytes.readBigInt64BE(offset).toString();
|
||||
offset += 8;
|
||||
|
||||
const emaConf = bytes.readBigUint64BE(offset).toString();
|
||||
offset += 8;
|
||||
|
||||
const status = bytes.readUint8(offset) as PriceAttestationStatus;
|
||||
offset += 1;
|
||||
|
||||
const numPublishers = bytes.readUint32BE(offset);
|
||||
offset += 4;
|
||||
|
||||
const maxNumPublishers = bytes.readUint32BE(offset);
|
||||
offset += 4;
|
||||
|
||||
const attestationTime = Number(bytes.readBigInt64BE(offset));
|
||||
offset += 8;
|
||||
|
||||
const publishTime = Number(bytes.readBigInt64BE(offset));
|
||||
offset += 8;
|
||||
|
||||
const prevPublishTime = Number(bytes.readBigInt64BE(offset));
|
||||
offset += 8;
|
||||
|
||||
const prevPrice = bytes.readBigInt64BE(offset).toString();
|
||||
offset += 8;
|
||||
|
||||
const prevConf = bytes.readBigUint64BE(offset).toString();
|
||||
offset += 8;
|
||||
|
||||
return {
|
||||
productId,
|
||||
priceId,
|
||||
price,
|
||||
conf,
|
||||
expo,
|
||||
emaPrice,
|
||||
emaConf,
|
||||
status,
|
||||
numPublishers,
|
||||
maxNumPublishers,
|
||||
attestationTime,
|
||||
publishTime,
|
||||
prevPublishTime,
|
||||
prevPrice,
|
||||
prevConf,
|
||||
};
|
||||
}
|
||||
|
||||
// Read the sdk/rust as the reference implementation and documentation.
|
||||
export function parseBatchPriceAttestation(
|
||||
bytes: Buffer
|
||||
): BatchPriceAttestation {
|
||||
let offset = 0;
|
||||
|
||||
const magic = bytes.slice(offset, offset + 4).toString("utf8");
|
||||
offset += 4;
|
||||
if (magic !== P2W_FORMAT_MAGIC) {
|
||||
throw new Error(`Invalid magic: ${magic}, expected: ${P2W_FORMAT_MAGIC}`);
|
||||
}
|
||||
|
||||
const versionMajor = bytes.readUInt16BE(offset);
|
||||
offset += 2;
|
||||
if (versionMajor !== P2W_FORMAT_VER_MAJOR) {
|
||||
throw new Error(
|
||||
`Unsupported major version: ${versionMajor}, expected: ${P2W_FORMAT_VER_MAJOR}`
|
||||
);
|
||||
}
|
||||
|
||||
const versionMinor = bytes.readUInt16BE(offset);
|
||||
offset += 2;
|
||||
if (versionMinor < P2W_FORMAT_VER_MINOR) {
|
||||
throw new Error(
|
||||
`Unsupported minor version: ${versionMinor}, expected: ${P2W_FORMAT_VER_MINOR}`
|
||||
);
|
||||
}
|
||||
|
||||
// Header size is added for future-compatibility
|
||||
const headerSize = bytes.readUint16BE(offset);
|
||||
offset += 2;
|
||||
|
||||
let headerOffset = 0;
|
||||
|
||||
const payloadId = bytes.readUint8(offset + headerOffset);
|
||||
headerOffset += 1;
|
||||
|
||||
if (payloadId !== P2W_FORMAT_PAYLOAD_ID) {
|
||||
throw new Error(
|
||||
`Invalid payloadId: ${payloadId}, expected: ${P2W_FORMAT_PAYLOAD_ID}`
|
||||
);
|
||||
}
|
||||
|
||||
offset += headerSize;
|
||||
|
||||
const batchLen = bytes.readUInt16BE(offset);
|
||||
offset += 2;
|
||||
|
||||
const attestationSize = bytes.readUint16BE(offset);
|
||||
offset += 2;
|
||||
|
||||
let priceAttestations: PriceAttestation[] = [];
|
||||
|
||||
for (let i = 0; i < batchLen; i += 1) {
|
||||
priceAttestations.push(
|
||||
parsePriceAttestation(bytes.subarray(offset, offset + attestationSize))
|
||||
);
|
||||
offset += attestationSize;
|
||||
}
|
||||
|
||||
if (offset !== bytes.length) {
|
||||
throw new Error(`Invalid length: ${bytes.length}, expected: ${offset}`);
|
||||
}
|
||||
|
||||
return {
|
||||
priceAttestations,
|
||||
};
|
||||
}
|
||||
|
||||
// Returns a hash of all priceIds within the batch, it can be used to identify whether there is a
|
||||
|
@ -75,27 +201,6 @@ export function getBatchSummary(batch: BatchPriceAttestation): string {
|
|||
return JSON.stringify(abstractRepresentation);
|
||||
}
|
||||
|
||||
export async function getSignedAttestation(
|
||||
host: string,
|
||||
p2wAddr: string,
|
||||
sequence: number,
|
||||
extraGrpcOpts = {}
|
||||
): Promise<any> {
|
||||
const [emitter, _] = await PublicKey.findProgramAddress(
|
||||
[Buffer.from("p2w-emitter")],
|
||||
new PublicKey(p2wAddr)
|
||||
);
|
||||
|
||||
const emitterHex = sol_addr2buf(emitter).toString("hex");
|
||||
return await getSignedVAA(
|
||||
host,
|
||||
CHAIN_ID_SOLANA,
|
||||
emitterHex,
|
||||
"" + sequence,
|
||||
extraGrpcOpts
|
||||
);
|
||||
}
|
||||
|
||||
export function priceAttestationToPriceFeed(
|
||||
priceAttestation: PriceAttestation
|
||||
): PriceFeed {
|
||||
|
@ -108,7 +213,7 @@ export function priceAttestationToPriceFeed(
|
|||
|
||||
let price: Price;
|
||||
|
||||
if (priceAttestation.status === 1) {
|
||||
if (priceAttestation.status === PriceAttestationStatus.Trading) {
|
||||
// 1 means trading
|
||||
price = new Price({
|
||||
conf: priceAttestation.conf,
|
||||
|
@ -135,7 +240,3 @@ export function priceAttestationToPriceFeed(
|
|||
price,
|
||||
});
|
||||
}
|
||||
|
||||
function sol_addr2buf(addr: PublicKey): Buffer {
|
||||
return Buffer.from(zeroPad(addr.toBytes(), 32));
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -11,14 +11,12 @@ crate-type = ["cdylib", "rlib"]
|
|||
[features]
|
||||
default = []
|
||||
solana = ["solitaire", "solana-program", "pyth-sdk-solana"]
|
||||
wasm = ["wasm-bindgen", "solana"]
|
||||
|
||||
[dependencies]
|
||||
hex = "0.4.3"
|
||||
serde = { version = "1.0.103", default-features = false, features = ["derive"] }
|
||||
pyth-sdk = {version = "0.5.0"}
|
||||
pyth-sdk-solana = { version = "0.5.0", optional = true }
|
||||
wasm-bindgen = { version = "0.2.74", features = ["serde-serialize"], optional = true}
|
||||
solitaire = { git = "https://github.com/wormhole-foundation/wormhole", tag = "v2.8.9", optional = true}
|
||||
solana-program = { version = "=1.10.31", optional = true }
|
||||
|
||||
|
|
|
@ -34,14 +34,6 @@ use {
|
|||
},
|
||||
};
|
||||
|
||||
#[cfg(feature = "wasm")]
|
||||
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||
pub mod wasm;
|
||||
|
||||
#[cfg(feature = "wasm")]
|
||||
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
pub type ErrBox = Box<dyn std::error::Error>;
|
||||
|
||||
/// Precedes every message implementing the p2w serialization format
|
||||
|
@ -527,7 +519,7 @@ mod tests {
|
|||
status: PriceStatus::Trading,
|
||||
num_publishers: 123212u32,
|
||||
max_num_publishers: 321232u32,
|
||||
attestation_time: (0xdeadbeeffadedeedu64) as i64,
|
||||
attestation_time: (0xdeadbeeffadeu64) as i64,
|
||||
publish_time: 0xdadebeefi64,
|
||||
prev_publish_time: 0xdeadbabei64,
|
||||
prev_price: 0xdeadfacebeefi64,
|
||||
|
@ -567,7 +559,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_batch_serde() -> Result<(), ErrBox> {
|
||||
let attestations: Vec<_> = (1..=10)
|
||||
let attestations: Vec<_> = (1..=3)
|
||||
.map(|i| {
|
||||
mock_attestation(
|
||||
Some([(i % 256) as u8; 32]),
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
use {
|
||||
crate::{
|
||||
BatchPriceAttestation,
|
||||
P2WEmitter,
|
||||
PriceAttestation,
|
||||
},
|
||||
solana_program::pubkey::Pubkey,
|
||||
solitaire::Seeded,
|
||||
std::str::FromStr,
|
||||
wasm_bindgen::prelude::*,
|
||||
};
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn get_emitter_address(program_id: String) -> Vec<u8> {
|
||||
let program_id = Pubkey::from_str(program_id.as_str()).unwrap();
|
||||
let emitter = P2WEmitter::key(None, &program_id);
|
||||
|
||||
emitter.to_bytes().to_vec()
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn parse_attestation(bytes: Vec<u8>) -> JsValue {
|
||||
let a = PriceAttestation::deserialize(bytes.as_slice()).unwrap();
|
||||
|
||||
JsValue::from_serde(&a).unwrap()
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn parse_batch_attestation(bytes: Vec<u8>) -> JsValue {
|
||||
let a = BatchPriceAttestation::deserialize(bytes.as_slice()).unwrap();
|
||||
|
||||
JsValue::from_serde(&a).unwrap()
|
||||
}
|
Loading…
Reference in New Issue