Skip to content

Commit

Permalink
fix: restore local AccumulatorUpdateData to price-service-sdk and kee…
Browse files Browse the repository at this point in the history
…p copy in hermes

Co-Authored-By: Jayant Krishnamurthy <[email protected]>
  • Loading branch information
devin-ai-integration[bot] and Jayant Krishnamurthy committed Jan 23, 2025
1 parent e97d2aa commit f065812
Show file tree
Hide file tree
Showing 4 changed files with 369 additions and 1 deletion.
2 changes: 2 additions & 0 deletions apps/hermes/client/js/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
],
"license": "Apache-2.0",
"devDependencies": {
"@types/bn.js": "^5.1.5",
"@types/eventsource": "^1.1.15",
"@types/jest": "^29.4.0",
"@types/node": "^20.14.2",
Expand All @@ -52,6 +53,7 @@
},
"dependencies": {
"@zodios/core": "^10.9.6",
"bn.js": "^5.2.1",
"eventsource": "^2.0.2",
"zod": "^3.23.8"
}
Expand Down
215 changes: 215 additions & 0 deletions price_service/sdk/js/src/AccumulatorUpdateData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
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;
const TWAP_MESSAGE_VARIANT = 1;

export type AccumulatorUpdateData = {
vaa: 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 type TwapMessage = {
feedId: Buffer;
cumulativePrice: BN;
cumulativeConf: BN;
numDownSlots: BN;
exponent: number;
publishTime: BN;
prevPublishTime: BN;
publishSlot: BN;
};

export function isAccumulatorUpdateData(updateBytes: Buffer): boolean {
return (
updateBytes.toString("hex").slice(0, 8) === ACCUMULATOR_MAGIC &&
updateBytes[4] === MAJOR_VERSION &&
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 parseTwapMessage(message: Buffer): TwapMessage {
let cursor = 0;
const variant = message.readUInt8(cursor);
if (variant !== TWAP_MESSAGE_VARIANT) {
throw new Error("Not a twap message");
}
cursor += 1;
const feedId = message.subarray(cursor, cursor + 32);
cursor += 32;
const cumulativePrice = new BN(message.subarray(cursor, cursor + 16), "be");
cursor += 16;
const cumulativeConf = new BN(message.subarray(cursor, cursor + 16), "be");
cursor += 16;
const numDownSlots = 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 publishSlot = new BN(message.subarray(cursor, cursor + 8), "be");
cursor += 8;
return {
feedId,
cumulativePrice,
cumulativeConf,
numDownSlots,
exponent,
publishTime,
prevPublishTime,
publishSlot,
};
}

/**
* An AccumulatorUpdateData contains a VAA and a list of updates. This function returns a new serialized AccumulatorUpdateData with only the updates in the range [start, end).
*/
export function sliceAccumulatorUpdateData(
data: Buffer,
start?: number,
end?: number
): Buffer {
if (!isAccumulatorUpdateData(data)) {
throw new Error("Invalid accumulator message");
}
let cursor = 6;
const trailingPayloadSize = data.readUint8(cursor);
cursor += 1 + trailingPayloadSize;

// const proofType = data.readUint8(cursor);
cursor += 1;

const vaaSize = data.readUint16BE(cursor);
cursor += 2;
cursor += vaaSize;

const endOfVaa = cursor;

const updates = [];
const numUpdates = data.readUInt8(cursor);
cursor += 1;

for (let i = 0; i < numUpdates; i++) {
const updateStart = cursor;
const messageSize = data.readUint16BE(cursor);
cursor += 2;
cursor += messageSize;

const numProofs = data.readUInt8(cursor);
cursor += 1;
cursor += KECCAK160_HASH_SIZE * numProofs;

updates.push(data.subarray(updateStart, cursor));
}

if (cursor !== data.length) {
throw new Error("Didn't reach the end of the message");
}

const sliceUpdates = updates.slice(start, end);
return Buffer.concat([
data.subarray(0, endOfVaa),
Buffer.from([sliceUpdates.length]),
...updates.slice(start, end),
]);
}

export function parseAccumulatorUpdateData(
data: Buffer
): AccumulatorUpdateData {
if (!isAccumulatorUpdateData(data)) {
throw new Error("Invalid accumulator message");
}

let cursor = 6;
const trailingPayloadSize = data.readUint8(cursor);
cursor += 1 + trailingPayloadSize;

// const proofType = data.readUint8(cursor);
cursor += 1;

const vaaSize = data.readUint16BE(cursor);
cursor += 2;

const vaa = data.subarray(cursor, cursor + vaaSize);
cursor += vaaSize;

const numUpdates = data.readUInt8(cursor);
const updates = [];
cursor += 1;

for (let i = 0; i < numUpdates; i++) {
const messageSize = data.readUint16BE(cursor);
cursor += 2;
const message = data.subarray(cursor, cursor + messageSize);
cursor += messageSize;

const numProofs = data.readUInt8(cursor);
cursor += 1;
const proof = [];
for (let j = 0; j < numProofs; j++) {
proof.push(
Array.from(data.subarray(cursor, cursor + KECCAK160_HASH_SIZE))
);
cursor += KECCAK160_HASH_SIZE;
}

updates.push({ message, proof });
}

if (cursor !== data.length) {
throw new Error("Didn't reach the end of the message");
}

return { vaa, updates };
}
151 changes: 151 additions & 0 deletions price_service/sdk/js/src/__tests__/AccumulatorUpdateData.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import {
parseAccumulatorUpdateData,
parsePriceFeedMessage,
parseTwapMessage,
sliceAccumulatorUpdateData,
} from "../AccumulatorUpdateData";

// This is just a sample update data from hermes
const TEST_ACCUMULATOR_UPDATE_DATA =
"UE5BVQEAAAADuAEAAAAEDQDCQmx/SUJVpnlJ6D/HXQvOYGhjtvPa8y2xwvTduTs6oj2N406RF1OYsMmqnuqXv8f7iEimuEkvQdHhgY28eRy9AAJanMT6xkyTcnMxNt8Vgn+EgOAxCryiqLhc05Rlpcwe0S3+OmLN1ifjPuanHH3D8CkwOvDJgUbU1PLhTloGH+0oAAPYrlxLSvd8hYqfjiC7eSdbpeD7X0R2jXb+0nL7YVHrAUeu3uEnvAziRg73GOTc0G9R6UWCg+YP/zRp3krAsDIPAASBDxiDxF2HE9LCH4NeC7D3s47gZKUwl0B3ptabRZYvc0U/7Ttz2RTzl5PfAXTK60DWJnJERDlAbj8c59Jos9v4AAY8OPOzSRUyoQhYpphlBaTjO8q3Dg5Qrv5amnGDclx6VAG6vGfqErtSpsMjBZLnz8Lhxp4eJ1Ot4DI1IGmxJbRdAAes8Nc5dDCvIiTPwMpzN4ma51whWivcHq/ymviUKhg9pFibGCzRQW8NsxRDfZH2/cf2fVyC1mr7Pftv2EPBJO1uAApXWWLkjOZXKUWDiEWkWyAE14xLHCNclXDlVPehMM0huEmDgijMSUKyRPHaw/NMFTzA3OecXGskVKxmdFQcX0DCAQv5QVoq0b+Td0Cs1/TwftoUGr+R8AmdUUuwDn2oRK4I61NmRhF4mYaszUH5ERsHo4SNxTA+RbcTT5fflAC7XriVAQxGICt7NNC5EnA6+MvTsQhRgbbmr+qnBSq5VvEF65iWyFWwaeRDhjtk81u6DZkxhfS7+QzUsFFjO9sGkl1ZMv8hAA1uAeD1DRgMxbipcmjTkmI6mXMWzbyFmMAJUi+jXe7740OVQOBMEjkYHGeDXdNaKXQmRCmNy5mXRnFO1n9piFzVAA4QwHiq6D/IJveCc8+ynJsaR+PNwADmbIrdGb4Y4sMSuWC6kEp6WyKcNZizrk1ZB1Dl8jF3aiunNXtb8DjtAMTDAA9yFaEkIKOml5mSceZ0yDnkDkE53a1/0yHKG1RLAF1iPD/aToPh3U07FRcf8uVnhof0q61VkNy1Bgm5R7cJDJFoABJToX2me8ANo3nZC/NDDxCfVBZcvIfgGsqPuxFEkgFOKGAqCWnMYRzhxaqPrgg1q6nYa/8qONS7zprGCiUHoI4iAWZCZIoAAAAAABrhAfrtrFhR4yubI7X5QRqMK6xKrj7U3XuBHdGnLqSqcQAAAAADW+Y9AUFVV1YAAAAAAAhmA68AACcQh+eO4lll0hkFZY214Rd4PGknF0YDAFUAyWRY05P+net6fWOgrEHiiYpnp3UNvRZmcyeeBsho3woAAAAAAEeOXAAAAAAAABge////+AAAAABmQmSKAAAAAGZCZIkAAAAAAEeS3gAAAAAAABhxChHz7A8jzwzaF8ZQL4TSYFOrMO27C2wkaI7qTgtVcAmYcC/k7aSXpmkPACMiQd+IP4agmvqvwdByAMA2cVSYxfwESuHDoqjanEewjAA6SION5ZwUkIrqTCPO+naSyR6H808OYDuzUX37m5Dc91HlPJqzeZBUg60znGDwRXLHtMte5ZKwxskxaSaMdPfK3dn+QLjw7IvRuvJNlhjDTC/KzQ3Pe7huLggEYJPpvJSw++VhJh9389orPHR1YFWlYdzY15NdQwX9gzObAFUA/2FJGpMREt3xvYFHzRtkE3X3n1glEm1mVICHRjT9Cs4AAABEq/mnjgAAAAAK+/OL////+AAAAABmQmSKAAAAAGZCZIkAAABEllmXcAAAAAAORmsgCmwQvv7XRaz2EALTUYcqq0yTDDQmryC22unSWFv2fJZ1MSkiFzk5ncckHRMfyPUbSdhSA26rcSJqnebJc6cnkSmWOgWUr1ewm4DCmcnBvdBzaQweGwv9Da04OQWF8I58YusFjTt/xajFt/SSBrSAmdcnLtMsOPGTh3HeistRvyzfTXD+qiT0KPwvwUd53dn+QLjw7IvRuvJNlhjDTC/KzQ3Pe7huLggEYJPpvJSw++VhJh9389orPHR1YFWlYdzY15NdQwX9gzObAFUA7w2Lb9os66QdoV1AldHaOSoNL47Qxse8D0z6yMKAtW0AAAADckz7IgAAAAAAl4iI////+AAAAABmQmSKAAAAAGZCZIkAAAADbhyjdAAAAAAAp3pCCgPM32dNQNYyhQutl5S290omaXtVA0QUgyoKd9L303zqKVOkRfXMQNf4p02im3SVDqEFHrvT9Dcv6ryXTbR+45EDouH3kPsTPI36oF9UCOLlPcIN790WYmTciwR/xgq4ftKmoGzXUl1bEduniNVERqzrUXF0Qi4E63HeistRvyzfTXD+qiT0KPwvwUd53dn+QLjw7IvRuvJNlhjDTC/KzQ3Pe7huLggEYJPpvJSw++VhJh9389orPHR1YFWlYdzY15NdQwX9gzOb";

describe("Test parse accumulator update", () => {
test("Happy path", async () => {
const { vaa, updates } = parseAccumulatorUpdateData(
Buffer.from(TEST_ACCUMULATOR_UPDATE_DATA, "base64")
);

const priceMessages = updates.map((update) => {
return parsePriceFeedMessage(update.message);
});
expect(priceMessages[0].feedId.toString("hex")).toBe(
"c96458d393fe9deb7a7d63a0ac41e2898a67a7750dbd166673279e06c868df0a"
);
expect(priceMessages[0].price.toString()).toBe("4689500");
expect(priceMessages[0].confidence.toString()).toBe("6174");
expect(priceMessages[0].exponent).toBe(-8);
expect(priceMessages[0].publishTime.toString()).toBe("1715627146");
expect(priceMessages[0].prevPublishTime.toString()).toBe("1715627145");
expect(priceMessages[0].emaPrice.toString()).toBe("4690654");
expect(priceMessages[0].emaConf.toString()).toBe("6257");

expect(priceMessages[1].feedId.toString("hex")).toBe(
"ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace"
);
expect(priceMessages[1].price.toString()).toBe("294943041422");
expect(priceMessages[1].confidence.toString()).toBe("184284043");
expect(priceMessages[1].exponent).toBe(-8);
expect(priceMessages[1].publishTime.toString()).toBe("1715627146");
expect(priceMessages[1].prevPublishTime.toString()).toBe("1715627145");
expect(priceMessages[1].emaPrice.toString()).toBe("294580230000");
expect(priceMessages[1].emaConf.toString()).toBe("239495968");

expect(priceMessages[2].feedId.toString("hex")).toBe(
"ef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d"
);
expect(priceMessages[2].price.toString()).toBe("14802549538");
expect(priceMessages[2].confidence.toString()).toBe("9930888");
expect(priceMessages[2].exponent).toBe(-8);
expect(priceMessages[2].publishTime.toString()).toBe("1715627146");
expect(priceMessages[2].prevPublishTime.toString()).toBe("1715627145");
expect(priceMessages[2].emaPrice.toString()).toBe("14732272500");
expect(priceMessages[2].emaConf.toString()).toBe("10975810");
});

test("Slice accumulator update data", async () => {
expect(
parseAccumulatorUpdateData(
sliceAccumulatorUpdateData(
Buffer.from(TEST_ACCUMULATOR_UPDATE_DATA, "base64"),
2,
1
)
).updates.length
).toBe(0);

expect(
parseAccumulatorUpdateData(
sliceAccumulatorUpdateData(
Buffer.from(TEST_ACCUMULATOR_UPDATE_DATA, "base64"),
0,
5
)
).updates.length
).toBe(3);

const { vaa, updates } = parseAccumulatorUpdateData(
sliceAccumulatorUpdateData(
Buffer.from(TEST_ACCUMULATOR_UPDATE_DATA, "base64"),
1,
3
)
);

const priceMessages = updates.map((update) => {
return parsePriceFeedMessage(update.message);
});
expect(priceMessages[0].feedId.toString("hex")).toBe(
"ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace"
);
expect(priceMessages[0].price.toString()).toBe("294943041422");
expect(priceMessages[0].confidence.toString()).toBe("184284043");
expect(priceMessages[0].exponent).toBe(-8);
expect(priceMessages[0].publishTime.toString()).toBe("1715627146");
expect(priceMessages[0].prevPublishTime.toString()).toBe("1715627145");
expect(priceMessages[0].emaPrice.toString()).toBe("294580230000");
expect(priceMessages[0].emaConf.toString()).toBe("239495968");

expect(priceMessages[1].feedId.toString("hex")).toBe(
"ef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d"
);
expect(priceMessages[1].price.toString()).toBe("14802549538");
expect(priceMessages[1].confidence.toString()).toBe("9930888");
expect(priceMessages[1].exponent).toBe(-8);
expect(priceMessages[1].publishTime.toString()).toBe("1715627146");
expect(priceMessages[1].prevPublishTime.toString()).toBe("1715627145");
expect(priceMessages[1].emaPrice.toString()).toBe("14732272500");
expect(priceMessages[1].emaConf.toString()).toBe("10975810");
});

test("Wrong magic number", async () => {
const data = Buffer.from(TEST_ACCUMULATOR_UPDATE_DATA, "base64");
data[0] = 0;
expect(() => parseAccumulatorUpdateData(data)).toThrow(
"Invalid accumulator message"
);
});

test("Parse TWAP message", () => {
// Sample data from the Hermes latest TWAP endpoint.
const testAccumulatorDataTwap =
"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";
const { updates } = parseAccumulatorUpdateData(
Buffer.from(testAccumulatorDataTwap, "base64")
);

// Test that both messages are parsed successfully
const twapMessage1 = parseTwapMessage(updates[0].message);
expect(twapMessage1.feedId.toString("hex")).toBe(
"49f6b65cb1de6b10eaf75e7c03ca029c306d0357e91b5311b175084a5ad55688"
);
expect(twapMessage1.cumulativePrice.toString()).toBe("1760238576144013");
expect(twapMessage1.cumulativeConf.toString()).toBe("5113466755162");
expect(twapMessage1.numDownSlots.toString()).toBe("72037403");
expect(twapMessage1.exponent).toBe(-5);
expect(twapMessage1.publishTime.toString()).toBe("1733155135");
expect(twapMessage1.prevPublishTime.toString()).toBe("1733155134");
expect(twapMessage1.publishSlot.toString()).toBe("181871343");

const twapMessage2 = parseTwapMessage(updates[1].message);
expect(twapMessage2.feedId.toString("hex")).toBe(
"2b9ab1e972a281585084148ba1389800799bd4be63b957507db1349314e47445"
);
expect(twapMessage2.cumulativePrice.toString()).toBe("949830028892149802");
expect(twapMessage2.cumulativeConf.toString()).toBe("973071467813959");
expect(twapMessage2.numDownSlots.toString()).toBe("27140");
expect(twapMessage2.exponent).toBe(-8);
expect(twapMessage2.publishTime.toString()).toBe("1733155135");
expect(twapMessage2.prevPublishTime.toString()).toBe("1733155134");
expect(twapMessage2.publishSlot.toString()).toBe("181871343");
});
});
2 changes: 1 addition & 1 deletion price_service/sdk/js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export {
AccumulatorUpdateData,
parsePriceFeedMessage,
parseTwapMessage,
} from "@pythnetwork/hermes-client";
} from "./AccumulatorUpdateData";

/**
* A Pyth Price represented as `${price} ± ${conf} * 10^${expo}` published at `publishTime`.
Expand Down

0 comments on commit f065812

Please sign in to comment.