simulate.ts 3.9 KB

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