Преглед на файлове

ts: make `Provider` an interface, add `AnchorProvider` class, and update provider functions (#1707)

Paul преди 3 години
родител
ревизия
537d470954
променени са 60 файла, в които са добавени 399 реда и са изтрити 160 реда
  1. 3 0
      .gitmodules
  2. 1 0
      CHANGELOG.md
  3. 1 1
      examples/tutorial/basic-0/client.js
  4. 1 1
      examples/tutorial/basic-0/tests/basic-0.js
  5. 1 1
      examples/tutorial/basic-1/tests/basic-1.js
  6. 1 1
      examples/tutorial/basic-2/tests/basic-2.js
  7. 1 1
      examples/tutorial/basic-3/tests/basic-3.js
  8. 1 1
      examples/tutorial/basic-4/tests/basic-4.js
  9. 1 1
      tests/auction-house
  10. 1 1
      tests/bpf-upgradeable-state/tests/bpf-upgradable-state.ts
  11. 5 1
      tests/cashiers-check/tests/cashiers-check.js
  12. 1 1
      tests/cfo/deps/stake
  13. 1 1
      tests/cfo/deps/swap
  14. 1 1
      tests/cfo/scripts/fees.js
  15. 4 1
      tests/cfo/scripts/list-market.js
  16. 1 1
      tests/cfo/scripts/trade-bot.js
  17. 5 1
      tests/cfo/tests/cfo.js
  18. 12 6
      tests/cfo/tests/utils/index.js
  19. 4 1
      tests/cfo/tests/utils/stake.js
  20. 1 1
      tests/chat/tests/chat.js
  21. 1 1
      tests/composite/tests/composite.js
  22. 1 1
      tests/cpi-returns/tests/cpi-return.ts
  23. 1 1
      tests/custom-coder/tests/custom-coder.ts
  24. 1 1
      tests/declare-id/tests/declare-id.ts
  25. 2 2
      tests/errors/tests/errors.ts
  26. 1 1
      tests/escrow/tests/escrow.ts
  27. 1 1
      tests/events/tests/events.js
  28. 1 1
      tests/floats/tests/floats.ts
  29. 3 3
      tests/ido-pool/tests/ido-pool.js
  30. 1 1
      tests/interface/tests/interface.js
  31. 4 1
      tests/lockup/tests/lockup.js
  32. 1 1
      tests/misc/miscNonRentExempt.ts
  33. 2 2
      tests/misc/tests/misc.ts
  34. 1 1
      tests/multiple-suites/tests/another-suite/another-suite.ts
  35. 1 1
      tests/multiple-suites/tests/fourth-and-fifth-suite/fifth-suite/fifthSuite.ts
  36. 1 1
      tests/multiple-suites/tests/fourth-and-fifth-suite/forth-suite/forth-suite.ts
  37. 1 1
      tests/multiple-suites/tests/multiple-suites/multiple-suites.ts
  38. 1 1
      tests/multiple-suites/tests/third-suite/sub-dir-one/subDirOne.ts
  39. 1 1
      tests/multiple-suites/tests/third-suite/sub-dir-two/subDirTwo.ts
  40. 1 1
      tests/multisig/tests/multisig.js
  41. 1 1
      tests/pda-derivation/tests/typescript.spec.ts
  42. 1 1
      tests/pyth/tests/pyth.spec.ts
  43. 1 1
      tests/safety-checks/tests/safety-checks.ts
  44. 3 3
      tests/spl/token-proxy/tests/token-proxy.js
  45. 5 1
      tests/swap/tests/swap.js
  46. 10 4
      tests/swap/tests/utils/index.js
  47. 1 1
      tests/system-accounts/tests/system-accounts.js
  48. 1 1
      tests/sysvars/tests/sysvars.js
  49. 1 1
      tests/tictactoe/tests/tictactoe.js
  50. 1 1
      tests/typescript/tests/typescript.spec.ts
  51. 1 1
      tests/validator-clone/tests/validator-clone.ts
  52. 1 1
      tests/zero-copy/tests/zero-copy.js
  53. 6 1
      ts/src/index.ts
  54. 7 0
      ts/src/program/accounts-resolver.ts
  55. 8 0
      ts/src/program/namespace/account.ts
  56. 10 1
      ts/src/program/namespace/rpc.ts
  57. 14 13
      ts/src/program/namespace/simulate.ts
  58. 7 0
      ts/src/program/namespace/state.ts
  59. 77 79
      ts/src/provider.ts
  60. 169 2
      ts/src/utils/rpc.ts

+ 3 - 0
.gitmodules

@@ -7,12 +7,15 @@
 [submodule "examples/cfo/deps/swap"]
 	path = tests/cfo/deps/swap
 	url = https://github.com/project-serum/swap.git
+	branch = armani/cfo
 [submodule "examples/cfo/deps/stake"]
 	path = tests/cfo/deps/stake
 	url = https://github.com/project-serum/stake.git
+	branch = armani/cfo
 [submodule "examples/permissioned-markets/deps/serum-dex"]
 	path = tests/permissioned-markets/deps/serum-dex
 	url = https://github.com/project-serum/serum-dex
 [submodule "tests/auction-house"]
 	path = tests/auction-house
 	url = https://github.com/armaniferrante/auction-house
+	branch = armani/pda

+ 1 - 0
CHANGELOG.md

@@ -35,6 +35,7 @@ The minor version will be incremented upon a breaking change and the patch versi
 * spl: Re-export the `spl_token` crate ([#1665](https://github.com/project-serum/anchor/pull/1665)).
 * lang, cli, spl: Update solana toolchain to v1.9.13 ([#1653](https://github.com/project-serum/anchor/pull/1653) and [#1751](https://github.com/project-serum/anchor/pull/1751)).
 * lang: `Program` type now deserializes `programdata_address` only on demand ([#1723](https://github.com/project-serum/anchor/pull/1723)).
+* ts: Make `Provider` an interface and adjust its signatures and add `AnchorProvider` implementor class ([#1707](https://github.com/project-serum/anchor/pull/1707)).
 * spl: Change "to" to "from" in `token::burn` ([#1080](https://github.com/project-serum/anchor/pull/1080)).
 
 ## [0.23.0] - 2022-03-20

+ 1 - 1
examples/tutorial/basic-0/client.js

@@ -5,7 +5,7 @@
 const anchor = require("@project-serum/anchor");
 
 // Configure the local cluster.
-anchor.setProvider(anchor.Provider.local());
+anchor.setProvider(anchor.AnchorProvider.local());
 
 async function main() {
   // #region main

+ 1 - 1
examples/tutorial/basic-0/tests/basic-0.js

@@ -2,7 +2,7 @@ const anchor = require("@project-serum/anchor");
 
 describe("basic-0", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.local());
+  anchor.setProvider(anchor.AnchorProvider.local());
 
   it("Uses the workspace to invoke the initialize instruction", async () => {
     // #region code

+ 1 - 1
examples/tutorial/basic-1/tests/basic-1.js

@@ -4,7 +4,7 @@ const { SystemProgram } = anchor.web3;
 
 describe("basic-1", () => {
   // Use a local provider.
-  const provider = anchor.Provider.local();
+  const provider = anchor.AnchorProvider.local();
 
   // Configure the client to use the local cluster.
   anchor.setProvider(provider);

+ 1 - 1
examples/tutorial/basic-2/tests/basic-2.js

@@ -3,7 +3,7 @@ const anchor = require("@project-serum/anchor");
 const { SystemProgram } = anchor.web3;
 
 describe("basic-2", () => {
-  const provider = anchor.Provider.local();
+  const provider = anchor.AnchorProvider.local();
 
   // Configure the client to use the local cluster.
   anchor.setProvider(provider);

+ 1 - 1
examples/tutorial/basic-3/tests/basic-3.js

@@ -3,7 +3,7 @@ const anchor = require("@project-serum/anchor");
 const { SystemProgram } = anchor.web3;
 
 describe("basic-3", () => {
-  const provider = anchor.Provider.local();
+  const provider = anchor.AnchorProvider.local();
 
   // Configure the client to use the local cluster.
   anchor.setProvider(provider);

+ 1 - 1
examples/tutorial/basic-4/tests/basic-4.js

@@ -2,7 +2,7 @@ const assert = require("assert");
 const anchor = require("@project-serum/anchor");
 
 describe("basic-4", () => {
-  const provider = anchor.Provider.local();
+  const provider = anchor.AnchorProvider.local();
 
   // Configure the client to use the local cluster.
   anchor.setProvider(provider);

+ 1 - 1
tests/auction-house

@@ -1 +1 @@
-Subproject commit 967650c531ba0f23c88374875ccfcecb9b1a7800
+Subproject commit 2d4d9583467d697267b87b250af8c8bd360f3745

+ 1 - 1
tests/bpf-upgradeable-state/tests/bpf-upgradable-state.ts

@@ -6,7 +6,7 @@ import { assert } from "chai";
 import { BpfUpgradeableState } from "../target/types/bpf_upgradeable_state";
 
 describe("bpf_upgradeable_state", () => {
-  const provider = anchor.Provider.env();
+  const provider = anchor.AnchorProvider.env();
   // Configure the client to use the local cluster.
   anchor.setProvider(provider);
 

+ 5 - 1
tests/cashiers-check/tests/cashiers-check.js

@@ -5,7 +5,11 @@ const { TOKEN_PROGRAM_ID } = require("@solana/spl-token");
 
 describe("cashiers-check", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.env());
+  const provider = anchor.AnchorProvider.env();
+  // hack so we don't have to update serum-common library
+  // to the new AnchorProvider class and Provider interface
+  provider.send = provider.sendAndConfirm;
+  anchor.setProvider(provider);
 
   const program = anchor.workspace.CashiersCheck;
 

+ 1 - 1
tests/cfo/deps/stake

@@ -1 +1 @@
-Subproject commit 9c41642dffbb334e1e39c616cd6a645d91768d3e
+Subproject commit b68b9a6fdea2c8befe95aa0f1fcca394579fc3bd

+ 1 - 1
tests/cfo/deps/swap

@@ -1 +1 @@
-Subproject commit 3da36aaae7af6ce901d68c0280aac34817fe7fd8
+Subproject commit 2ac6045cf37436c4702fbe12678f3a6243feecb1

+ 1 - 1
tests/cfo/scripts/fees.js

@@ -6,7 +6,7 @@ const anchor = require("@project-serum/anchor");
 const { Market, OpenOrders } = require("@project-serum/serum");
 const Account = anchor.web3.Account;
 const Program = anchor.Program;
-const provider = anchor.Provider.local();
+const provider = anchor.AnchorProvider.local();
 const secret = JSON.parse(fs.readFileSync("./scripts/market-maker.json"));
 const MARKET_MAKER = new Account(secret);
 const PublicKey = anchor.web3.PublicKey;

+ 4 - 1
tests/cfo/scripts/list-market.js

@@ -5,7 +5,10 @@
 const utils = require("../tests/utils");
 const fs = require("fs");
 const anchor = require("@project-serum/anchor");
-const provider = anchor.Provider.local();
+const provider = anchor.AnchorProvider.local();
+// hack so we don't have to update serum-common library
+// to the new AnchorProvider class and Provider interface
+provider.send = provider.sendAndConfirm;
 
 async function main() {
   ORDERBOOK_ENV = await utils.initMarket({

+ 1 - 1
tests/cfo/scripts/trade-bot.js

@@ -9,7 +9,7 @@ const { runTradeBot } = require("../tests/utils");
 
 async function main() {
   const market = new PublicKey(process.argv[2]);
-  const provider = anchor.Provider.local();
+  const provider = anchor.AnchorProvider.local();
   runTradeBot(market, provider);
 }
 

+ 5 - 1
tests/cfo/tests/cfo.js

@@ -23,9 +23,13 @@ const SYSVAR_INSTRUCTIONS_PUBKEY = new PublicKey(
 const FEES = "6160355581";
 
 describe("cfo", () => {
-  anchor.setProvider(anchor.Provider.env());
+  const provider = anchor.AnchorProvider.env();
+  anchor.setProvider(provider);
 
   const program = anchor.workspace.Cfo;
+  // hack so we don't have to update serum-common library
+  // to the new AnchorProvider class and Provider interface
+  program.provider.send = provider.sendAndConfirm;
   const sweepAuthority = program.provider.wallet.publicKey;
   let officer, srmVault, usdcVault, bVault, stake, treasury;
   let officerBump, srmBump, usdcBump, bBump, stakeBump, treasuryBump;

+ 12 - 6
tests/cfo/tests/utils/index.js

@@ -125,7 +125,7 @@ async function fundAccount({ provider, mints }) {
   };
 
   // Transfer lamports to market maker.
-  await provider.send(
+  await provider.sendAndConfirm(
     (() => {
       const tx = new Transaction();
       tx.add(
@@ -155,7 +155,7 @@ async function fundAccount({ provider, mints }) {
       MARKET_MAKER.publicKey
     );
 
-    await provider.send(
+    await provider.sendAndConfirm(
       (() => {
         const tx = new Transaction();
         tx.add(
@@ -220,7 +220,10 @@ async function setupMarket({
         feeDiscountPubkey: null,
         selfTradeBehavior: "abortTransaction",
       });
-    await provider.send(transaction, signers.concat(marketMaker.account));
+    await provider.sendAndConfirm(
+      transaction,
+      signers.concat(marketMaker.account)
+    );
   }
 
   for (let k = 0; k < bids.length; k += 1) {
@@ -239,7 +242,10 @@ async function setupMarket({
         feeDiscountPubkey: null,
         selfTradeBehavior: "abortTransaction",
       });
-    await provider.send(transaction, signers.concat(marketMaker.account));
+    await provider.sendAndConfirm(
+      transaction,
+      signers.concat(marketMaker.account)
+    );
   }
 
   return [MARKET_A_USDC, vaultOwner];
@@ -527,7 +533,7 @@ async function runTradeBot(market, provider, iterations = undefined) {
         feeDiscountPubkey: null,
         selfTradeBehavior: "abortTransaction",
       });
-    let txSig = await provider.send(tx_ask, sigs_ask.concat(maker));
+    let txSig = await provider.sendAndConfirm(tx_ask, sigs_ask.concat(maker));
     console.log("Ask", txSig);
 
     // Take.
@@ -545,7 +551,7 @@ async function runTradeBot(market, provider, iterations = undefined) {
         feeDiscountPubkey: null,
         selfTradeBehavior: "abortTransaction",
       });
-    txSig = await provider.send(tx_bid, sigs_bid.concat(taker));
+    txSig = await provider.sendAndConfirm(tx_bid, sigs_bid.concat(taker));
     console.log("Bid", txSig);
 
     await sleep(1000);

+ 4 - 1
tests/cfo/tests/utils/stake.js

@@ -5,7 +5,10 @@ const utils = require("../../deps/stake/tests/utils");
 
 const lockup = anchor.workspace.Lockup;
 const registry = anchor.workspace.Registry;
-const provider = anchor.Provider.env();
+const provider = anchor.AnchorProvider.env();
+// hack so we don't have to update serum-common library
+// to the new AnchorProvider class and Provider interface
+provider.send = provider.sendAndConfirm;
 
 let lockupAddress = null;
 let mint = null;

+ 1 - 1
tests/chat/tests/chat.js

@@ -4,7 +4,7 @@ const { PublicKey } = anchor.web3;
 
 describe("chat", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.env());
+  anchor.setProvider(anchor.AnchorProvider.env());
 
   // Program client handle.
   const program = anchor.workspace.Chat;

+ 1 - 1
tests/composite/tests/composite.js

@@ -2,7 +2,7 @@ const { assert } = require("chai");
 const anchor = require("@project-serum/anchor");
 
 describe("composite", () => {
-  const provider = anchor.Provider.local();
+  const provider = anchor.AnchorProvider.local();
 
   // Configure the client to use the local cluster.
   anchor.setProvider(provider);

+ 1 - 1
tests/cpi-returns/tests/cpi-return.ts

@@ -8,7 +8,7 @@ import { Caller } from "../target/types/caller";
 const { SystemProgram } = anchor.web3;
 
 describe("CPI return", () => {
-  const provider = anchor.Provider.env();
+  const provider = anchor.AnchorProvider.env();
   anchor.setProvider(provider);
 
   const callerProgram = anchor.workspace.Caller as Program<Caller>;

+ 1 - 1
tests/custom-coder/tests/custom-coder.ts

@@ -6,7 +6,7 @@ import { Keypair, SYSVAR_RENT_PUBKEY } from "@solana/web3.js";
 
 describe("custom-coder", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.env());
+  anchor.setProvider(anchor.AnchorProvider.env());
 
   // Client.
   const program = Spl.token();

+ 1 - 1
tests/declare-id/tests/declare-id.ts

@@ -5,7 +5,7 @@ import { DeclareId } from "../target/types/declare_id";
 import { assert } from "chai";
 
 describe("declare_id", () => {
-  anchor.setProvider(anchor.Provider.local());
+  anchor.setProvider(anchor.AnchorProvider.local());
   const program = anchor.workspace.DeclareId as Program<DeclareId>;
 
   it("throws error!", async () => {

+ 2 - 2
tests/errors/tests/errors.ts

@@ -64,7 +64,7 @@ const withLogTest = async (callback, expectedLogs) => {
 
 describe("errors", () => {
   // Configure the client to use the local cluster.
-  const localProvider = anchor.Provider.local();
+  const localProvider = anchor.AnchorProvider.local();
   localProvider.opts.skipPreflight = true;
   // processed failed tx do not result in AnchorErrors in the client
   // because we cannot get logs for them (only through overkill `onLogs`)
@@ -279,7 +279,7 @@ describe("errors", () => {
           data: program.coder.instruction.encode("signer_error", {}),
         })
       );
-      await program.provider.send(tx);
+      await program.provider.sendAndConfirm(tx);
       assert.ok(false);
     } catch (err) {
       anchor.getProvider().connection.removeOnLogsListener(listener);

+ 1 - 1
tests/escrow/tests/escrow.ts

@@ -8,7 +8,7 @@ import { Escrow } from "../target/types/escrow";
 type EscrowAccount = IdlAccounts<Escrow>["escrowAccount"];
 
 describe("escrow", () => {
-  const provider = anchor.Provider.env();
+  const provider = anchor.AnchorProvider.env();
   anchor.setProvider(provider);
 
   const program = anchor.workspace.Escrow as Program<Escrow>;

+ 1 - 1
tests/events/tests/events.js

@@ -3,7 +3,7 @@ const { assert } = require("chai");
 
 describe("events", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.env());
+  anchor.setProvider(anchor.AnchorProvider.env());
   const program = anchor.workspace.Events;
 
   it("Is initialized!", async () => {

+ 1 - 1
tests/floats/tests/floats.ts

@@ -6,7 +6,7 @@ import { assert } from "chai";
 
 describe("floats", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.env());
+  anchor.setProvider(anchor.AnchorProvider.env());
 
   const program = anchor.workspace.Floats as Program<Floats>;
 

+ 3 - 3
tests/ido-pool/tests/ido-pool.js

@@ -14,7 +14,7 @@ const {
 const { token } = require("@project-serum/anchor/dist/cjs/utils");
 
 describe("ido-pool", () => {
-  const provider = anchor.Provider.local();
+  const provider = anchor.AnchorProvider.local();
 
   // Configure the client to use the local cluster.
   anchor.setProvider(provider);
@@ -182,7 +182,7 @@ describe("ido-pool", () => {
     let createUserUsdcTrns = new anchor.web3.Transaction().add(
       createUserUsdcInstr
     );
-    await provider.send(createUserUsdcTrns);
+    await provider.sendAndConfirm(createUserUsdcTrns);
     await usdcMintAccount.mintTo(
       userUsdc,
       provider.wallet.publicKey,
@@ -283,7 +283,7 @@ describe("ido-pool", () => {
     let createSecondUserUsdcTrns = new anchor.web3.Transaction();
     createSecondUserUsdcTrns.add(transferSolInstr);
     createSecondUserUsdcTrns.add(createSecondUserUsdcInstr);
-    await provider.send(createSecondUserUsdcTrns);
+    await provider.sendAndConfirm(createSecondUserUsdcTrns);
     await usdcMintAccount.mintTo(
       secondUserUsdc,
       provider.wallet.publicKey,

+ 1 - 1
tests/interface/tests/interface.js

@@ -4,7 +4,7 @@ const nativeAssert = require("assert");
 
 describe("interface", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.env());
+  anchor.setProvider(anchor.AnchorProvider.env());
 
   const counter = anchor.workspace.Counter;
   const counterAuth = anchor.workspace.CounterAuth;

+ 4 - 1
tests/lockup/tests/lockup.js

@@ -9,7 +9,10 @@ anchor.utils.features.set("anchor-deprecated-state");
 
 describe("Lockup and Registry", () => {
   // Read the provider from the configured environmnet.
-  const provider = anchor.Provider.env();
+  const provider = anchor.AnchorProvider.env();
+  // hack so we don't have to update serum-common library
+  // to the new AnchorProvider class and Provider interface
+  provider.send = provider.sendAndConfirm;
 
   // Configure the client to use the provider.
   anchor.setProvider(provider);

+ 1 - 1
tests/misc/miscNonRentExempt.ts

@@ -11,7 +11,7 @@ const { assert } = require("chai");
 
 describe("miscNonRentExempt", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.env());
+  anchor.setProvider(anchor.AnchorProvider.env());
   const program = anchor.workspace.Misc as Program<Misc>;
 
   it("init_if_needed checks rent_exemption if init is not needed", async () => {

+ 2 - 2
tests/misc/tests/misc.ts

@@ -20,7 +20,7 @@ const miscIdl = require("../target/idl/misc.json");
 
 describe("misc", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.env());
+  anchor.setProvider(anchor.AnchorProvider.env());
   const program = anchor.workspace.Misc as Program<Misc>;
   const misc2Program = anchor.workspace.Misc2 as Program<Misc2>;
 
@@ -760,7 +760,7 @@ describe("misc", () => {
     const anotherProgram = new anchor.Program(
       miscIdl,
       program.programId,
-      new anchor.Provider(
+      new anchor.AnchorProvider(
         program.provider.connection,
         new anchor.Wallet(anchor.web3.Keypair.generate()),
         { commitment: program.provider.connection.commitment }

+ 1 - 1
tests/multiple-suites/tests/another-suite/another-suite.ts

@@ -6,7 +6,7 @@ import { MultipleSuites } from "../../target/types/multiple_suites";
 
 describe("multiple-suites", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.env());
+  anchor.setProvider(anchor.AnchorProvider.env());
 
   const program = anchor.workspace.MultipleSuites as Program<MultipleSuites>;
 

+ 1 - 1
tests/multiple-suites/tests/fourth-and-fifth-suite/fifth-suite/fifthSuite.ts

@@ -6,7 +6,7 @@ import { MultipleSuites } from "../../../target/types/multiple_suites";
 
 describe("multiple-suites", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.env());
+  anchor.setProvider(anchor.AnchorProvider.env());
 
   const program = anchor.workspace.MultipleSuites as Program<MultipleSuites>;
 

+ 1 - 1
tests/multiple-suites/tests/fourth-and-fifth-suite/forth-suite/forth-suite.ts

@@ -6,7 +6,7 @@ import { MultipleSuites } from "../../../target/types/multiple_suites";
 
 describe("multiple-suites", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.env());
+  anchor.setProvider(anchor.AnchorProvider.env());
 
   const program = anchor.workspace.MultipleSuites as Program<MultipleSuites>;
 

+ 1 - 1
tests/multiple-suites/tests/multiple-suites/multiple-suites.ts

@@ -6,7 +6,7 @@ import { MultipleSuites } from "../../target/types/multiple_suites";
 
 describe("multiple-suites", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.env());
+  anchor.setProvider(anchor.AnchorProvider.env());
 
   const program = anchor.workspace.MultipleSuites as Program<MultipleSuites>;
 

+ 1 - 1
tests/multiple-suites/tests/third-suite/sub-dir-one/subDirOne.ts

@@ -6,7 +6,7 @@ import { assert } from "chai";
 
 describe("multiple-suites", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.env());
+  anchor.setProvider(anchor.AnchorProvider.env());
 
   const program = anchor.workspace.MultipleSuites as Program<MultipleSuites>;
 

+ 1 - 1
tests/multiple-suites/tests/third-suite/sub-dir-two/subDirTwo.ts

@@ -6,7 +6,7 @@ import { MultipleSuites } from "../../../target/types/multiple_suites";
 
 describe("multiple-suites", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.env());
+  anchor.setProvider(anchor.AnchorProvider.env());
 
   const program = anchor.workspace.MultipleSuites as Program<MultipleSuites>;
 

+ 1 - 1
tests/multisig/tests/multisig.js

@@ -3,7 +3,7 @@ const { assert } = require("chai");
 
 describe("multisig", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.env());
+  anchor.setProvider(anchor.AnchorProvider.env());
 
   const program = anchor.workspace.Multisig;
 

+ 1 - 1
tests/pda-derivation/tests/typescript.spec.ts

@@ -9,7 +9,7 @@ const encode = anchor.utils.bytes.utf8.encode;
 
 describe("typescript", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.env());
+  anchor.setProvider(anchor.AnchorProvider.env());
 
   const program = anchor.workspace.PdaDerivation as Program<PdaDerivation>;
   const base = Keypair.generate();

+ 1 - 1
tests/pyth/tests/pyth.spec.ts

@@ -4,7 +4,7 @@ import { assert } from "chai";
 import { createPriceFeed, setFeedPrice, getFeedData } from "./oracleUtils";
 
 describe("pyth-oracle", () => {
-  anchor.setProvider(anchor.Provider.env());
+  anchor.setProvider(anchor.AnchorProvider.env());
   const program = anchor.workspace.Pyth as Program;
 
   it("initialize", async () => {

+ 1 - 1
tests/safety-checks/tests/safety-checks.ts

@@ -4,7 +4,7 @@ import { SafetyChecks } from "../target/types/safety_checks";
 
 describe("safety-checks", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.env());
+  anchor.setProvider(anchor.AnchorProvider.env());
 
   const program = anchor.workspace.SafetyChecks as Program<SafetyChecks>;
 

+ 3 - 3
tests/spl/token-proxy/tests/token-proxy.js

@@ -2,7 +2,7 @@ const anchor = require("@project-serum/anchor");
 const { assert } = require("chai");
 
 describe("token", () => {
-  const provider = anchor.Provider.local();
+  const provider = anchor.AnchorProvider.local();
 
   // Configure the client to use the local cluster.
   anchor.setProvider(provider);
@@ -118,7 +118,7 @@ async function createMint(provider, authority) {
   const tx = new anchor.web3.Transaction();
   tx.add(...instructions);
 
-  await provider.send(tx, [mint]);
+  await provider.sendAndConfirm(tx, [mint]);
 
   return mint.publicKey;
 }
@@ -147,7 +147,7 @@ async function createTokenAccount(provider, mint, owner) {
   tx.add(
     ...(await createTokenAccountInstrs(provider, vault.publicKey, mint, owner))
   );
-  await provider.send(tx, [vault]);
+  await provider.sendAndConfirm(tx, [vault]);
   return vault.publicKey;
 }
 

+ 5 - 1
tests/swap/tests/swap.js

@@ -11,7 +11,11 @@ const TAKER_FEE = 0.0022;
 
 describe("swap", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.env());
+  const provider = anchor.AnchorProvider.env();
+  // hack so we don't have to update serum-common library
+  // to the new AnchorProvider class and Provider interface
+  provider.send = provider.sendAndConfirm;
+  anchor.setProvider(provider);
 
   // Swap program client.
   const program = anchor.workspace.Swap;

+ 10 - 4
tests/swap/tests/utils/index.js

@@ -196,7 +196,7 @@ async function fundAccount({ provider, mints }) {
   };
 
   // Transfer lamports to market maker.
-  await provider.send(
+  await provider.sendAndConfirm(
     (() => {
       const tx = new Transaction();
       tx.add(
@@ -226,7 +226,7 @@ async function fundAccount({ provider, mints }) {
       MARKET_MAKER.publicKey
     );
 
-    await provider.send(
+    await provider.sendAndConfirm(
       (() => {
         const tx = new Transaction();
         tx.add(
@@ -291,7 +291,10 @@ async function setupMarket({
         feeDiscountPubkey: null,
         selfTradeBehavior: "abortTransaction",
       });
-    await provider.send(transaction, signers.concat(marketMaker.account));
+    await provider.sendAndConfirm(
+      transaction,
+      signers.concat(marketMaker.account)
+    );
   }
 
   for (let k = 0; k < bids.length; k += 1) {
@@ -310,7 +313,10 @@ async function setupMarket({
         feeDiscountPubkey: null,
         selfTradeBehavior: "abortTransaction",
       });
-    await provider.send(transaction, signers.concat(marketMaker.account));
+    await provider.sendAndConfirm(
+      transaction,
+      signers.concat(marketMaker.account)
+    );
   }
 
   return MARKET_A_USDC;

+ 1 - 1
tests/system-accounts/tests/system-accounts.js

@@ -3,7 +3,7 @@ const splToken = require("@solana/spl-token");
 const { assert } = require("chai");
 
 describe("system_accounts", () => {
-  anchor.setProvider(anchor.Provider.local());
+  anchor.setProvider(anchor.AnchorProvider.local());
   const program = anchor.workspace.SystemAccounts;
   const authority = program.provider.wallet.payer;
   const wallet = anchor.web3.Keypair.generate();

+ 1 - 1
tests/sysvars/tests/sysvars.js

@@ -3,7 +3,7 @@ const { assert } = require("chai");
 
 describe("sysvars", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.local());
+  anchor.setProvider(anchor.AnchorProvider.local());
   const program = anchor.workspace.Sysvars;
 
   it("Is initialized!", async () => {

+ 1 - 1
tests/tictactoe/tests/tictactoe.js

@@ -1,7 +1,7 @@
 const anchor = require("@project-serum/anchor");
 
 describe("tictactoe", () => {
-  anchor.setProvider(anchor.Provider.env());
+  anchor.setProvider(anchor.AnchorProvider.env());
   const program = anchor.workspace.Tictactoe;
   let dashboard = anchor.web3.Keypair.generate();
   let game = anchor.web3.Keypair.generate();

+ 1 - 1
tests/typescript/tests/typescript.spec.ts

@@ -2,7 +2,7 @@ import * as anchor from "@project-serum/anchor";
 
 describe("typescript", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.env());
+  anchor.setProvider(anchor.AnchorProvider.env());
 
   it("Is initialized!", async () => {
     // Add your test here.

+ 1 - 1
tests/validator-clone/tests/validator-clone.ts

@@ -5,7 +5,7 @@ import { ValidatorClone } from "../target/types/validator_clone";
 
 describe("validator-clone", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.env());
+  anchor.setProvider(anchor.AnchorProvider.env());
 
   const program = anchor.workspace.ValidatorClone as Program<ValidatorClone>;
   const connection = program.provider.connection;

+ 1 - 1
tests/zero-copy/tests/zero-copy.js

@@ -6,7 +6,7 @@ const BN = anchor.BN;
 
 describe("zero-copy", () => {
   // Configure the client to use the local cluster.
-  anchor.setProvider(anchor.Provider.env());
+  anchor.setProvider(anchor.AnchorProvider.env());
 
   const program = anchor.workspace.ZeroCopy;
   const programCpi = anchor.workspace.ZeroCpi;

+ 6 - 1
ts/src/index.ts

@@ -3,7 +3,12 @@ import { isBrowser } from "./utils/common.js";
 
 export { default as BN } from "bn.js";
 export * as web3 from "@solana/web3.js";
-export { default as Provider, getProvider, setProvider } from "./provider.js";
+export {
+  default as Provider,
+  getProvider,
+  setProvider,
+  AnchorProvider,
+} from "./provider.js";
 export * from "./error.js";
 export { Instruction } from "./coder/borsh/instruction.js";
 export { Idl } from "./idl.js";

+ 7 - 0
ts/src/program/accounts-resolver.ts

@@ -58,6 +58,13 @@ export class AccountsResolver<IDL extends Idl, I extends AllInstructions<IDL>> {
 
       // Signers default to the provider.
       if (accountDesc.isSigner && !this._accounts[accountDescName]) {
+        // @ts-expect-error
+        if (this._provider.wallet === undefined) {
+          throw new Error(
+            "This function requires the Provider interface implementor to have a 'wallet' field."
+          );
+        }
+        // @ts-expect-error
         this._accounts[accountDescName] = this._provider.wallet.publicKey;
         continue;
       }

+ 8 - 0
ts/src/program/namespace/account.ts

@@ -287,7 +287,15 @@ export class AccountClient<
   ): Promise<TransactionInstruction> {
     const size = this.size;
 
+    // @ts-expect-error
+    if (this._provider.wallet === undefined) {
+      throw new Error(
+        "This function requires the Provider interface implementor to have a 'wallet' field."
+      );
+    }
+
     return SystemProgram.createAccount({
+      // @ts-expect-error
       fromPubkey: this._provider.wallet.publicKey,
       newAccountPubkey: signer.publicKey,
       space: sizeOverride ?? size,

+ 10 - 1
ts/src/program/namespace/rpc.ts

@@ -20,8 +20,17 @@ export default class RpcFactory {
     const rpc: RpcFn<IDL, I> = async (...args) => {
       const tx = txFn(...args);
       const [, ctx] = splitArgsAndCtx(idlIx, [...args]);
+      if (provider.sendAndConfirm === undefined) {
+        throw new Error(
+          "This function requires 'Provider.sendAndConfirm' to be implemented."
+        );
+      }
       try {
-        return await provider.send(tx, ctx.signers, ctx.options);
+        return await provider.sendAndConfirm(
+          tx,
+          ctx.signers ?? [],
+          ctx.options
+        );
       } catch (err) {
         throw translateError(err, idlErrors);
       }

+ 14 - 13
ts/src/program/namespace/simulate.ts

@@ -1,9 +1,6 @@
-import {
-  PublicKey,
-  RpcResponseAndContext,
-  SimulatedTransactionResponse,
-} from "@solana/web3.js";
+import { PublicKey } from "@solana/web3.js";
 import Provider from "../../provider.js";
+import { SuccessfulTxSimulationResponse } from "src/utils/rpc.js";
 import { splitArgsAndCtx } from "../context.js";
 import { TransactionFn } from "./transaction.js";
 import { EventParser, Event } from "../event.js";
@@ -30,21 +27,25 @@ export default class SimulateFactory {
     const simulate: SimulateFn<IDL> = async (...args) => {
       const tx = txFn(...args);
       const [, ctx] = splitArgsAndCtx(idlIx, [...args]);
-      let resp:
-        | RpcResponseAndContext<SimulatedTransactionResponse>
-        | undefined = undefined;
+      let resp: SuccessfulTxSimulationResponse | undefined = undefined;
+      if (provider.simulate === undefined) {
+        throw new Error(
+          "This function requires 'Provider.simulate' to be implemented."
+        );
+      }
       try {
-        resp = await provider!.simulate(tx, ctx.signers, ctx.options);
+        resp = await provider!.simulate(
+          tx,
+          ctx.signers,
+          ctx.options?.commitment
+        );
       } catch (err) {
         throw translateError(err, idlErrors);
       }
       if (resp === undefined) {
         throw new Error("Unable to simulate transaction");
       }
-      if (resp.value.err) {
-        throw new Error(`Simulate error: ${resp.value.err.toString()}`);
-      }
-      const logs = resp.value.logs;
+      const logs = resp.logs;
       if (!logs) {
         throw new Error("Simulated logs not found");
       }

+ 7 - 0
ts/src/program/namespace/state.ts

@@ -245,8 +245,15 @@ function stateInstructionKeys<M extends IdlStateMethod>(
   if (m.name === "new") {
     // Ctor `new` method.
     const [programSigner] = findProgramAddressSync([], programId);
+    // @ts-expect-error
+    if (provider.wallet === undefined) {
+      throw new Error(
+        "This function requires the Provider interface implementor to have a 'wallet' field."
+      );
+    }
     return [
       {
+        // @ts-expect-error
         pubkey: provider.wallet.publicKey,
         isWritable: false,
         isSigner: true,

+ 77 - 79
ts/src/provider.ts

@@ -5,19 +5,49 @@ import {
   Transaction,
   TransactionSignature,
   ConfirmOptions,
-  RpcResponseAndContext,
   SimulatedTransactionResponse,
   Commitment,
   SendTransactionError,
+  SendOptions,
+  RpcResponseAndContext,
 } from "@solana/web3.js";
 import { bs58 } from "./utils/bytes/index.js";
 import { isBrowser } from "./utils/common.js";
+import {
+  simulateTransaction,
+  SuccessfulTxSimulationResponse,
+} from "./utils/rpc.js";
+
+export default interface Provider {
+  readonly connection: Connection;
+
+  send?(
+    tx: Transaction,
+    signers?: Signer[],
+    opts?: SendOptions
+  ): Promise<TransactionSignature>;
+  sendAndConfirm?(
+    tx: Transaction,
+    signers?: Signer[],
+    opts?: ConfirmOptions
+  ): Promise<TransactionSignature>;
+  sendAll?(
+    txWithSigners: { tx: Transaction; signers?: Signer[] }[],
+    opts?: ConfirmOptions
+  ): Promise<Array<TransactionSignature>>;
+  simulate?(
+    tx: Transaction,
+    signers?: Signer[],
+    commitment?: Commitment,
+    includeAccounts?: boolean | PublicKey[]
+  ): Promise<SuccessfulTxSimulationResponse>;
+}
 
 /**
  * The network and wallet context used to send transactions paid for and signed
  * by the provider.
  */
-export default class Provider {
+export class AnchorProvider implements Provider {
   /**
    * @param connection The cluster connection where the program is deployed.
    * @param wallet     The wallet used to pay for and sign all transactions.
@@ -44,18 +74,18 @@ export default class Provider {
    *
    * (This api is for Node only.)
    */
-  static local(url?: string, opts?: ConfirmOptions): Provider {
+  static local(url?: string, opts?: ConfirmOptions): AnchorProvider {
     if (isBrowser) {
       throw new Error(`Provider local is not available on browser.`);
     }
-    opts = opts ?? Provider.defaultOptions();
+    opts = opts ?? AnchorProvider.defaultOptions();
     const connection = new Connection(
       url ?? "http://localhost:8899",
       opts.preflightCommitment
     );
     const NodeWallet = require("./nodewallet.js").default;
     const wallet = NodeWallet.local();
-    return new Provider(connection, wallet, opts);
+    return new AnchorProvider(connection, wallet, opts);
   }
 
   /**
@@ -64,7 +94,7 @@ export default class Provider {
    *
    * (This api is for Node only.)
    */
-  static env(): Provider {
+  static env(): AnchorProvider {
     if (isBrowser) {
       throw new Error(`Provider env is not available on browser.`);
     }
@@ -74,30 +104,26 @@ export default class Provider {
     if (url === undefined) {
       throw new Error("ANCHOR_PROVIDER_URL is not defined");
     }
-    const options = Provider.defaultOptions();
+    const options = AnchorProvider.defaultOptions();
     const connection = new Connection(url, options.commitment);
     const NodeWallet = require("./nodewallet.js").default;
     const wallet = NodeWallet.local();
 
-    return new Provider(connection, wallet, options);
+    return new AnchorProvider(connection, wallet, options);
   }
 
   /**
    * Sends the given transaction, paid for and signed by the provider's wallet.
    *
    * @param tx      The transaction to send.
-   * @param signers The set of signers in addition to the provider wallet that
-   *                will sign the transaction.
+   * @param signers The signers of the transaction.
    * @param opts    Transaction confirmation options.
    */
-  async send(
+  async sendAndConfirm(
     tx: Transaction,
-    signers?: Array<Signer | undefined>,
+    signers?: Signer[],
     opts?: ConfirmOptions
   ): Promise<TransactionSignature> {
-    if (signers === undefined) {
-      signers = [];
-    }
     if (opts === undefined) {
       opts = this.opts;
     }
@@ -108,11 +134,9 @@ export default class Provider {
     ).blockhash;
 
     tx = await this.wallet.signTransaction(tx);
-    signers
-      .filter((s): s is Signer => s !== undefined)
-      .forEach((kp) => {
-        tx.partialSign(kp);
-      });
+    (signers ?? []).forEach((kp) => {
+      tx.partialSign(kp);
+    });
 
     const rawTx = tx.serialize();
 
@@ -146,7 +170,7 @@ export default class Provider {
    * Similar to `send`, but for an array of transactions and signers.
    */
   async sendAll(
-    reqs: Array<SendTxRequest>,
+    txWithSigners: { tx: Transaction; signers?: Signer[] }[],
     opts?: ConfirmOptions
   ): Promise<Array<TransactionSignature>> {
     if (opts === undefined) {
@@ -156,22 +180,16 @@ export default class Provider {
       opts.preflightCommitment
     );
 
-    let txs = reqs.map((r) => {
+    let txs = txWithSigners.map((r) => {
       let tx = r.tx;
-      let signers = r.signers;
-
-      if (signers === undefined) {
-        signers = [];
-      }
+      let signers = r.signers ?? [];
 
       tx.feePayer = this.wallet.publicKey;
       tx.recentBlockhash = blockhash.blockhash;
 
-      signers
-        .filter((s): s is Signer => s !== undefined)
-        .forEach((kp) => {
-          tx.partialSign(kp);
-        });
+      signers.forEach((kp) => {
+        tx.partialSign(kp);
+      });
 
       return tx;
     });
@@ -195,38 +213,45 @@ export default class Provider {
    * Simulates the given transaction, returning emitted logs from execution.
    *
    * @param tx      The transaction to send.
-   * @param signers The set of signers in addition to the provdier wallet that
-   *                will sign the transaction.
+   * @param signers The signers of the transaction.
    * @param opts    Transaction confirmation options.
    */
   async simulate(
     tx: Transaction,
-    signers?: Array<Signer | undefined>,
-    opts: ConfirmOptions = this.opts
-  ): Promise<RpcResponseAndContext<SimulatedTransactionResponse>> {
-    if (signers === undefined) {
-      signers = [];
-    }
-
+    signers?: Signer[],
+    commitment?: Commitment,
+    includeAccounts?: boolean | PublicKey[]
+  ): Promise<SuccessfulTxSimulationResponse> {
     tx.feePayer = this.wallet.publicKey;
     tx.recentBlockhash = (
-      await this.connection.getRecentBlockhash(
-        opts.preflightCommitment ?? this.opts.preflightCommitment
+      await this.connection.getLatestBlockhash(
+        commitment ?? this.connection.commitment
       )
     ).blockhash;
 
     tx = await this.wallet.signTransaction(tx);
-    signers
-      .filter((s): s is Signer => s !== undefined)
-      .forEach((kp) => {
-        tx.partialSign(kp);
-      });
-
-    return await simulateTransaction(
+    const result = await simulateTransaction(
       this.connection,
       tx,
-      opts.commitment ?? this.opts.commitment ?? "processed"
+      signers,
+      commitment,
+      includeAccounts
     );
+
+    if (result.value.err) {
+      throw new SimulateError(result.value);
+    }
+
+    return result.value;
+  }
+}
+
+class SimulateError extends Error {
+  constructor(
+    readonly simulationResponse: SimulatedTransactionResponse,
+    message?: string
+  ) {
+    super(message);
   }
 }
 
@@ -244,33 +269,6 @@ export interface Wallet {
   publicKey: PublicKey;
 }
 
-// Copy of Connection.simulateTransaction that takes a commitment parameter.
-async function simulateTransaction(
-  connection: Connection,
-  transaction: Transaction,
-  commitment: Commitment
-): Promise<RpcResponseAndContext<SimulatedTransactionResponse>> {
-  // @ts-ignore
-  transaction.recentBlockhash = await connection._recentBlockhash(
-    // @ts-ignore
-    connection._disableBlockhashCaching
-  );
-
-  const signData = transaction.serializeMessage();
-  // @ts-ignore
-  const wireTransaction = transaction._serialize(signData);
-  const encodedTransaction = wireTransaction.toString("base64");
-  const config: any = { encoding: "base64", commitment };
-  const args = [encodedTransaction, config];
-
-  // @ts-ignore
-  const res = await connection._rpcRequest("simulateTransaction", args);
-  if (res.error) {
-    throw new Error("failed to simulate transaction: " + res.error.message);
-  }
-  return res.result;
-}
-
 // Copy of Connection.sendAndConfirmRawTransaction that throws
 // a better error if 'confirmTransaction` returns an error status
 async function sendAndConfirmRawTransaction(
@@ -322,7 +320,7 @@ export function setProvider(provider: Provider) {
  */
 export function getProvider(): Provider {
   if (_provider === null) {
-    return Provider.local();
+    return AnchorProvider.local();
   }
   return _provider;
 }

+ 169 - 2
ts/src/utils/rpc.ts

@@ -9,10 +9,33 @@ import {
   Transaction,
   TransactionInstruction,
   Commitment,
+  Signer,
+  RpcResponseAndContext,
+  SimulatedTransactionResponse,
+  SendTransactionError,
 } from "@solana/web3.js";
 import { chunks } from "../utils/common.js";
 import { Address, translateAddress } from "../program/common.js";
-import Provider, { getProvider } from "../provider.js";
+import Provider, { getProvider, Wallet } from "../provider.js";
+import {
+  type as pick,
+  number,
+  string,
+  array,
+  boolean,
+  literal,
+  record,
+  union,
+  optional,
+  nullable,
+  coerce,
+  instance,
+  create,
+  tuple,
+  unknown,
+  any,
+  Struct,
+} from "superstruct";
 
 /**
  * Sends a transaction to a program with the given accounts and instruction
@@ -38,7 +61,13 @@ export async function invoke(
     })
   );
 
-  return await provider.send(tx);
+  if (provider.sendAndConfirm === undefined) {
+    throw new Error(
+      "This function requires 'Provider.sendAndConfirm' to be implemented."
+    );
+  }
+
+  return await provider.sendAndConfirm(tx, []);
 }
 
 const GET_MULTIPLE_ACCOUNTS_LIMIT: number = 99;
@@ -87,3 +116,141 @@ async function getMultipleAccountsCore(
     };
   });
 }
+
+// copy from @solana/web3.js that has a commitment param
+export async function simulateTransaction(
+  connection: Connection,
+  transaction: Transaction,
+  signers?: Array<Signer>,
+  commitment?: Commitment,
+  includeAccounts?: boolean | Array<PublicKey>
+): Promise<RpcResponseAndContext<SimulatedTransactionResponse>> {
+  if (signers && signers.length > 0) {
+    transaction.sign(...signers);
+  }
+
+  // @ts-expect-error
+  const message = transaction._compile();
+  const signData = message.serialize();
+  // @ts-expect-error
+  const wireTransaction = transaction._serialize(signData);
+  const encodedTransaction = wireTransaction.toString("base64");
+  const config: any = {
+    encoding: "base64",
+    commitment: commitment ?? connection.commitment,
+  };
+
+  if (includeAccounts) {
+    const addresses = (
+      Array.isArray(includeAccounts) ? includeAccounts : message.nonProgramIds()
+    ).map((key) => key.toBase58());
+
+    config["accounts"] = {
+      encoding: "base64",
+      addresses,
+    };
+  }
+
+  if (signers) {
+    config.sigVerify = true;
+  }
+
+  const args = [encodedTransaction, config];
+  // @ts-expect-error
+  const unsafeRes = await connection._rpcRequest("simulateTransaction", args);
+  const res = create(unsafeRes, SimulatedTransactionResponseStruct);
+  if ("error" in res) {
+    let logs;
+    if ("data" in res.error) {
+      logs = res.error.data.logs;
+      if (logs && Array.isArray(logs)) {
+        const traceIndent = "\n    ";
+        const logTrace = traceIndent + logs.join(traceIndent);
+        console.error(res.error.message, logTrace);
+      }
+    }
+    throw new SendTransactionError(
+      "failed to simulate transaction: " + res.error.message,
+      logs
+    );
+  }
+  return res.result;
+}
+
+// copy from @solana/web3.js
+function jsonRpcResult<T, U>(schema: Struct<T, U>) {
+  return coerce(createRpcResult(schema), UnknownRpcResult, (value) => {
+    if ("error" in value) {
+      return value;
+    } else {
+      return {
+        ...value,
+        result: create(value.result, schema),
+      };
+    }
+  });
+}
+
+// copy from @solana/web3.js
+const UnknownRpcResult = createRpcResult(unknown());
+
+// copy from @solana/web3.js
+function createRpcResult<T, U>(result: Struct<T, U>) {
+  return union([
+    pick({
+      jsonrpc: literal("2.0"),
+      id: string(),
+      result,
+    }),
+    pick({
+      jsonrpc: literal("2.0"),
+      id: string(),
+      error: pick({
+        code: unknown(),
+        message: string(),
+        data: optional(any()),
+      }),
+    }),
+  ]);
+}
+
+// copy from @solana/web3.js
+function jsonRpcResultAndContext<T, U>(value: Struct<T, U>) {
+  return jsonRpcResult(
+    pick({
+      context: pick({
+        slot: number(),
+      }),
+      value,
+    })
+  );
+}
+
+// copy from @solana/web3.js
+const SimulatedTransactionResponseStruct = jsonRpcResultAndContext(
+  pick({
+    err: nullable(union([pick({}), string()])),
+    logs: nullable(array(string())),
+    accounts: optional(
+      nullable(
+        array(
+          nullable(
+            pick({
+              executable: boolean(),
+              owner: string(),
+              lamports: number(),
+              data: array(string()),
+              rentEpoch: optional(number()),
+            })
+          )
+        )
+      )
+    ),
+    unitsConsumed: optional(number()),
+  })
+);
+
+export type SuccessfulTxSimulationResponse = Omit<
+  SimulatedTransactionResponse,
+  "err"
+>;