simulate.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. import { PublicKey } from "@solana/web3.js";
  2. import Provider from "../../provider";
  3. import { IdlInstruction } from "../../idl";
  4. import { splitArgsAndCtx } from "../context";
  5. import { TransactionFn } from "./transaction";
  6. import { EventParser } from "../event";
  7. import Coder from "../../coder";
  8. import { Idl } from "../../idl";
  9. import { ProgramError } from "../../error";
  10. export default class SimulateFactory {
  11. public static build(
  12. idlIx: IdlInstruction,
  13. txFn: TransactionFn,
  14. idlErrors: Map<number, string>,
  15. provider: Provider,
  16. coder: Coder,
  17. programId: PublicKey,
  18. idl: Idl
  19. ): SimulateFn {
  20. const simulate = async (...args: any[]): Promise<SimulateResponse> => {
  21. const tx = txFn(...args);
  22. const [, ctx] = splitArgsAndCtx(idlIx, [...args]);
  23. let resp = undefined;
  24. try {
  25. resp = await provider.simulate(tx, ctx.signers, ctx.options);
  26. } catch (err) {
  27. console.log("Translating error", err);
  28. let translatedErr = ProgramError.parse(err, idlErrors);
  29. if (translatedErr === null) {
  30. throw err;
  31. }
  32. throw translatedErr;
  33. }
  34. if (resp === undefined) {
  35. throw new Error("Unable to simulate transaction");
  36. }
  37. if (resp.value.err) {
  38. throw new Error(`Simulate error: ${resp.value.err.toString()}`);
  39. }
  40. const logs = resp.value.logs;
  41. if (!logs) {
  42. throw new Error("Simulated logs not found");
  43. }
  44. const events = [];
  45. if (idl.events) {
  46. let parser = new EventParser(coder, programId);
  47. parser.parseLogs(logs, (event) => {
  48. events.push(event);
  49. });
  50. }
  51. return { events, raw: logs };
  52. };
  53. return simulate;
  54. }
  55. }
  56. /**
  57. * The namespace provides functions to simulate transactions for each method
  58. * of a program, returning a list of deserialized events *and* raw program
  59. * logs.
  60. *
  61. * One can use this to read data calculated from a program on chain, by
  62. * emitting an event in the program and reading the emitted event client side
  63. * via the `simulate` namespace.
  64. *
  65. * ## Usage
  66. *
  67. * ```javascript
  68. * program.simulate.<method>(...args, ctx);
  69. * ```
  70. *
  71. * ## Parameters
  72. *
  73. * 1. `args` - The positional arguments for the program. The type and number
  74. * of these arguments depend on the program being used.
  75. * 2. `ctx` - [[Context]] non-argument parameters to pass to the method.
  76. * Always the last parameter in the method call.
  77. *
  78. * ## Example
  79. *
  80. * To simulate the `increment` method above,
  81. *
  82. * ```javascript
  83. * const events = await program.simulate.increment({
  84. * accounts: {
  85. * counter,
  86. * },
  87. * });
  88. * ```
  89. */
  90. export interface SimulateNamespace {
  91. [key: string]: SimulateFn;
  92. }
  93. /**
  94. * RpcFn is a single method generated from an IDL. It simulates a method
  95. * against a cluster configured by the provider, returning a list of all the
  96. * events and raw logs that were emitted during the execution of the
  97. * method.
  98. */
  99. export type SimulateFn = (...args: any[]) => Promise<SimulateResponse>;
  100. type SimulateResponse = {
  101. events: Event[];
  102. raw: string[];
  103. };