AccumulatorUpdateData.ts 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. import BN from "bn.js";
  2. const ACCUMULATOR_MAGIC = "504e4155";
  3. const MAJOR_VERSION = 1;
  4. const MINOR_VERSION = 0;
  5. const KECCAK160_HASH_SIZE = 20;
  6. const PRICE_FEED_MESSAGE_VARIANT = 0;
  7. export type AccumulatorUpdateData = {
  8. vaa: Buffer;
  9. updates: { message: Buffer; proof: number[][] }[];
  10. };
  11. export type PriceFeedMessage = {
  12. feedId: Buffer;
  13. price: BN;
  14. confidence: BN;
  15. exponent: number;
  16. publishTime: BN;
  17. prevPublishTime: BN;
  18. emaPrice: BN;
  19. emaConf: BN;
  20. };
  21. export function isAccumulatorUpdateData(updateBytes: Buffer): boolean {
  22. return (
  23. updateBytes.toString("hex").slice(0, 8) === ACCUMULATOR_MAGIC &&
  24. updateBytes[4] === MAJOR_VERSION &&
  25. updateBytes[5] === MINOR_VERSION
  26. );
  27. }
  28. export function parsePriceFeedMessage(message: Buffer): PriceFeedMessage {
  29. let cursor = 0;
  30. const variant = message.readUInt8(cursor);
  31. if (variant !== PRICE_FEED_MESSAGE_VARIANT) {
  32. throw new Error("Not a price feed message");
  33. }
  34. cursor += 1;
  35. const feedId = message.subarray(cursor, cursor + 32);
  36. cursor += 32;
  37. const price = new BN(message.subarray(cursor, cursor + 8), "be");
  38. cursor += 8;
  39. const confidence = new BN(message.subarray(cursor, cursor + 8), "be");
  40. cursor += 8;
  41. const exponent = message.readInt32BE(cursor);
  42. cursor += 4;
  43. const publishTime = new BN(message.subarray(cursor, cursor + 8), "be");
  44. cursor += 8;
  45. const prevPublishTime = new BN(message.subarray(cursor, cursor + 8), "be");
  46. cursor += 8;
  47. const emaPrice = new BN(message.subarray(cursor, cursor + 8), "be");
  48. cursor += 8;
  49. const emaConf = new BN(message.subarray(cursor, cursor + 8), "be");
  50. cursor += 8;
  51. return {
  52. feedId,
  53. price,
  54. confidence,
  55. exponent,
  56. publishTime,
  57. prevPublishTime,
  58. emaPrice,
  59. emaConf,
  60. };
  61. }
  62. export function parseAccumulatorUpdateData(
  63. data: Buffer
  64. ): AccumulatorUpdateData {
  65. if (!isAccumulatorUpdateData(data)) {
  66. throw new Error("Invalid accumulator message");
  67. }
  68. let cursor = 6;
  69. const trailingPayloadSize = data.readUint8(cursor);
  70. cursor += 1 + trailingPayloadSize;
  71. // const proofType = data.readUint8(cursor);
  72. cursor += 1;
  73. const vaaSize = data.readUint16BE(cursor);
  74. cursor += 2;
  75. const vaa = data.subarray(cursor, cursor + vaaSize);
  76. cursor += vaaSize;
  77. const numUpdates = data.readUInt8(cursor);
  78. const updates = [];
  79. cursor += 1;
  80. for (let i = 0; i < numUpdates; i++) {
  81. const messageSize = data.readUint16BE(cursor);
  82. cursor += 2;
  83. const message = data.subarray(cursor, cursor + messageSize);
  84. cursor += messageSize;
  85. const numProofs = data.readUInt8(cursor);
  86. cursor += 1;
  87. const proof = [];
  88. for (let j = 0; j < numProofs; j++) {
  89. proof.push(
  90. Array.from(data.subarray(cursor, cursor + KECCAK160_HASH_SIZE))
  91. );
  92. cursor += KECCAK160_HASH_SIZE;
  93. }
  94. updates.push({ message, proof });
  95. }
  96. if (cursor !== data.length) {
  97. throw new Error("Didn't reach the end of the message");
  98. }
  99. return { vaa, updates };
  100. }