instruction.ts 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import { PublicKey, TransactionInstruction } from "@solana/web3.js";
  2. import { IdlAccount, IdlInstruction, IdlAccountItem } from "../../idl";
  3. import { IdlError } from "../../error";
  4. import Coder from "../../coder";
  5. import { toInstruction, validateAccounts } from "../common";
  6. import { RpcAccounts, splitArgsAndCtx } from "../context";
  7. /**
  8. * Dynamically generated instruction namespace.
  9. */
  10. export interface Ixs {
  11. [key: string]: IxFn;
  12. }
  13. /**
  14. * Ix is a function to create a `TransactionInstruction` generated from an IDL.
  15. */
  16. export type IxFn = IxProps & ((...args: any[]) => any);
  17. type IxProps = {
  18. accounts: (ctx: RpcAccounts) => any;
  19. };
  20. export default class InstructionNamespace {
  21. // Builds the instuction namespace.
  22. public static build(
  23. idlIx: IdlInstruction,
  24. coder: Coder,
  25. programId: PublicKey
  26. ): IxFn {
  27. if (idlIx.name === "_inner") {
  28. throw new IdlError("the _inner name is reserved");
  29. }
  30. const ix = (...args: any[]): TransactionInstruction => {
  31. const [ixArgs, ctx] = splitArgsAndCtx(idlIx, [...args]);
  32. validateAccounts(idlIx.accounts, ctx.accounts);
  33. validateInstruction(idlIx, ...args);
  34. const keys = InstructionNamespace.accountsArray(
  35. ctx.accounts,
  36. idlIx.accounts
  37. );
  38. if (ctx.remainingAccounts !== undefined) {
  39. keys.push(...ctx.remainingAccounts);
  40. }
  41. if (ctx.__private && ctx.__private.logAccounts) {
  42. console.log("Outgoing account metas:", keys);
  43. }
  44. return new TransactionInstruction({
  45. keys,
  46. programId,
  47. data: coder.instruction.encode(
  48. idlIx.name,
  49. toInstruction(idlIx, ...ixArgs)
  50. ),
  51. });
  52. };
  53. // Utility fn for ordering the accounts for this instruction.
  54. ix["accounts"] = (accs: RpcAccounts) => {
  55. return InstructionNamespace.accountsArray(accs, idlIx.accounts);
  56. };
  57. return ix;
  58. }
  59. public static accountsArray(
  60. ctx: RpcAccounts,
  61. accounts: IdlAccountItem[]
  62. ): any {
  63. return accounts
  64. .map((acc: IdlAccountItem) => {
  65. // Nested accounts.
  66. // @ts-ignore
  67. const nestedAccounts: IdlAccountItem[] | undefined = acc.accounts;
  68. if (nestedAccounts !== undefined) {
  69. const rpcAccs = ctx[acc.name] as RpcAccounts;
  70. return InstructionNamespace.accountsArray(
  71. rpcAccs,
  72. nestedAccounts
  73. ).flat();
  74. } else {
  75. const account: IdlAccount = acc as IdlAccount;
  76. return {
  77. pubkey: ctx[acc.name],
  78. isWritable: account.isMut,
  79. isSigner: account.isSigner,
  80. };
  81. }
  82. })
  83. .flat();
  84. }
  85. }
  86. // Throws error if any argument required for the `ix` is not given.
  87. function validateInstruction(ix: IdlInstruction, ...args: any[]) {
  88. // todo
  89. }