Skip to content

Commit f065812

Browse files
devin-ai-integration[bot]Jayant Krishnamurthy
andcommitted
fix: restore local AccumulatorUpdateData to price-service-sdk and keep copy in hermes
Co-Authored-By: Jayant Krishnamurthy <[email protected]>
1 parent e97d2aa commit f065812

File tree

4 files changed

+369
-1
lines changed

4 files changed

+369
-1
lines changed

apps/hermes/client/js/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
],
3737
"license": "Apache-2.0",
3838
"devDependencies": {
39+
"@types/bn.js": "^5.1.5",
3940
"@types/eventsource": "^1.1.15",
4041
"@types/jest": "^29.4.0",
4142
"@types/node": "^20.14.2",
@@ -52,6 +53,7 @@
5253
},
5354
"dependencies": {
5455
"@zodios/core": "^10.9.6",
56+
"bn.js": "^5.2.1",
5557
"eventsource": "^2.0.2",
5658
"zod": "^3.23.8"
5759
}
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
import BN from "bn.js";
2+
3+
const ACCUMULATOR_MAGIC = "504e4155";
4+
const MAJOR_VERSION = 1;
5+
const MINOR_VERSION = 0;
6+
const KECCAK160_HASH_SIZE = 20;
7+
const PRICE_FEED_MESSAGE_VARIANT = 0;
8+
const TWAP_MESSAGE_VARIANT = 1;
9+
10+
export type AccumulatorUpdateData = {
11+
vaa: Buffer;
12+
updates: { message: Buffer; proof: number[][] }[];
13+
};
14+
export type PriceFeedMessage = {
15+
feedId: Buffer;
16+
price: BN;
17+
confidence: BN;
18+
exponent: number;
19+
publishTime: BN;
20+
prevPublishTime: BN;
21+
emaPrice: BN;
22+
emaConf: BN;
23+
};
24+
25+
export type TwapMessage = {
26+
feedId: Buffer;
27+
cumulativePrice: BN;
28+
cumulativeConf: BN;
29+
numDownSlots: BN;
30+
exponent: number;
31+
publishTime: BN;
32+
prevPublishTime: BN;
33+
publishSlot: BN;
34+
};
35+
36+
export function isAccumulatorUpdateData(updateBytes: Buffer): boolean {
37+
return (
38+
updateBytes.toString("hex").slice(0, 8) === ACCUMULATOR_MAGIC &&
39+
updateBytes[4] === MAJOR_VERSION &&
40+
updateBytes[5] === MINOR_VERSION
41+
);
42+
}
43+
44+
export function parsePriceFeedMessage(message: Buffer): PriceFeedMessage {
45+
let cursor = 0;
46+
const variant = message.readUInt8(cursor);
47+
if (variant !== PRICE_FEED_MESSAGE_VARIANT) {
48+
throw new Error("Not a price feed message");
49+
}
50+
cursor += 1;
51+
const feedId = message.subarray(cursor, cursor + 32);
52+
cursor += 32;
53+
const price = new BN(message.subarray(cursor, cursor + 8), "be");
54+
cursor += 8;
55+
const confidence = new BN(message.subarray(cursor, cursor + 8), "be");
56+
cursor += 8;
57+
const exponent = message.readInt32BE(cursor);
58+
cursor += 4;
59+
const publishTime = new BN(message.subarray(cursor, cursor + 8), "be");
60+
cursor += 8;
61+
const prevPublishTime = new BN(message.subarray(cursor, cursor + 8), "be");
62+
cursor += 8;
63+
const emaPrice = new BN(message.subarray(cursor, cursor + 8), "be");
64+
cursor += 8;
65+
const emaConf = new BN(message.subarray(cursor, cursor + 8), "be");
66+
cursor += 8;
67+
return {
68+
feedId,
69+
price,
70+
confidence,
71+
exponent,
72+
publishTime,
73+
prevPublishTime,
74+
emaPrice,
75+
emaConf,
76+
};
77+
}
78+
79+
export function parseTwapMessage(message: Buffer): TwapMessage {
80+
let cursor = 0;
81+
const variant = message.readUInt8(cursor);
82+
if (variant !== TWAP_MESSAGE_VARIANT) {
83+
throw new Error("Not a twap message");
84+
}
85+
cursor += 1;
86+
const feedId = message.subarray(cursor, cursor + 32);
87+
cursor += 32;
88+
const cumulativePrice = new BN(message.subarray(cursor, cursor + 16), "be");
89+
cursor += 16;
90+
const cumulativeConf = new BN(message.subarray(cursor, cursor + 16), "be");
91+
cursor += 16;
92+
const numDownSlots = new BN(message.subarray(cursor, cursor + 8), "be");
93+
cursor += 8;
94+
const exponent = message.readInt32BE(cursor);
95+
cursor += 4;
96+
const publishTime = new BN(message.subarray(cursor, cursor + 8), "be");
97+
cursor += 8;
98+
const prevPublishTime = new BN(message.subarray(cursor, cursor + 8), "be");
99+
cursor += 8;
100+
const publishSlot = new BN(message.subarray(cursor, cursor + 8), "be");
101+
cursor += 8;
102+
return {
103+
feedId,
104+
cumulativePrice,
105+
cumulativeConf,
106+
numDownSlots,
107+
exponent,
108+
publishTime,
109+
prevPublishTime,
110+
publishSlot,
111+
};
112+
}
113+
114+
/**
115+
* 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).
116+
*/
117+
export function sliceAccumulatorUpdateData(
118+
data: Buffer,
119+
start?: number,
120+
end?: number
121+
): Buffer {
122+
if (!isAccumulatorUpdateData(data)) {
123+
throw new Error("Invalid accumulator message");
124+
}
125+
let cursor = 6;
126+
const trailingPayloadSize = data.readUint8(cursor);
127+
cursor += 1 + trailingPayloadSize;
128+
129+
// const proofType = data.readUint8(cursor);
130+
cursor += 1;
131+
132+
const vaaSize = data.readUint16BE(cursor);
133+
cursor += 2;
134+
cursor += vaaSize;
135+
136+
const endOfVaa = cursor;
137+
138+
const updates = [];
139+
const numUpdates = data.readUInt8(cursor);
140+
cursor += 1;
141+
142+
for (let i = 0; i < numUpdates; i++) {
143+
const updateStart = cursor;
144+
const messageSize = data.readUint16BE(cursor);
145+
cursor += 2;
146+
cursor += messageSize;
147+
148+
const numProofs = data.readUInt8(cursor);
149+
cursor += 1;
150+
cursor += KECCAK160_HASH_SIZE * numProofs;
151+
152+
updates.push(data.subarray(updateStart, cursor));
153+
}
154+
155+
if (cursor !== data.length) {
156+
throw new Error("Didn't reach the end of the message");
157+
}
158+
159+
const sliceUpdates = updates.slice(start, end);
160+
return Buffer.concat([
161+
data.subarray(0, endOfVaa),
162+
Buffer.from([sliceUpdates.length]),
163+
...updates.slice(start, end),
164+
]);
165+
}
166+
167+
export function parseAccumulatorUpdateData(
168+
data: Buffer
169+
): AccumulatorUpdateData {
170+
if (!isAccumulatorUpdateData(data)) {
171+
throw new Error("Invalid accumulator message");
172+
}
173+
174+
let cursor = 6;
175+
const trailingPayloadSize = data.readUint8(cursor);
176+
cursor += 1 + trailingPayloadSize;
177+
178+
// const proofType = data.readUint8(cursor);
179+
cursor += 1;
180+
181+
const vaaSize = data.readUint16BE(cursor);
182+
cursor += 2;
183+
184+
const vaa = data.subarray(cursor, cursor + vaaSize);
185+
cursor += vaaSize;
186+
187+
const numUpdates = data.readUInt8(cursor);
188+
const updates = [];
189+
cursor += 1;
190+
191+
for (let i = 0; i < numUpdates; i++) {
192+
const messageSize = data.readUint16BE(cursor);
193+
cursor += 2;
194+
const message = data.subarray(cursor, cursor + messageSize);
195+
cursor += messageSize;
196+
197+
const numProofs = data.readUInt8(cursor);
198+
cursor += 1;
199+
const proof = [];
200+
for (let j = 0; j < numProofs; j++) {
201+
proof.push(
202+
Array.from(data.subarray(cursor, cursor + KECCAK160_HASH_SIZE))
203+
);
204+
cursor += KECCAK160_HASH_SIZE;
205+
}
206+
207+
updates.push({ message, proof });
208+
}
209+
210+
if (cursor !== data.length) {
211+
throw new Error("Didn't reach the end of the message");
212+
}
213+
214+
return { vaa, updates };
215+
}
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
import {
2+
parseAccumulatorUpdateData,
3+
parsePriceFeedMessage,
4+
parseTwapMessage,
5+
sliceAccumulatorUpdateData,
6+
} from "../AccumulatorUpdateData";
7+
8+
// This is just a sample update data from hermes
9+
const TEST_ACCUMULATOR_UPDATE_DATA =
10+
"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";
11+
12+
describe("Test parse accumulator update", () => {
13+
test("Happy path", async () => {
14+
const { vaa, updates } = parseAccumulatorUpdateData(
15+
Buffer.from(TEST_ACCUMULATOR_UPDATE_DATA, "base64")
16+
);
17+
18+
const priceMessages = updates.map((update) => {
19+
return parsePriceFeedMessage(update.message);
20+
});
21+
expect(priceMessages[0].feedId.toString("hex")).toBe(
22+
"c96458d393fe9deb7a7d63a0ac41e2898a67a7750dbd166673279e06c868df0a"
23+
);
24+
expect(priceMessages[0].price.toString()).toBe("4689500");
25+
expect(priceMessages[0].confidence.toString()).toBe("6174");
26+
expect(priceMessages[0].exponent).toBe(-8);
27+
expect(priceMessages[0].publishTime.toString()).toBe("1715627146");
28+
expect(priceMessages[0].prevPublishTime.toString()).toBe("1715627145");
29+
expect(priceMessages[0].emaPrice.toString()).toBe("4690654");
30+
expect(priceMessages[0].emaConf.toString()).toBe("6257");
31+
32+
expect(priceMessages[1].feedId.toString("hex")).toBe(
33+
"ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace"
34+
);
35+
expect(priceMessages[1].price.toString()).toBe("294943041422");
36+
expect(priceMessages[1].confidence.toString()).toBe("184284043");
37+
expect(priceMessages[1].exponent).toBe(-8);
38+
expect(priceMessages[1].publishTime.toString()).toBe("1715627146");
39+
expect(priceMessages[1].prevPublishTime.toString()).toBe("1715627145");
40+
expect(priceMessages[1].emaPrice.toString()).toBe("294580230000");
41+
expect(priceMessages[1].emaConf.toString()).toBe("239495968");
42+
43+
expect(priceMessages[2].feedId.toString("hex")).toBe(
44+
"ef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d"
45+
);
46+
expect(priceMessages[2].price.toString()).toBe("14802549538");
47+
expect(priceMessages[2].confidence.toString()).toBe("9930888");
48+
expect(priceMessages[2].exponent).toBe(-8);
49+
expect(priceMessages[2].publishTime.toString()).toBe("1715627146");
50+
expect(priceMessages[2].prevPublishTime.toString()).toBe("1715627145");
51+
expect(priceMessages[2].emaPrice.toString()).toBe("14732272500");
52+
expect(priceMessages[2].emaConf.toString()).toBe("10975810");
53+
});
54+
55+
test("Slice accumulator update data", async () => {
56+
expect(
57+
parseAccumulatorUpdateData(
58+
sliceAccumulatorUpdateData(
59+
Buffer.from(TEST_ACCUMULATOR_UPDATE_DATA, "base64"),
60+
2,
61+
1
62+
)
63+
).updates.length
64+
).toBe(0);
65+
66+
expect(
67+
parseAccumulatorUpdateData(
68+
sliceAccumulatorUpdateData(
69+
Buffer.from(TEST_ACCUMULATOR_UPDATE_DATA, "base64"),
70+
0,
71+
5
72+
)
73+
).updates.length
74+
).toBe(3);
75+
76+
const { vaa, updates } = parseAccumulatorUpdateData(
77+
sliceAccumulatorUpdateData(
78+
Buffer.from(TEST_ACCUMULATOR_UPDATE_DATA, "base64"),
79+
1,
80+
3
81+
)
82+
);
83+
84+
const priceMessages = updates.map((update) => {
85+
return parsePriceFeedMessage(update.message);
86+
});
87+
expect(priceMessages[0].feedId.toString("hex")).toBe(
88+
"ff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace"
89+
);
90+
expect(priceMessages[0].price.toString()).toBe("294943041422");
91+
expect(priceMessages[0].confidence.toString()).toBe("184284043");
92+
expect(priceMessages[0].exponent).toBe(-8);
93+
expect(priceMessages[0].publishTime.toString()).toBe("1715627146");
94+
expect(priceMessages[0].prevPublishTime.toString()).toBe("1715627145");
95+
expect(priceMessages[0].emaPrice.toString()).toBe("294580230000");
96+
expect(priceMessages[0].emaConf.toString()).toBe("239495968");
97+
98+
expect(priceMessages[1].feedId.toString("hex")).toBe(
99+
"ef0d8b6fda2ceba41da15d4095d1da392a0d2f8ed0c6c7bc0f4cfac8c280b56d"
100+
);
101+
expect(priceMessages[1].price.toString()).toBe("14802549538");
102+
expect(priceMessages[1].confidence.toString()).toBe("9930888");
103+
expect(priceMessages[1].exponent).toBe(-8);
104+
expect(priceMessages[1].publishTime.toString()).toBe("1715627146");
105+
expect(priceMessages[1].prevPublishTime.toString()).toBe("1715627145");
106+
expect(priceMessages[1].emaPrice.toString()).toBe("14732272500");
107+
expect(priceMessages[1].emaConf.toString()).toBe("10975810");
108+
});
109+
110+
test("Wrong magic number", async () => {
111+
const data = Buffer.from(TEST_ACCUMULATOR_UPDATE_DATA, "base64");
112+
data[0] = 0;
113+
expect(() => parseAccumulatorUpdateData(data)).toThrow(
114+
"Invalid accumulator message"
115+
);
116+
});
117+
118+
test("Parse TWAP message", () => {
119+
// Sample data from the Hermes latest TWAP endpoint.
120+
const testAccumulatorDataTwap =
121+
"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";
122+
const { updates } = parseAccumulatorUpdateData(
123+
Buffer.from(testAccumulatorDataTwap, "base64")
124+
);
125+
126+
// Test that both messages are parsed successfully
127+
const twapMessage1 = parseTwapMessage(updates[0].message);
128+
expect(twapMessage1.feedId.toString("hex")).toBe(
129+
"49f6b65cb1de6b10eaf75e7c03ca029c306d0357e91b5311b175084a5ad55688"
130+
);
131+
expect(twapMessage1.cumulativePrice.toString()).toBe("1760238576144013");
132+
expect(twapMessage1.cumulativeConf.toString()).toBe("5113466755162");
133+
expect(twapMessage1.numDownSlots.toString()).toBe("72037403");
134+
expect(twapMessage1.exponent).toBe(-5);
135+
expect(twapMessage1.publishTime.toString()).toBe("1733155135");
136+
expect(twapMessage1.prevPublishTime.toString()).toBe("1733155134");
137+
expect(twapMessage1.publishSlot.toString()).toBe("181871343");
138+
139+
const twapMessage2 = parseTwapMessage(updates[1].message);
140+
expect(twapMessage2.feedId.toString("hex")).toBe(
141+
"2b9ab1e972a281585084148ba1389800799bd4be63b957507db1349314e47445"
142+
);
143+
expect(twapMessage2.cumulativePrice.toString()).toBe("949830028892149802");
144+
expect(twapMessage2.cumulativeConf.toString()).toBe("973071467813959");
145+
expect(twapMessage2.numDownSlots.toString()).toBe("27140");
146+
expect(twapMessage2.exponent).toBe(-8);
147+
expect(twapMessage2.publishTime.toString()).toBe("1733155135");
148+
expect(twapMessage2.prevPublishTime.toString()).toBe("1733155134");
149+
expect(twapMessage2.publishSlot.toString()).toBe("181871343");
150+
});
151+
});

price_service/sdk/js/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export {
1616
AccumulatorUpdateData,
1717
parsePriceFeedMessage,
1818
parseTwapMessage,
19-
} from "@pythnetwork/hermes-client";
19+
} from "./AccumulatorUpdateData";
2020

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

0 commit comments

Comments
 (0)