simulate.ts 3.8 KB

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