event.ts 2.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import { Buffer } from "buffer";
  2. import * as base64 from "base64-js";
  3. import { Layout } from "buffer-layout";
  4. import { sha256 } from "js-sha256";
  5. import { Idl, IdlEvent, IdlTypeDef } from "../idl.js";
  6. import { Event, EventData } from "../program/event.js";
  7. import { IdlCoder } from "./idl.js";
  8. export class EventCoder {
  9. /**
  10. * Maps account type identifier to a layout.
  11. */
  12. private layouts: Map<string, Layout>;
  13. /**
  14. * Maps base64 encoded event discriminator to event name.
  15. */
  16. private discriminators: Map<string, string>;
  17. public constructor(idl: Idl) {
  18. if (idl.events === undefined) {
  19. this.layouts = new Map();
  20. return;
  21. }
  22. const layouts: [string, Layout<any>][] = idl.events.map((event) => {
  23. let eventTypeDef: IdlTypeDef = {
  24. name: event.name,
  25. type: {
  26. kind: "struct",
  27. fields: event.fields.map((f) => {
  28. return { name: f.name, type: f.type };
  29. }),
  30. },
  31. };
  32. return [event.name, IdlCoder.typeDefLayout(eventTypeDef, idl.types)];
  33. });
  34. this.layouts = new Map(layouts);
  35. this.discriminators = new Map<string, string>(
  36. idl.events === undefined
  37. ? []
  38. : idl.events.map((e) => [
  39. base64.fromByteArray(eventDiscriminator(e.name)),
  40. e.name,
  41. ])
  42. );
  43. }
  44. public decode<E extends IdlEvent = IdlEvent, T = Record<string, never>>(
  45. log: string
  46. ): Event<E, T> | null {
  47. let logArr: Buffer;
  48. // This will throw if log length is not a multiple of 4.
  49. try {
  50. logArr = Buffer.from(base64.toByteArray(log));
  51. } catch (e) {
  52. return null;
  53. }
  54. const disc = base64.fromByteArray(logArr.slice(0, 8));
  55. // Only deserialize if the discriminator implies a proper event.
  56. const eventName = this.discriminators.get(disc);
  57. if (eventName === undefined) {
  58. return null;
  59. }
  60. const layout = this.layouts.get(eventName);
  61. if (!layout) {
  62. throw new Error(`Unknown event: ${eventName}`);
  63. }
  64. const data = layout.decode(logArr.slice(8)) as EventData<
  65. E["fields"][number],
  66. T
  67. >;
  68. return { data, name: eventName };
  69. }
  70. }
  71. export function eventDiscriminator(name: string): Buffer {
  72. return Buffer.from(sha256.digest(`event:${name}`)).slice(0, 8);
  73. }