rpc.ts 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. import assert from "assert";
  2. import {
  3. AccountInfo,
  4. AccountMeta,
  5. Connection,
  6. PublicKey,
  7. TransactionSignature,
  8. Transaction,
  9. TransactionInstruction,
  10. } from "@solana/web3.js";
  11. import { chunks } from "../utils/common";
  12. import { Address, translateAddress } from "../program/common";
  13. import Provider, { getProvider } from "../provider";
  14. /**
  15. * Sends a transaction to a program with the given accounts and instruction
  16. * data.
  17. */
  18. export async function invoke(
  19. programId: Address,
  20. accounts?: Array<AccountMeta>,
  21. data?: Buffer,
  22. provider?: Provider
  23. ): Promise<TransactionSignature> {
  24. programId = translateAddress(programId);
  25. if (!provider) {
  26. provider = getProvider();
  27. }
  28. const tx = new Transaction();
  29. tx.add(
  30. new TransactionInstruction({
  31. programId,
  32. keys: accounts ?? [],
  33. data,
  34. })
  35. );
  36. return await provider.send(tx);
  37. }
  38. const GET_MULTIPLE_ACCOUNTS_LIMIT: number = 99;
  39. export async function getMultipleAccounts(
  40. connection: Connection,
  41. publicKeys: PublicKey[]
  42. ): Promise<
  43. Array<null | { publicKey: PublicKey; account: AccountInfo<Buffer> }>
  44. > {
  45. if (publicKeys.length <= GET_MULTIPLE_ACCOUNTS_LIMIT) {
  46. return await getMultipleAccountsCore(connection, publicKeys);
  47. } else {
  48. const batches = chunks(publicKeys, GET_MULTIPLE_ACCOUNTS_LIMIT);
  49. const results = await Promise.all<
  50. Array<null | { publicKey: PublicKey; account: AccountInfo<Buffer> }>
  51. >(batches.map((batch) => getMultipleAccountsCore(connection, batch)));
  52. return results.flat();
  53. }
  54. }
  55. async function getMultipleAccountsCore(
  56. connection: Connection,
  57. publicKeys: PublicKey[]
  58. ): Promise<
  59. Array<null | { publicKey: PublicKey; account: AccountInfo<Buffer> }>
  60. > {
  61. const args = [publicKeys.map((k) => k.toBase58()), { commitment: "recent" }];
  62. // @ts-ignore
  63. const res = await connection._rpcRequest("getMultipleAccounts", args);
  64. if (res.error) {
  65. throw new Error(
  66. "failed to get info about accounts " +
  67. publicKeys.map((k) => k.toBase58()).join(", ") +
  68. ": " +
  69. res.error.message
  70. );
  71. }
  72. assert(typeof res.result !== "undefined");
  73. const accounts: Array<null | {
  74. executable: any;
  75. owner: PublicKey;
  76. lamports: any;
  77. data: Buffer;
  78. }> = [];
  79. for (const account of res.result.value) {
  80. let value: {
  81. executable: any;
  82. owner: PublicKey;
  83. lamports: any;
  84. data: Buffer;
  85. } | null = null;
  86. if (account === null) {
  87. accounts.push(null);
  88. continue;
  89. }
  90. if (res.result.value) {
  91. const { executable, owner, lamports, data } = account;
  92. assert(data[1] === "base64");
  93. value = {
  94. executable,
  95. owner: new PublicKey(owner),
  96. lamports,
  97. data: Buffer.from(data[0], "base64"),
  98. };
  99. }
  100. if (value === null) {
  101. throw new Error("Invalid response");
  102. }
  103. accounts.push(value);
  104. }
  105. return accounts.map((account, idx) => {
  106. if (account === null) {
  107. return null;
  108. }
  109. return {
  110. publicKey: publicKeys[idx],
  111. account,
  112. };
  113. });
  114. }