provider.ts 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import {
  2. Connection,
  3. Account,
  4. PublicKey,
  5. Transaction,
  6. TransactionSignature,
  7. ConfirmOptions,
  8. sendAndConfirmRawTransaction,
  9. } from "@solana/web3.js";
  10. export default class Provider {
  11. constructor(
  12. readonly connection: Connection,
  13. readonly wallet: Wallet,
  14. readonly opts: ConfirmOptions
  15. ) {}
  16. static defaultOptions(): ConfirmOptions {
  17. return {
  18. preflightCommitment: "recent",
  19. commitment: "recent",
  20. };
  21. }
  22. // Node only api.
  23. static local(url?: string, opts?: ConfirmOptions): Provider {
  24. opts = opts || Provider.defaultOptions();
  25. const connection = new Connection(
  26. url || "http://localhost:8899",
  27. opts.preflightCommitment
  28. );
  29. const wallet = NodeWallet.local();
  30. return new Provider(connection, wallet, opts);
  31. }
  32. // Node only api.
  33. static env(): Provider {
  34. const process = require("process");
  35. const url = process.env.ANCHOR_PROVIDER_URL;
  36. if (url === undefined) {
  37. throw new Error("ANCHOR_PROVIDER_URL is not defined");
  38. }
  39. const options = Provider.defaultOptions();
  40. const connection = new Connection(url, options.commitment);
  41. const wallet = NodeWallet.local();
  42. return new Provider(connection, wallet, options);
  43. }
  44. async send(
  45. tx: Transaction,
  46. signers?: Array<Account | undefined>,
  47. opts?: ConfirmOptions
  48. ): Promise<TransactionSignature> {
  49. if (signers === undefined) {
  50. signers = [];
  51. }
  52. if (opts === undefined) {
  53. opts = this.opts;
  54. }
  55. const signerKps = signers.filter((s) => s !== undefined) as Array<Account>;
  56. const signerPubkeys = [this.wallet.publicKey].concat(
  57. signerKps.map((s) => s.publicKey)
  58. );
  59. tx.setSigners(...signerPubkeys);
  60. tx.recentBlockhash = (
  61. await this.connection.getRecentBlockhash(opts.preflightCommitment)
  62. ).blockhash;
  63. await this.wallet.signTransaction(tx);
  64. signerKps.forEach((kp) => {
  65. tx.partialSign(kp);
  66. });
  67. const rawTx = tx.serialize();
  68. const txId = await sendAndConfirmRawTransaction(
  69. this.connection,
  70. rawTx,
  71. opts
  72. );
  73. return txId;
  74. }
  75. async sendAll(
  76. reqs: Array<SendTxRequest>,
  77. opts?: ConfirmOptions
  78. ): Promise<Array<TransactionSignature>> {
  79. if (opts === undefined) {
  80. opts = this.opts;
  81. }
  82. const blockhash = await this.connection.getRecentBlockhash(
  83. opts.preflightCommitment
  84. );
  85. let txs = reqs.map((r) => {
  86. let tx = r.tx;
  87. let signers = r.signers;
  88. if (signers === undefined) {
  89. signers = [];
  90. }
  91. const signerKps = signers.filter(
  92. (s) => s !== undefined
  93. ) as Array<Account>;
  94. const signerPubkeys = [this.wallet.publicKey].concat(
  95. signerKps.map((s) => s.publicKey)
  96. );
  97. tx.setSigners(...signerPubkeys);
  98. tx.recentBlockhash = blockhash.blockhash;
  99. signerKps.forEach((kp) => {
  100. tx.partialSign(kp);
  101. });
  102. return tx;
  103. });
  104. const signedTxs = await this.wallet.signAllTransactions(txs);
  105. const sigs = [];
  106. for (let k = 0; k < txs.length; k += 1) {
  107. const tx = signedTxs[k];
  108. const rawTx = tx.serialize();
  109. sigs.push(
  110. await sendAndConfirmRawTransaction(this.connection, rawTx, opts)
  111. );
  112. }
  113. return sigs;
  114. }
  115. }
  116. export type SendTxRequest = {
  117. tx: Transaction;
  118. signers: Array<Account | undefined>;
  119. };
  120. export interface Wallet {
  121. signTransaction(tx: Transaction): Promise<Transaction>;
  122. signAllTransactions(txs: Transaction[]): Promise<Transaction[]>;
  123. publicKey: PublicKey;
  124. }
  125. export class NodeWallet implements Wallet {
  126. constructor(readonly payer: Account) {}
  127. static local(): NodeWallet {
  128. const payer = new Account(
  129. Buffer.from(
  130. JSON.parse(
  131. require("fs").readFileSync(
  132. require("os").homedir() + "/.config/solana/id.json",
  133. {
  134. encoding: "utf-8",
  135. }
  136. )
  137. )
  138. )
  139. );
  140. return new NodeWallet(payer);
  141. }
  142. async signTransaction(tx: Transaction): Promise<Transaction> {
  143. tx.partialSign(this.payer);
  144. return tx;
  145. }
  146. async signAllTransactions(txs: Transaction[]): Promise<Transaction[]> {
  147. return txs.map((t) => {
  148. t.partialSign(this.payer);
  149. return t;
  150. });
  151. }
  152. get publicKey(): PublicKey {
  153. return this.payer.publicKey;
  154. }
  155. }