ts: add error log parsing to ts client (#1640)

This commit is contained in:
Paul 2022-03-20 20:29:12 -04:00 committed by GitHub
parent 45a5d20a79
commit 9afdb17ac2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 1129 additions and 235 deletions

View File

@ -23,6 +23,7 @@ incremented for features.
* lang: Handle arrays with const as size in instruction data ([#1623](https://github.com/project-serum/anchor/issues/1623).
* spl: Add support for revoke instruction ([#1493](https://github.com/project-serum/anchor/pull/1493)).
* ts: Add provider parameter to `Spl.token` factory method ([#1597](https://github.com/project-serum/anchor/pull/1597)).
* ts: Add `AnchorError` with program stack and also a program stack for non-`AnchorError` errors ([#1640](https://github.com/project-serum/anchor/pull/1640)). `AnchorError` is not returned for `processed` tx that have `skipPreflight` set to `true` (it falls back to `ProgramError` or the raw solana library error).
### Fixes

View File

@ -1,5 +1,5 @@
import * as anchor from "@project-serum/anchor";
import { Program } from "@project-serum/anchor";
import { AnchorError, Program } from "@project-serum/anchor";
import { findProgramAddressSync } from "@project-serum/anchor/dist/cjs/utils/pubkey";
import { PublicKey } from "@solana/web3.js";
import assert from "assert";
@ -78,9 +78,11 @@ describe("bpf_upgradeable_state", () => {
signers: [settings, authority],
});
assert.ok(false);
} catch (err) {
assert.equal(err.code, 2003);
assert.equal(err.msg, "A raw constraint was violated");
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2003);
assert.equal(err.error.errorMessage, "A raw constraint was violated");
}
});
@ -98,9 +100,14 @@ describe("bpf_upgradeable_state", () => {
signers: [settings],
});
assert.ok(false);
} catch (err) {
assert.equal(err.code, 3013);
assert.equal(err.msg, "The given account is not a program data account");
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 3013);
assert.equal(
err.error.errorMessage,
"The given account is not a program data account"
);
}
});
@ -118,10 +125,12 @@ describe("bpf_upgradeable_state", () => {
signers: [settings],
});
assert.ok(false);
} catch (err) {
assert.equal(err.code, 3007);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 3007);
assert.equal(
err.msg,
err.error.errorMessage,
"The given account is owned by a different program than expected"
);
}
@ -149,8 +158,10 @@ describe("bpf_upgradeable_state", () => {
signers: [settings],
});
assert.ok(false);
} catch (err) {
assert.equal(err.code, 6000);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 6000);
}
});
@ -176,8 +187,10 @@ describe("bpf_upgradeable_state", () => {
signers: [settings],
});
assert.ok(false);
} catch (err) {
assert.equal(err.code, 2003);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2003);
}
});
});

View File

@ -6,4 +6,4 @@ wallet = "~/.config/solana/id.json"
declare_id = "FJcF5c8HncdfAgjPjTH49GAEypkJCG2ZADh2xhduNi5B"
[scripts]
test = "yarn run mocha -t 1000000 tests/"
test = "yarn run ts-mocha -t 1000000 tests/*.ts"

View File

@ -1,17 +0,0 @@
const anchor = require("@project-serum/anchor");
const splToken = require("@solana/spl-token");
const assert = require("assert");
describe("declare_id", () => {
anchor.setProvider(anchor.Provider.local());
const program = anchor.workspace.DeclareId;
it("throws error!", async () => {
try {
await program.rpc.initialize();
assert.ok(false);
} catch (err) {
assert.equal(err.code, 4100);
}
});
});

View File

@ -0,0 +1,22 @@
import * as anchor from "@project-serum/anchor";
import { AnchorError, Program } from "@project-serum/anchor";
import splToken from "@solana/spl-token";
import { DeclareId } from "../target/types/declare_id";
import { assert } from "chai";
describe("declare_id", () => {
anchor.setProvider(anchor.Provider.local());
const program = anchor.workspace.DeclareId as Program<DeclareId>;
it("throws error!", async () => {
try {
await program.rpc.initialize();
assert.ok(false);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 4100);
}
});
});

View File

@ -0,0 +1,10 @@
{
"compilerOptions": {
"types": ["mocha", "chai", "node"],
"typeRoots": ["./node_modules/@types"],
"lib": ["es2015"],
"module": "commonjs",
"target": "es6",
"esModuleInterop": true
}
}

View File

@ -6,7 +6,7 @@ wallet = "~/.config/solana/id.json"
errors = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
[scripts]
test = "yarn run mocha -t 1000000 tests/"
test = "yarn run ts-mocha -t 1000000 tests/*.ts"
[features]
seeds = false

View File

@ -1,13 +1,26 @@
const assert = require("assert");
const anchor = require("@project-serum/anchor");
const { Account, Transaction, TransactionInstruction } = anchor.web3;
const { TOKEN_PROGRAM_ID, Token } = require("@solana/spl-token");
const { Keypair } = require("@solana/web3.js");
import * as anchor from "@project-serum/anchor";
import {
Program,
BN,
IdlAccounts,
AnchorError,
ProgramError,
} from "@project-serum/anchor";
import {
PublicKey,
Keypair,
SystemProgram,
Transaction,
TransactionInstruction,
} from "@solana/web3.js";
import { TOKEN_PROGRAM_ID, Token } from "@solana/spl-token";
import { assert, expect } from "chai";
import { Errors } from "../target/types/errors";
// sleep to allow logs to come in
const sleep = (ms) =>
new Promise((resolve) => {
setTimeout(() => resolve(), ms);
setTimeout(() => resolve(0), ms);
});
const withLogTest = async (callback, expectedLogs) => {
@ -45,7 +58,6 @@ const withLogTest = async (callback, expectedLogs) => {
anchor.getProvider().connection.removeOnLogsListener(listener);
throw err;
}
await sleep(3000);
anchor.getProvider().connection.removeOnLogsListener(listener);
assert.ok(logTestOk);
};
@ -54,21 +66,33 @@ describe("errors", () => {
// Configure the client to use the local cluster.
const localProvider = anchor.Provider.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`)
localProvider.opts.commitment = "confirmed";
anchor.setProvider(localProvider);
const program = anchor.workspace.Errors;
const program = anchor.workspace.Errors as Program<Errors>;
it("Emits a Hello error", async () => {
await withLogTest(async () => {
try {
const tx = await program.rpc.hello();
assert.ok(false);
} catch (err) {
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
const errMsg =
"This is an error message clients will automatically display";
assert.equal(err.toString(), errMsg);
assert.equal(err.msg, errMsg);
assert.equal(err.code, 6000);
const fullErrMsg =
"AnchorError thrown in programs/errors/src/lib.rs:13. Error Code: Hello. Error Number: 6000. Error Message: This is an error message clients will automatically display.";
assert.equal(err.toString(), fullErrMsg);
assert.equal(err.error.errorMessage, errMsg);
assert.equal(err.error.errorCode.number, 6000);
assert.equal(err.program.toString(), program.programId.toString());
expect(err.error.origin).to.deep.equal({
file: "programs/errors/src/lib.rs",
line: 13,
});
}
}, [
"Program log: AnchorError thrown in programs/errors/src/lib.rs:13. Error Code: Hello. Error Number: 6000. Error Message: This is an error message clients will automatically display.",
@ -79,12 +103,14 @@ describe("errors", () => {
try {
const tx = await program.rpc.testRequire();
assert.ok(false);
} catch (err) {
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
const errMsg =
"This is an error message clients will automatically display";
assert.equal(err.toString(), errMsg);
assert.equal(err.msg, errMsg);
assert.equal(err.code, 6000);
assert.equal(err.error.errorMessage, errMsg);
assert.equal(err.error.errorCode.number, 6000);
assert.equal(err.error.errorCode.code, "Hello");
}
});
@ -92,12 +118,13 @@ describe("errors", () => {
try {
const tx = await program.rpc.testErr();
assert.ok(false);
} catch (err) {
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
const errMsg =
"This is an error message clients will automatically display";
assert.equal(err.toString(), errMsg);
assert.equal(err.msg, errMsg);
assert.equal(err.code, 6000);
assert.equal(err.error.errorMessage, errMsg);
assert.equal(err.error.errorCode.number, 6000);
}
});
@ -107,7 +134,10 @@ describe("errors", () => {
const tx = await program.rpc.testProgramError();
assert.ok(false);
} catch (err) {
// No-op (withLogTest expects the callback to catch the initial tx error)
expect(err.programErrorStack.map((pk) => pk.toString())).to.deep.equal([
program.programId.toString(),
]);
expect(err.program.toString()).to.equal(program.programId.toString());
}
}, [
"Program log: ProgramError occurred. Error Code: InvalidAccountData. Error Number: 17179869184. Error Message: An account's data contents was invalid.",
@ -120,7 +150,9 @@ describe("errors", () => {
const tx = await program.rpc.testProgramErrorWithSource();
assert.ok(false);
} catch (err) {
// No-op (withLogTest expects the callback to catch the initial tx error)
expect(err.programErrorStack.map((pk) => pk.toString())).to.deep.equal([
program.programId.toString(),
]);
}
}, [
"Program log: ProgramError thrown in programs/errors/src/lib.rs:38. Error Code: InvalidAccountData. Error Number: 17179869184. Error Message: An account's data contents was invalid.",
@ -131,11 +163,11 @@ describe("errors", () => {
try {
const tx = await program.rpc.helloNoMsg();
assert.ok(false);
} catch (err) {
const errMsg = "HelloNoMsg";
assert.equal(err.toString(), errMsg);
assert.equal(err.msg, errMsg);
assert.equal(err.code, 6000 + 123);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorMessage, "HelloNoMsg");
assert.equal(err.error.errorCode.number, 6123);
}
});
@ -143,11 +175,11 @@ describe("errors", () => {
try {
const tx = await program.rpc.helloNext();
assert.ok(false);
} catch (err) {
const errMsg = "HelloNext";
assert.equal(err.toString(), errMsg);
assert.equal(err.msg, errMsg);
assert.equal(err.code, 6000 + 124);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorMessage, "HelloNext");
assert.equal(err.error.errorCode.number, 6124);
}
});
@ -160,11 +192,12 @@ describe("errors", () => {
},
});
assert.ok(false);
} catch (err) {
const errMsg = "A mut constraint was violated";
assert.equal(err.toString(), errMsg);
assert.equal(err.msg, errMsg);
assert.equal(err.code, 2000);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorMessage, "A mut constraint was violated");
assert.equal(err.error.errorCode.number, 2000);
assert.equal(err.error.origin, "my_account");
}
}, [
"Program log: AnchorError caused by account: my_account. Error Code: ConstraintMut. Error Number: 2000. Error Message: A mut constraint was violated.",
@ -187,11 +220,23 @@ describe("errors", () => {
signers: [account],
});
assert.ok(false);
} catch (err) {
const errMsg = "A has_one constraint was violated";
assert.equal(err.toString(), errMsg);
assert.equal(err.msg, errMsg);
assert.equal(err.code, 2001);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(
err.error.errorMessage,
"A has one constraint was violated"
);
assert.equal(err.error.errorCode.number, 2001);
assert.equal(err.error.errorCode.code, "ConstraintHasOne");
assert.equal(err.error.origin, "my_account");
assert.equal(err.program.toString(), program.programId.toString());
expect(
err.error.comparedValues.map((pk) => pk.toString())
).to.deep.equal([
"11111111111111111111111111111111",
"SysvarRent111111111111111111111111111111111",
]);
}
}, [
"Program log: AnchorError caused by account: my_account. Error Code: ConstraintHasOne. Error Number: 2001. Error Message: A has one constraint was violated.",
@ -228,7 +273,6 @@ describe("errors", () => {
await program.provider.send(tx);
assert.ok(false);
} catch (err) {
await sleep(3000);
anchor.getProvider().connection.removeOnLogsListener(listener);
const errMsg = `Error: Raw transaction ${signature} failed ({"err":{"InstructionError":[0,{"Custom":3010}]}})`;
assert.equal(err.toString(), errMsg);
@ -245,11 +289,12 @@ describe("errors", () => {
},
});
assert.ok(false);
} catch (err) {
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
const errMsg = "HelloCustom";
assert.equal(err.toString(), errMsg);
assert.equal(err.msg, errMsg);
assert.equal(err.code, 6000 + 125);
assert.equal(err.error.errorMessage, errMsg);
assert.equal(err.error.errorCode.number, 6125);
}
});
@ -264,10 +309,12 @@ describe("errors", () => {
assert.fail(
"Unexpected success in creating a transaction that should have fail with `AccountNotInitialized` error"
);
} catch (err) {
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
const errMsg =
"The program expected this account to be already initialized";
assert.equal(err.toString(), errMsg);
assert.equal(err.error.errorMessage, errMsg);
}
}, [
"Program log: AnchorError caused by account: not_initialized_account. Error Code: AccountNotInitialized. Error Number: 3012. Error Message: The program expected this account to be already initialized.",
@ -294,10 +341,12 @@ describe("errors", () => {
assert.fail(
"Unexpected success in creating a transaction that should have failed with `AccountOwnedByWrongProgram` error"
);
} catch (err) {
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
const errMsg =
"The given account is owned by a different program than expected";
assert.equal(err.toString(), errMsg);
assert.equal(err.error.errorMessage, errMsg);
}
}, [
"Program log: AnchorError caused by account: wrong_account. Error Code: AccountOwnedByWrongProgram. Error Number: 3007. Error Message: The given account is owned by a different program than expected.",
@ -315,8 +364,11 @@ describe("errors", () => {
assert.fail(
"Unexpected success in creating a transaction that should have failed with `ValueMismatch` error"
);
} catch (err) {
assert.equal(err.code, 6126);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 6126);
expect(err.error.comparedValues).to.deep.equal(["5241", "124124124"]);
}
}, [
"Program log: AnchorError thrown in programs/errors/src/lib.rs:68. Error Code: ValueMismatch. Error Number: 6126. Error Message: ValueMismatch.",
@ -332,8 +384,10 @@ describe("errors", () => {
assert.fail(
"Unexpected success in creating a transaction that should have failed with `ValueMismatch` error"
);
} catch (err) {
assert.equal(err.code, 2501);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2501);
}
}, [
"Program log: AnchorError thrown in programs/errors/src/lib.rs:73. Error Code: RequireEqViolated. Error Number: 2501. Error Message: A require_eq expression was violated.",
@ -349,8 +403,10 @@ describe("errors", () => {
assert.fail(
"Unexpected success in creating a transaction that should have failed with `ValueMatch` error"
);
} catch (err) {
assert.equal(err.code, 6127);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 6127);
}
}, [
"Program log: AnchorError thrown in programs/errors/src/lib.rs:78. Error Code: ValueMatch. Error Number: 6127. Error Message: ValueMatch.",
@ -366,8 +422,10 @@ describe("errors", () => {
assert.fail(
"Unexpected success in creating a transaction that should have failed with `RequireNeqViolated` error"
);
} catch (err) {
assert.equal(err.code, 2503);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2503);
}
}, [
"Program log: AnchorError thrown in programs/errors/src/lib.rs:83. Error Code: RequireNeqViolated. Error Number: 2503. Error Message: A require_neq expression was violated.",
@ -388,8 +446,10 @@ describe("errors", () => {
assert.fail(
"Unexpected success in creating a transaction that should have failed with `ValueMismatch` error"
);
} catch (err) {
assert.equal(err.code, 6126);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 6126);
}
}, [
"Program log: AnchorError thrown in programs/errors/src/lib.rs:88. Error Code: ValueMismatch. Error Number: 6126. Error Message: ValueMismatch.",
@ -412,8 +472,10 @@ describe("errors", () => {
assert.fail(
"Unexpected success in creating a transaction that should have failed with `ValueMismatch` error"
);
} catch (err) {
assert.equal(err.code, 2502);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2502);
}
}, [
"Program log: AnchorError thrown in programs/errors/src/lib.rs:97. Error Code: RequireKeysEqViolated. Error Number: 2502. Error Message: A require_keys_eq expression was violated.",
@ -436,8 +498,10 @@ describe("errors", () => {
assert.fail(
"Unexpected success in creating a transaction that should have failed with `ValueMatch` error"
);
} catch (err) {
assert.equal(err.code, 6127);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 6127);
}
}, [
"Program log: AnchorError thrown in programs/errors/src/lib.rs:102. Error Code: ValueMatch. Error Number: 6127. Error Message: ValueMatch.",
@ -460,8 +524,10 @@ describe("errors", () => {
assert.fail(
"Unexpected success in creating a transaction that should have failed with `RequireKeysNeqViolated` error"
);
} catch (err) {
assert.equal(err.code, 2504);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2504);
}
}, [
"Program log: AnchorError thrown in programs/errors/src/lib.rs:111. Error Code: RequireKeysNeqViolated. Error Number: 2504. Error Message: A require_keys_neq expression was violated.",
@ -479,8 +545,10 @@ describe("errors", () => {
assert.fail(
"Unexpected success in creating a transaction that should have failed with `ValueLessOrEqual` error"
);
} catch (err) {
assert.equal(err.code, 6129);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 6129);
}
}, [
"Program log: AnchorError thrown in programs/errors/src/lib.rs:116. Error Code: ValueLessOrEqual. Error Number: 6129. Error Message: ValueLessOrEqual.",
@ -496,8 +564,10 @@ describe("errors", () => {
assert.fail(
"Unexpected success in creating a transaction that should have failed with `RequireGtViolated` error"
);
} catch (err) {
assert.equal(err.code, 2505);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2505);
}
}, [
"Program log: AnchorError thrown in programs/errors/src/lib.rs:121. Error Code: RequireGtViolated. Error Number: 2505. Error Message: A require_gt expression was violated.",
@ -513,8 +583,10 @@ describe("errors", () => {
assert.fail(
"Unexpected success in creating a transaction that should have failed with `ValueLess` error"
);
} catch (err) {
assert.equal(err.code, 6128);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 6128);
}
}, [
"Program log: AnchorError thrown in programs/errors/src/lib.rs:126. Error Code: ValueLess. Error Number: 6128. Error Message: ValueLess.",
@ -530,8 +602,10 @@ describe("errors", () => {
assert.fail(
"Unexpected success in creating a transaction that should have failed with `RequireGteViolated` error"
);
} catch (err) {
assert.equal(err.code, 2506);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2506);
}
}, [
"Program log: AnchorError thrown in programs/errors/src/lib.rs:131. Error Code: RequireGteViolated. Error Number: 2506. Error Message: A require_gte expression was violated.",

View File

@ -0,0 +1,10 @@
{
"compilerOptions": {
"types": ["mocha", "chai", "node"],
"typeRoots": ["./node_modules/@types"],
"lib": ["es2015"],
"module": "commonjs",
"target": "es6",
"esModuleInterop": true
}
}

View File

@ -3,6 +3,8 @@ const anchor = require("@project-serum/anchor");
const serumCmn = require("@project-serum/common");
const { TOKEN_PROGRAM_ID } = require("@solana/spl-token");
const utils = require("./utils");
const chai = require("chai");
const expect = chai.expect;
anchor.utils.features.set("anchor-deprecated-state");
@ -118,8 +120,8 @@ describe("Lockup and Registry", () => {
await lockup.state.rpc.whitelistAdd(e, { accounts });
},
(err) => {
assert.equal(err.code, 6008);
assert.equal(err.msg, "Whitelist is full");
assert.equal(err.error.errorCode.number, 6008);
assert.equal(err.error.errorMessage, "Whitelist is full");
return true;
}
);
@ -216,8 +218,11 @@ describe("Lockup and Registry", () => {
});
},
(err) => {
assert.equal(err.code, 6007);
assert.equal(err.msg, "Insufficient withdrawal balance.");
assert.equal(err.error.errorCode.number, 6007);
assert.equal(
err.error.errorMessage,
"Insufficient withdrawal balance."
);
return true;
}
);
@ -773,8 +778,24 @@ describe("Lockup and Registry", () => {
(err) => {
// Solana doesn't propagate errors across CPI. So we receive the registry's error code,
// not the lockup's.
const errorCode = "custom program error: 0x1784";
assert.ok(err.toString().split(errorCode).length === 2);
assert.equal(err.error.errorCode.number, 6020);
assert.equal(err.error.errorCode.code, "UnrealizedReward");
assert.equal(
err.error.errorMessage,
"Locked rewards cannot be realized until one unstaked all tokens."
);
expect(err.error.origin).to.deep.equal({
file: "programs/registry/src/lib.rs",
line: 63,
});
assert.equal(
err.program.toString(),
"HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L"
);
expect(err.programErrorStack.map((pk) => pk.toString())).to.deep.equal([
"Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS",
"HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L",
]);
return true;
}
);
@ -855,8 +876,11 @@ describe("Lockup and Registry", () => {
await tryEndUnstake();
},
(err) => {
assert.equal(err.code, 6009);
assert.equal(err.msg, "The unstake timelock has not yet expired.");
assert.equal(err.error.errorCode.number, 6009);
assert.equal(
err.error.errorMessage,
"The unstake timelock has not yet expired."
);
return true;
}
);

View File

@ -14,4 +14,4 @@ program = "./target/deploy/misc.so"
exclude = ["programs/shared"]
[scripts]
test = "yarn run mocha -t 1000000 tests/"
test = "yarn run ts-mocha -t 1000000 tests/*.ts"

View File

@ -1,18 +1,20 @@
const anchor = require("@project-serum/anchor");
const assert = require("assert");
const {
ASSOCIATED_TOKEN_PROGRAM_ID,
import * as anchor from "@project-serum/anchor";
import { Program, BN, IdlAccounts, AnchorError } from "@project-serum/anchor";
import {
PublicKey,
Keypair,
SystemProgram,
SYSVAR_RENT_PUBKEY,
} from "@solana/web3.js";
import {
TOKEN_PROGRAM_ID,
Token,
} = require("@solana/spl-token");
const miscIdl = require("../target/idl/misc.json");
const {
SystemProgram,
Keypair,
PublicKey,
SYSVAR_RENT_PUBKEY,
} = require("@solana/web3.js");
ASSOCIATED_TOKEN_PROGRAM_ID,
} from "@solana/spl-token";
import { Misc } from "../target/types/misc";
const utf8 = anchor.utils.bytes.utf8;
const assert = require("assert");
const miscIdl = require("../target/idl/misc.json");
describe("misc", () => {
// Configure the client to use the local cluster.
@ -231,9 +233,8 @@ describe("misc", () => {
assert.ok(false);
} catch (err) {
const errMsg = "A close constraint was violated";
assert.equal(err.toString(), errMsg);
assert.equal(err.msg, errMsg);
assert.equal(err.code, 2011);
assert.equal(err.error.errorMessage, errMsg);
assert.equal(err.error.errorCode.number, 2011);
}
});
@ -702,7 +703,7 @@ describe("misc", () => {
});
},
(err) => {
assert.equal(err.code, 2009);
assert.equal(err.error.errorCode.number, 2009);
return true;
}
);
@ -737,7 +738,7 @@ describe("misc", () => {
});
},
(err) => {
assert.equal(err.code, 2015);
assert.equal(err.error.errorCode.number, 2015);
return true;
}
);
@ -872,7 +873,7 @@ describe("misc", () => {
},
}),
(err) => {
assert.equal(err.code, 2006);
assert.equal(err.error.errorCode.number, 2006);
return true;
}
);
@ -990,8 +991,10 @@ describe("misc", () => {
},
});
assert.ok(false);
} catch (err) {
assert.equal(err.code, 2004);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2004);
}
});
@ -1027,8 +1030,10 @@ describe("misc", () => {
},
});
assert.ok(false);
} catch (err) {
assert.equal(err.code, 2006);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2006);
}
});
@ -1054,8 +1059,10 @@ describe("misc", () => {
signers: [newAcc],
});
assert.ok(false);
} catch (err) {
assert.equal(err.code, 2019);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2019);
}
});
@ -1086,8 +1093,10 @@ describe("misc", () => {
signers: [mint],
});
assert.ok(false);
} catch (err) {
assert.equal(err.code, 2016);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2016);
}
});
@ -1118,8 +1127,10 @@ describe("misc", () => {
signers: [mint],
});
assert.ok(false);
} catch (err) {
assert.equal(err.code, 2017);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2017);
}
});
@ -1150,8 +1161,10 @@ describe("misc", () => {
signers: [mint],
});
assert.ok(false);
} catch (err) {
assert.equal(err.code, 2018);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2018);
}
});
@ -1195,8 +1208,10 @@ describe("misc", () => {
signers: [token],
});
assert.ok(false);
} catch (err) {
assert.equal(err.code, 2015);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2015);
}
});
@ -1252,8 +1267,10 @@ describe("misc", () => {
signers: [token],
});
assert.ok(false);
} catch (err) {
assert.equal(err.code, 2014);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2014);
}
});
@ -1303,8 +1320,10 @@ describe("misc", () => {
},
});
assert.ok(false);
} catch (err) {
assert.equal(err.code, 2015);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2015);
}
});
@ -1366,8 +1385,10 @@ describe("misc", () => {
},
});
assert.ok(false);
} catch (err) {
assert.equal(err.code, 2014);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2014);
}
});
@ -1430,8 +1451,10 @@ describe("misc", () => {
},
});
assert.ok(false);
} catch (err) {
assert.equal(err.code, 3014);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 3014);
}
});
@ -1456,8 +1479,10 @@ describe("misc", () => {
signers: [data],
});
assert.ok(false);
} catch (err) {
assert.equal(err.code, 2005);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2005);
}
});
@ -1582,9 +1607,14 @@ describe("misc", () => {
},
});
assert.ok(false);
} catch (err) {
assert.equal(2005, err.code);
assert.equal("A rent exempt constraint was violated", err.msg);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2005);
assert.equal(
"A rent exemption constraint was violated",
err.error.errorMessage
);
}
});
@ -1611,8 +1641,10 @@ describe("misc", () => {
},
});
assert.ok(false);
} catch (err) {
assert.equal(err.code, 2006);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2006);
}
// matching bump seed for wrong address but derived from wrong program
@ -1624,8 +1656,10 @@ describe("misc", () => {
},
});
assert.ok(false);
} catch (err) {
assert.equal(err.code, 2006);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2006);
}
// correct inputs should lead to successful tx
@ -1661,8 +1695,10 @@ describe("misc", () => {
},
});
assert.ok(false);
} catch (err) {
assert.equal(err.code, 2006);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2006);
}
// same seeds but derived from wrong program
@ -1674,8 +1710,10 @@ describe("misc", () => {
},
});
assert.ok(false);
} catch (err) {
assert.equal(err.code, 2006);
} catch (_err) {
assert.ok(_err instanceof AnchorError);
const err: AnchorError = _err;
assert.equal(err.error.errorCode.number, 2006);
}
// correct inputs should lead to successful tx

10
tests/misc/tsconfig.json Normal file
View File

@ -0,0 +1,10 @@
{
"compilerOptions": {
"types": ["mocha", "chai"],
"typeRoots": ["./node_modules/@types"],
"lib": ["es2015"],
"module": "commonjs",
"target": "es6",
"esModuleInterop": true
}
}

View File

@ -42,6 +42,8 @@
"devDependencies": {
"@types/node": "^14.14.37",
"chai": "^4.3.4",
"@types/chai": "^4.3.0",
"@types/mocha": "^9.1.0",
"mocha": "^9.1.3",
"ts-mocha": "^8.0.0",
"typescript": "^4.4.4",

View File

@ -52,9 +52,8 @@ describe("system_accounts", () => {
assert.ok(false);
} catch (err) {
const errMsg = "The given account is not owned by the system program";
assert.equal(err.toString(), errMsg);
assert.equal(err.msg, errMsg);
assert.equal(err.code, 3011);
assert.equal(err.error.errorMessage, errMsg);
assert.equal(err.error.errorCode.number, 3011);
}
});
});

View File

@ -18,7 +18,7 @@ describe("sysvars", () => {
console.log("Your transaction signature", tx);
});
it("Fails when the wrote pubkeys are provided", async () => {
it("Fails when the wrong pubkeys are provided", async () => {
try {
await program.methods
.sysvars()
@ -31,9 +31,8 @@ describe("sysvars", () => {
assert.ok(false);
} catch (err) {
const errMsg = "The given public key does not match the required sysvar";
assert.strictEqual(err.toString(), errMsg);
assert.strictEqual(err.msg, errMsg);
assert.strictEqual(err.code, 3015);
assert.strictEqual(err.error.errorMessage, errMsg);
assert.strictEqual(err.error.errorCode.number, 3015);
}
});
});

View File

@ -153,6 +153,11 @@
dependencies:
"@types/node" "*"
"@types/chai@^4.3.0":
version "4.3.0"
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.0.tgz#23509ebc1fa32f1b4d50d6a66c4032d5b8eaabdc"
integrity sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw==
"@types/connect@^3.4.33":
version "3.4.35"
resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1"
@ -179,6 +184,11 @@
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.176.tgz#641150fc1cda36fbfa329de603bbb175d7ee20c0"
integrity sha512-xZmuPTa3rlZoIbtDUyJKZQimJV3bxCmzMIO2c9Pz9afyDro6kr7R79GwcB6mRhuoPmV2p1Vb66WOJH7F886WKQ==
"@types/mocha@^9.1.0":
version "9.1.0"
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.0.tgz#baf17ab2cca3fcce2d322ebc30454bff487efad5"
integrity sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg==
"@types/node@*":
version "16.11.7"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.7.tgz#36820945061326978c42a01e56b61cd223dfdc42"

View File

@ -34,7 +34,7 @@
},
"dependencies": {
"@project-serum/borsh": "^0.2.5",
"@solana/web3.js": "^1.17.0",
"@solana/web3.js": "^1.36.0",
"base64-js": "^1.5.1",
"bn.js": "^5.1.2",
"bs58": "^4.0.1",
@ -59,7 +59,7 @@
"@types/bn.js": "^4.11.6",
"@types/bs58": "^4.0.1",
"@types/crypto-hash": "^1.1.2",
"@types/jest": "^27.0.3",
"@types/jest": "^27.4.1",
"@types/pako": "^1.0.1",
"@typescript-eslint/eslint-plugin": "^4.6.0",
"@typescript-eslint/parser": "^4.6.0",

View File

@ -2,7 +2,6 @@ import BN from "bn.js";
import * as BufferLayout from "buffer-layout";
import { Layout } from "buffer-layout";
import { PublicKey } from "@solana/web3.js";
import * as utils from "../../utils";
export function uint64(property?: string): Layout<u64 | null> {
return new WrappedLayout(
@ -88,7 +87,7 @@ export class COptionLayout<T> extends Layout<T | null> {
} else if (discriminator === 1) {
return this.layout.decode(b, offset + 4);
}
throw new Error("Invalid coption " + this.property);
throw new Error("Invalid coption " + this.layout.property);
}
getSpan(b: Buffer, offset = 0): number {

View File

@ -1,3 +1,6 @@
import { PublicKey } from "@solana/web3.js";
import * as features from "./utils/features.js";
export class IdlError extends Error {
constructor(message: string) {
super(message);
@ -5,10 +8,212 @@ export class IdlError extends Error {
}
}
interface ErrorCode {
code: string;
number: number;
}
interface FileLine {
file: string;
line: number;
}
type Origin = string | FileLine;
type ComparedAccountNames = [string, string];
type ComparedPublicKeys = [PublicKey, PublicKey];
type ComparedValues = ComparedAccountNames | ComparedPublicKeys;
export class ProgramErrorStack {
constructor(readonly stack: PublicKey[]) {}
public static parse(logs: string[]) {
const programKeyRegex = /^Program (\w*) invoke/;
const successRegex = /^Program \w* success/;
const programStack: PublicKey[] = [];
for (let i = 0; i < logs.length; i++) {
if (successRegex.exec(logs[i])) {
programStack.pop();
continue;
}
const programKey = programKeyRegex.exec(logs[i])?.[1];
if (!programKey) {
continue;
}
programStack.push(new PublicKey(programKey));
}
return new ProgramErrorStack(programStack);
}
}
export class AnchorError extends Error {
readonly error: {
errorCode: ErrorCode;
errorMessage: string;
comparedValues?: ComparedValues;
origin?: Origin;
};
private readonly _programErrorStack: ProgramErrorStack;
constructor(
errorCode: ErrorCode,
errorMessage: string,
readonly errorLogs: string[],
readonly logs: string[],
origin?: Origin,
comparedValues?: ComparedValues
) {
super(errorLogs.join("\n").replace("Program log: ", ""));
this.error = { errorCode, errorMessage, comparedValues, origin };
this._programErrorStack = ProgramErrorStack.parse(logs);
}
public static parse(logs: string[]) {
if (!logs) {
return null;
}
const anchorErrorLogIndex = logs.findIndex((log) =>
log.startsWith("Program log: AnchorError")
);
if (anchorErrorLogIndex === -1) {
return null;
}
const anchorErrorLog = logs[anchorErrorLogIndex];
const errorLogs = [anchorErrorLog];
let comparedValues: ComparedValues | undefined;
if (anchorErrorLogIndex + 1 < logs.length) {
// This catches the comparedValues where the following is logged
// <AnchorError>
// Left:
// <Pubkey>
// Right:
// <Pubkey>
if (logs[anchorErrorLogIndex + 1] === "Program log: Left:") {
const pubkeyRegex = /^Program log: (.*)$/;
const leftPubkey = pubkeyRegex.exec(logs[anchorErrorLogIndex + 2])![1];
const rightPubkey = pubkeyRegex.exec(logs[anchorErrorLogIndex + 4])![1];
comparedValues = [
new PublicKey(leftPubkey),
new PublicKey(rightPubkey),
];
errorLogs.push(
...logs.slice(anchorErrorLogIndex + 1, anchorErrorLogIndex + 5)
);
}
// This catches the comparedValues where the following is logged
// <AnchorError>
// Left: <value>
// Right: <value>
else if (logs[anchorErrorLogIndex + 1].startsWith("Program log: Left:")) {
const valueRegex = /^Program log: (Left|Right): (.*)$/;
const leftValue = valueRegex.exec(logs[anchorErrorLogIndex + 1])![2];
const rightValue = valueRegex.exec(logs[anchorErrorLogIndex + 2])![2];
errorLogs.push(
...logs.slice(anchorErrorLogIndex + 1, anchorErrorLogIndex + 3)
);
comparedValues = [leftValue, rightValue];
}
}
const regexNoInfo = /^Program log: AnchorError occurred\. Error Code: (.*)\. Error Number: (\d*)\. Error Message: (.*)\./;
const noInfoAnchorErrorLog = regexNoInfo.exec(anchorErrorLog);
const regexFileLine = /^Program log: AnchorError thrown in (.*):(\d*)\. Error Code: (.*)\. Error Number: (\d*)\. Error Message: (.*)\./;
const fileLineAnchorErrorLog = regexFileLine.exec(anchorErrorLog);
const regexAccountName = /^Program log: AnchorError caused by account: (.*)\. Error Code: (.*)\. Error Number: (\d*)\. Error Message: (.*)\./;
const accountNameAnchorErrorLog = regexAccountName.exec(anchorErrorLog);
if (noInfoAnchorErrorLog) {
const [
errorCodeString,
errorNumber,
errorMessage,
] = noInfoAnchorErrorLog.slice(1, 4);
const errorCode = {
code: errorCodeString,
number: parseInt(errorNumber),
};
return new AnchorError(
errorCode,
errorMessage,
errorLogs,
logs,
undefined,
comparedValues
);
} else if (fileLineAnchorErrorLog) {
const [
file,
line,
errorCodeString,
errorNumber,
errorMessage,
] = fileLineAnchorErrorLog.slice(1, 6);
const errorCode = {
code: errorCodeString,
number: parseInt(errorNumber),
};
const fileLine = { file, line: parseInt(line) };
return new AnchorError(
errorCode,
errorMessage,
errorLogs,
logs,
fileLine,
comparedValues
);
} else if (accountNameAnchorErrorLog) {
const [
accountName,
errorCodeString,
errorNumber,
errorMessage,
] = accountNameAnchorErrorLog.slice(1, 5);
const origin = accountName;
const errorCode = {
code: errorCodeString,
number: parseInt(errorNumber),
};
return new AnchorError(
errorCode,
errorMessage,
errorLogs,
logs,
origin,
comparedValues
);
} else {
return null;
}
}
get program(): PublicKey {
return this._programErrorStack.stack[
this._programErrorStack.stack.length - 1
];
}
get programErrorStack(): PublicKey[] {
return this._programErrorStack.stack;
}
public toString(): string {
return this.message;
}
}
// An error from a user defined program.
export class ProgramError extends Error {
constructor(readonly code: number, readonly msg: string, ...params: any[]) {
super(...params);
private readonly _programErrorStack?: ProgramErrorStack;
constructor(
readonly code: number,
readonly msg: string,
readonly logs?: string[]
) {
super();
if (logs) {
this._programErrorStack = ProgramErrorStack.parse(logs);
}
}
public static parse(
@ -44,24 +249,71 @@ export class ProgramError extends Error {
// Parse user error.
let errorMsg = idlErrors.get(errorCode);
if (errorMsg !== undefined) {
return new ProgramError(errorCode, errorMsg, errorCode + ": " + errorMsg);
return new ProgramError(errorCode, errorMsg, err.logs);
}
// Parse framework internal error.
errorMsg = LangErrorMessage.get(errorCode);
if (errorMsg !== undefined) {
return new ProgramError(errorCode, errorMsg, errorCode + ": " + errorMsg);
return new ProgramError(errorCode, errorMsg, err.logs);
}
// Unable to parse the error. Just return the untranslated error.
return null;
}
get program(): PublicKey | undefined {
return this._programErrorStack?.stack[
this._programErrorStack.stack.length - 1
];
}
get programErrorStack(): PublicKey[] | undefined {
return this._programErrorStack?.stack;
}
public toString(): string {
return this.msg;
}
}
export function translateError(err: any, idlErrors: Map<number, string>) {
if (features.isSet("debug-logs")) {
console.log("Translating error:", err);
}
const anchorError = AnchorError.parse(err.logs);
if (anchorError) {
return anchorError;
}
const programError = ProgramError.parse(err, idlErrors);
if (programError) {
return programError;
}
if (err.logs) {
const handler = {
get: function (target, prop) {
if (prop === "programErrorStack") {
return target.programErrorStack.stack;
} else if (prop === "program") {
return target.programErrorStack.stack[
err.programErrorStack.stack.length - 1
];
} else {
// this is the normal way to return all other props
// without modifying them.
// @ts-expect-error
return Reflect.get(...arguments);
}
},
};
err.programErrorStack = ProgramErrorStack.parse(err.logs);
return new Proxy(err, handler);
}
return err;
}
const LangErrorCode = {
// Instructions.
InstructionMissing: 100,
@ -166,7 +418,10 @@ const LangErrorMessage = new Map([
[LangErrorCode.ConstraintSigner, "A signer constraint was violated"],
[LangErrorCode.ConstraintRaw, "A raw constraint was violated"],
[LangErrorCode.ConstraintOwner, "An owner constraint was violated"],
[LangErrorCode.ConstraintRentExempt, "A rent exempt constraint was violated"],
[
LangErrorCode.ConstraintRentExempt,
"A rent exemption constraint was violated",
],
[LangErrorCode.ConstraintSeeds, "A seeds constraint was violated"],
[LangErrorCode.ConstraintExecutable, "An executable constraint was violated"],
[LangErrorCode.ConstraintState, "A state constraint was violated"],

View File

@ -3,8 +3,7 @@ import Provider from "../../provider.js";
import { Idl } from "../../idl.js";
import { splitArgsAndCtx } from "../context.js";
import { TransactionFn } from "./transaction.js";
import { ProgramError } from "../../error.js";
import * as features from "../../utils/features.js";
import { translateError } from "../../error.js";
import {
AllInstructions,
InstructionContextFn,
@ -22,18 +21,9 @@ export default class RpcFactory {
const tx = txFn(...args);
const [, ctx] = splitArgsAndCtx(idlIx, [...args]);
try {
const txSig = await provider.send(tx, ctx.signers, ctx.options);
return txSig;
return await provider.send(tx, ctx.signers, ctx.options);
} catch (err) {
if (features.isSet("debug-logs")) {
console.log("Translating error:", err);
}
let translatedErr = ProgramError.parse(err, idlErrors);
if (translatedErr === null) {
throw err;
}
throw translatedErr;
throw translateError(err, idlErrors);
}
};

View File

@ -9,8 +9,7 @@ import { TransactionFn } from "./transaction.js";
import { EventParser, Event } from "../event.js";
import { Coder } from "../../coder/index.js";
import { Idl, IdlEvent } from "../../idl.js";
import { ProgramError } from "../../error.js";
import * as features from "../../utils/features.js";
import { translateError } from "../../error.js";
import {
AllInstructions,
IdlTypes,
@ -37,15 +36,7 @@ export default class SimulateFactory {
try {
resp = await provider!.simulate(tx, ctx.signers, ctx.options);
} catch (err) {
if (features.isSet("debug-logs")) {
console.log("Translating error:", err);
}
let translatedErr = ProgramError.parse(err, idlErrors);
if (translatedErr === null) {
throw err;
}
throw translatedErr;
throw translateError(err, idlErrors);
}
if (resp === undefined) {
throw new Error("Unable to simulate transaction");

View File

@ -5,11 +5,12 @@ import {
Transaction,
TransactionSignature,
ConfirmOptions,
sendAndConfirmRawTransaction,
RpcResponseAndContext,
SimulatedTransactionResponse,
Commitment,
SendTransactionError,
} from "@solana/web3.js";
import { bs58 } from "./utils/bytes/index.js";
import { isBrowser } from "./utils/common.js";
/**
@ -115,13 +116,30 @@ export default class Provider {
const rawTx = tx.serialize();
const txId = await sendAndConfirmRawTransaction(
this.connection,
rawTx,
opts
);
return txId;
try {
return await sendAndConfirmRawTransaction(this.connection, rawTx, opts);
} catch (err) {
// thrown if the underlying 'confirmTransaction' encounters a failed tx
// the 'confirmTransaction' error does not return logs so we make another rpc call to get them
if (err instanceof ConfirmError) {
// choose the shortest available commitment for 'getTransaction'
// (the json RPC does not support any shorter than "confirmed" for 'getTransaction')
// because that will see the tx sent with `sendAndConfirmRawTransaction` no matter which
// commitment `sendAndConfirmRawTransaction` used
const failedTx = await this.connection.getTransaction(
bs58.encode(tx.signature!),
{ commitment: "confirmed" }
);
if (!failedTx) {
throw err;
} else {
const logs = failedTx.meta?.logMessages;
throw !logs ? err : new SendTransactionError(err.message, logs);
}
} else {
throw err;
}
}
}
/**
@ -253,6 +271,45 @@ async function simulateTransaction(
return res.result;
}
// Copy of Connection.sendAndConfirmRawTransaction that throws
// a better error if 'confirmTransaction` returns an error status
async function sendAndConfirmRawTransaction(
connection: Connection,
rawTransaction: Buffer,
options?: ConfirmOptions
): Promise<TransactionSignature> {
const sendOptions = options && {
skipPreflight: options.skipPreflight,
preflightCommitment: options.preflightCommitment || options.commitment,
};
const signature = await connection.sendRawTransaction(
rawTransaction,
sendOptions
);
const status = (
await connection.confirmTransaction(
signature,
options && options.commitment
)
).value;
if (status.err) {
throw new ConfirmError(
`Raw transaction ${signature} failed (${JSON.stringify(status)})`
);
}
return signature;
}
class ConfirmError extends Error {
constructor(message?: string) {
super(message);
}
}
/**
* Sets the default provider on the client.
*/

332
ts/tests/error.spec.ts Normal file
View File

@ -0,0 +1,332 @@
import { ProgramErrorStack, AnchorError } from "../src/error";
describe("ProgramErrorStack", () => {
test("basic", () => {
const logs = [
"Program ERRM6YCMsccM22TEaPuu35KVU4iCY3GLCz4qMsKLYReE invoke [1]",
"Program ERRM6YCMsccM22TEaPuu35KVU4iCY3GLCz4qMsKLYReE consumed 3797 of 200000 compute units",
"Program ERRM6YCMsccM22TEaPuu35KVU4iCY3GLCz4qMsKLYReE failed: custom program error: 0x29",
];
expect(
ProgramErrorStack.parse(logs).stack.map((publicKey) =>
publicKey.toString()
)
).toEqual(["ERRM6YCMsccM22TEaPuu35KVU4iCY3GLCz4qMsKLYReE"]);
});
it("basic multiple ix", () => {
const logs = [
"Program 9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin invoke [1]",
"Program 9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin consumed 4308 of 200000 compute units",
"Program 9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin success",
"Program ERRM6YCMsccM22TEaPuu35KVU4iCY3GLCz4qMsKLYReE invoke [1]",
"Program ERRM6YCMsccM22TEaPuu35KVU4iCY3GLCz4qMsKLYReE consumed 3797 of 200000 compute units",
"Program ERRM6YCMsccM22TEaPuu35KVU4iCY3GLCz4qMsKLYReE failed: custom program error: 0x29",
];
expect(
ProgramErrorStack.parse(logs).stack.map((publicKey) =>
publicKey.toString()
)
).toEqual(["ERRM6YCMsccM22TEaPuu35KVU4iCY3GLCz4qMsKLYReE"]);
});
it("failed inner ix", () => {
const logs = [
"Program Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS invoke [1]",
"Program log: Instruction: Create",
"Program ERRM6YCMsccM22TEaPuu35KVU4iCY3GLCz4qMsKLYReE invoke [2]",
"Program log: AnchorError thrown in programs/switchboard_v2/src/actions/aggregator_save_result_action.rs:235. Error Code: OracleMismatchError. Error Number: 6021. Error Message: An unexpected oracle account was provided for the transaction..",
"Program Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS consumed 12619 of 1400000 compute units",
"Program Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS failed: Program failed to complete",
];
expect(
ProgramErrorStack.parse(logs).stack.map((publicKey) =>
publicKey.toString()
)
).toEqual([
"Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS",
"ERRM6YCMsccM22TEaPuu35KVU4iCY3GLCz4qMsKLYReE",
]);
});
it("ignore successful inner ix", () => {
const logs = [
"Program Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS invoke [1]",
"Program log: Instruction: Create",
"Program 11111111111111111111111111111111 invoke [2]",
"Program 11111111111111111111111111111111 success",
"Program log: panicked at programs/floats/src/lib.rs:17:9",
"Program Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS consumed 12619 of 1400000 compute units",
"Program failed to complete: BPF program panicked",
"Program Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS failed: Program failed to complete",
];
expect(
ProgramErrorStack.parse(logs).stack.map((publicKey) =>
publicKey.toString()
)
).toEqual(["Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"]);
});
it("ignore successful inner ix but don't ignore failing inner ix", () => {
const logs = [
"Program Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS invoke [1]",
"Program log: Instruction: Create",
"Program 11111111111111111111111111111111 invoke [2]",
"Program 11111111111111111111111111111111 success",
"Program ERRM6YCMsccM22TEaPuu35KVU4iCY3GLCz4qMsKLYReE invoke [2]",
"Program log: panicked at programs/floats/src/lib.rs:17:9",
"Program Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS consumed 12619 of 1400000 compute units",
"Program failed to complete: BPF program panicked",
"Program Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS failed: Program failed to complete",
];
expect(
ProgramErrorStack.parse(logs).stack.map((publicKey) =>
publicKey.toString()
)
).toEqual([
"Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS",
"ERRM6YCMsccM22TEaPuu35KVU4iCY3GLCz4qMsKLYReE",
]);
});
it("ignore successful inner ix but don't ignore failing inner ix - big nested", () => {
const logs = [
"Program Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS invoke [1]",
"Program log: Instruction: Create",
"Program 11111111111111111111111111111111 invoke [2]",
"Program 11111111111111111111111111111111 success",
"Program 1119iqpxV28XnisGGQVMHsABdWZAx9PjtwegepRhGm5 invoke [2]",
"Program 1119iqpxV28XnisGGQVMHsABdWZAx9PjtwegepRhGm5 consumed 4308 of 200000 compute units",
"Program 1119iqpxV28XnisGGQVMHsABdWZAx9PjtwegepRhGm5 success",
"Program 222fsxyjMZSSpT9gpucChbiFmjZC2GtaZmKsBkh66KMZ invoke [2]",
"Program 333fE7qebyWBjcaCJcVmkzwrheA1Ka9bjGChuhVD9iQr invoke [3]",
"Program 444D5MLf9UbeJBiuFw5WzVG3bMejweunZHPboWm2oTsh invoke [4]",
"Program 444D5MLf9UbeJBiuFw5WzVG3bMejweunZHPboWm2oTsh consumed 14343 of 200000 compute units",
"Program 444D5MLf9UbeJBiuFw5WzVG3bMejweunZHPboWm2oTsh success",
"Program 555CBVR14jAYjK8jRE5kurBACiSNYXkffciRSG2R3krX invoke [4]",
"Program 555CBVR14jAYjK8jRE5kurBACiSNYXkffciRSG2R3krX consumed 163337 of 200000 compute units",
"Program 555CBVR14jAYjK8jRE5kurBACiSNYXkffciRSG2R3krX success",
"Program 666UBGVHWNP7qNqUdnYz86owJ8oErztVvgeF5Dd5v8cR invoke [4]",
"Program 666UBGVHWNP7qNqUdnYz86owJ8oErztVvgeF5Dd5v8cR success",
"Program 333fE7qebyWBjcaCJcVmkzwrheA1Ka9bjGChuhVD9iQr success",
"Program 222fsxyjMZSSpT9gpucChbiFmjZC2GtaZmKsBkh66KMZ success",
"Program 777UGK3pU4ygVWwnn7MDnetec1nSVg4Xi53DFSHu9D6A invoke [2]",
"Program 888E49S65VpyDmydi6juT7tsSwNyD3ZEVkV8te1rL3iH invoke [3]",
"Program 999X95icuyGzfYoeP6SPMb8aMn6ahfCpAt9VPddSNPPi invoke [4]",
"Program 999X95icuyGzfYoeP6SPMb8aMn6ahfCpAt9VPddSNPPi success",
"Program ERRM6YCMsccM22TEaPuu35KVU4iCY3GLCz4qMsKLYReE invoke [4]",
"Program log: panicked at programs/floats/src/lib.rs:17:9",
"Program Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS consumed 12619 of 1400000 compute units",
"Program failed to complete: BPF program panicked",
"Program Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS failed: Program failed to complete",
];
expect(
ProgramErrorStack.parse(logs).stack.map((publicKey) =>
publicKey.toString()
)
).toEqual([
"Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS",
"777UGK3pU4ygVWwnn7MDnetec1nSVg4Xi53DFSHu9D6A",
"888E49S65VpyDmydi6juT7tsSwNyD3ZEVkV8te1rL3iH",
"ERRM6YCMsccM22TEaPuu35KVU4iCY3GLCz4qMsKLYReE",
]);
});
});
describe("AnchorError", () => {
it("FileLine AnchorError with Pubkeys", () => {
const logs = [
"Program SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f invoke [1]",
"Program log: Instruction: AggregatorSaveResult",
"Program log: AnchorError thrown in programs/switchboard_v2/src/actions/aggregator_save_result_action.rs:235. Error Code: OracleMismatchError. Error Number: 6021. Error Message: An unexpected oracle account was provided for the transaction..",
"Program log: Left:",
"Program log: Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS",
"Program log: Right:",
"Program log: SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f",
"Program SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f consumed 28928 of 200000 compute units",
"Program SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f failed: custom program error: 0x1785",
];
const anchorError = AnchorError.parse(logs)!;
expect(anchorError.program.toString()).toEqual(
"SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f"
);
expect(anchorError.error.errorCode).toEqual({
code: "OracleMismatchError",
number: 6021,
});
expect(anchorError.error.errorMessage).toEqual(
"An unexpected oracle account was provided for the transaction."
);
expect(anchorError.error.origin).toEqual({
file:
"programs/switchboard_v2/src/actions/aggregator_save_result_action.rs",
line: 235,
});
expect(
anchorError.error.comparedValues!.map((pk) => pk.toString())
).toEqual([
"Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS",
"SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f",
]);
expect(
anchorError.programErrorStack!.map((publicKey) => publicKey.toString())
).toEqual(["SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f"]);
expect(anchorError.errorLogs).toEqual([
"Program log: AnchorError thrown in programs/switchboard_v2/src/actions/aggregator_save_result_action.rs:235. Error Code: OracleMismatchError. Error Number: 6021. Error Message: An unexpected oracle account was provided for the transaction..",
"Program log: Left:",
"Program log: Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS",
"Program log: Right:",
"Program log: SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f",
]);
});
it("FileLine AnchorError with Values", () => {
const logs = [
"Program SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f invoke [1]",
"Program log: Instruction: AggregatorSaveResult",
"Program log: AnchorError thrown in programs/switchboard_v2/src/actions/aggregator_save_result_action.rs:235. Error Code: OracleMismatchError. Error Number: 6021. Error Message: An unexpected oracle account was provided for the transaction..",
"Program log: Left: 1337",
"Program log: Right: 4220",
"Program SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f consumed 28928 of 200000 compute units",
"Program SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f failed: custom program error: 0x1785",
];
const anchorError = AnchorError.parse(logs)!;
expect(anchorError.program.toString()).toEqual(
"SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f"
);
expect(anchorError.error.errorCode).toEqual({
code: "OracleMismatchError",
number: 6021,
});
expect(anchorError.error.errorMessage).toEqual(
"An unexpected oracle account was provided for the transaction."
);
expect(anchorError.error.origin).toEqual({
file:
"programs/switchboard_v2/src/actions/aggregator_save_result_action.rs",
line: 235,
});
expect(anchorError.error.comparedValues!).toEqual(["1337", "4220"]);
expect(
anchorError.programErrorStack!.map((publicKey) => publicKey.toString())
).toEqual(["SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f"]);
expect(anchorError.errorLogs).toEqual([
"Program log: AnchorError thrown in programs/switchboard_v2/src/actions/aggregator_save_result_action.rs:235. Error Code: OracleMismatchError. Error Number: 6021. Error Message: An unexpected oracle account was provided for the transaction..",
"Program log: Left: 1337",
"Program log: Right: 4220",
]);
});
it("AccountName AnchorError with Pubkeys", () => {
const logs = [
"Program SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f invoke [1]",
"Program log: Instruction: AggregatorSaveResult",
"Program log: AnchorError caused by account: some_account. Error Code: OracleMismatchError. Error Number: 6021. Error Message: An unexpected oracle account was provided for the transaction..",
"Program log: Left:",
"Program log: Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS",
"Program log: Right:",
"Program log: SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f",
"Program SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f consumed 28928 of 200000 compute units",
"Program SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f failed: custom program error: 0x1785",
];
const anchorError = AnchorError.parse(logs)!;
expect(anchorError.program.toString()).toEqual(
"SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f"
);
expect(anchorError.error.errorCode).toEqual({
code: "OracleMismatchError",
number: 6021,
});
expect(anchorError.error.errorMessage).toEqual(
"An unexpected oracle account was provided for the transaction."
);
expect(anchorError.error.origin).toEqual("some_account");
expect(
anchorError.error.comparedValues!.map((pk) => pk.toString())
).toEqual([
"Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS",
"SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f",
]);
expect(
anchorError.programErrorStack!.map((publicKey) => publicKey.toString())
).toEqual(["SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f"]);
expect(anchorError.errorLogs).toEqual([
"Program log: AnchorError caused by account: some_account. Error Code: OracleMismatchError. Error Number: 6021. Error Message: An unexpected oracle account was provided for the transaction..",
"Program log: Left:",
"Program log: Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS",
"Program log: Right:",
"Program log: SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f",
]);
});
it("AccountName AnchorError with Values", () => {
const logs = [
"Program SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f invoke [1]",
"Program log: Instruction: AggregatorSaveResult",
"Program log: AnchorError caused by account: some_account. Error Code: OracleMismatchError. Error Number: 6021. Error Message: An unexpected oracle account was provided for the transaction..",
"Program log: Left: 1337",
"Program log: Right: 4220",
"Program SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f consumed 28928 of 200000 compute units",
"Program SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f failed: custom program error: 0x1785",
];
const anchorError = AnchorError.parse(logs)!;
expect(anchorError.program.toString()).toEqual(
"SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f"
);
expect(anchorError.error.errorCode).toEqual({
code: "OracleMismatchError",
number: 6021,
});
expect(anchorError.error.errorMessage).toEqual(
"An unexpected oracle account was provided for the transaction."
);
expect(anchorError.error.origin).toEqual("some_account");
expect(anchorError.error.comparedValues!).toEqual(["1337", "4220"]);
expect(
anchorError.programErrorStack!.map((publicKey) => publicKey.toString())
).toEqual(["SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f"]);
expect(anchorError.errorLogs).toEqual([
"Program log: AnchorError caused by account: some_account. Error Code: OracleMismatchError. Error Number: 6021. Error Message: An unexpected oracle account was provided for the transaction..",
"Program log: Left: 1337",
"Program log: Right: 4220",
]);
});
it("Empty AnchorError", () => {
const logs = [
"Program SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f invoke [1]",
"Program log: Instruction: AggregatorSaveResult",
"Program log: AnchorError occurred. Error Code: OracleMismatchError. Error Number: 6021. Error Message: An unexpected oracle account was provided for the transaction..",
"Program SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f consumed 28928 of 200000 compute units",
"Program SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f failed: custom program error: 0x1785",
];
const anchorError = AnchorError.parse(logs)!;
expect(anchorError.program.toString()).toEqual(
"SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f"
);
expect(anchorError.error.errorCode).toEqual({
code: "OracleMismatchError",
number: 6021,
});
expect(anchorError.error.errorMessage).toEqual(
"An unexpected oracle account was provided for the transaction."
);
expect(anchorError.error.origin).toBeUndefined();
expect(anchorError.error.comparedValues).toBeUndefined();
expect(
anchorError.programErrorStack!.map((publicKey) => publicKey.toString())
).toEqual(["SW1TCH7qEPTdLsDHRgPuMQjbQxKdH2aBStViMFnt64f"]);
expect(anchorError.errorLogs).toEqual([
"Program log: AnchorError occurred. Error Code: OracleMismatchError. Error Number: 6021. Error Message: An unexpected oracle account was provided for the transaction..",
]);
});
});

View File

@ -649,6 +649,27 @@
minimatch "^3.0.4"
strip-json-comments "^3.1.1"
"@ethersproject/bytes@^5.6.0":
version "5.6.0"
resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.6.0.tgz#81652f2a0e04533575befadce555213c11d8aa20"
integrity sha512-3hJPlYemb9V4VLfJF5BfN0+55vltPZSHU3QKUyP9M3Y2TcajbiRrz65UG+xVHOzBereB1b9mn7r12o177xgN7w==
dependencies:
"@ethersproject/logger" "^5.6.0"
"@ethersproject/logger@^5.6.0":
version "5.6.0"
resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.6.0.tgz#d7db1bfcc22fd2e4ab574cba0bb6ad779a9a3e7a"
integrity sha512-BiBWllUROH9w+P21RzoxJKzqoqpkyM1pRnEKG69bulE9TSQD8SAIvTQqIMZmmCO8pUNkgLP1wndX1gKghSpBmg==
"@ethersproject/sha2@^5.5.0":
version "5.6.0"
resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.6.0.tgz#364c4c11cc753bda36f31f001628706ebadb64d9"
integrity sha512-1tNWCPFLu1n3JM9t4/kytz35DkuF9MxqkGGEHNauEbaARdm2fafnOyw1s0tIQDPKF/7bkP1u3dbrmjpn5CelyA==
dependencies:
"@ethersproject/bytes" "^5.6.0"
"@ethersproject/logger" "^5.6.0"
hash.js "1.1.7"
"@istanbuljs/load-nyc-config@^1.0.0":
version "1.1.0"
resolved "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz"
@ -927,21 +948,28 @@
dependencies:
"@sinonjs/commons" "^1.7.0"
"@solana/web3.js@^1.17.0":
version "1.17.0"
resolved "https://registry.npmjs.org/@solana/web3.js/-/web3.js-1.17.0.tgz"
integrity sha512-PBOHY260CudciLwBgwt1U8upwCS1Jq0BbS6EVyX0tz6Tj14Dp4i87dQNyntentNiGQQ+yWBIk4vJEm+PMCSd/A==
"@solana/buffer-layout@^3.0.0":
version "3.0.0"
resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-3.0.0.tgz#b9353caeb9a1589cb77a1b145bcb1a9a93114326"
integrity sha512-MVdgAKKL39tEs0l8je0hKaXLQFb7Rdfb0Xg2LjFZd8Lfdazkg6xiS98uAZrEKvaoF3i4M95ei9RydkGIDMeo3w==
dependencies:
buffer "~6.0.3"
"@solana/web3.js@^1.36.0":
version "1.36.0"
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.36.0.tgz#79d7d5217b49b80139f4de68953adc5b9a9a264f"
integrity sha512-RNT1451iRR7TyW7EJKMCrH/0OXawIe4zVm0DWQASwXlR/u1jmW6FrmH0lujIh7cGTlfOVbH+2ZU9AVUPLBFzwA==
dependencies:
"@babel/runtime" "^7.12.5"
"@ethersproject/sha2" "^5.5.0"
"@solana/buffer-layout" "^3.0.0"
bn.js "^5.0.0"
borsh "^0.4.0"
bs58 "^4.0.1"
buffer "6.0.1"
buffer-layout "^1.2.0"
crypto-hash "^1.2.2"
cross-fetch "^3.1.4"
jayson "^3.4.4"
js-sha3 "^0.8.0"
node-fetch "^2.6.1"
rpc-websockets "^7.4.2"
secp256k1 "^4.0.2"
superstruct "^0.14.2"
@ -1069,12 +1097,12 @@
dependencies:
"@types/istanbul-lib-report" "*"
"@types/jest@^27.0.3":
version "27.0.3"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.0.3.tgz#0cf9dfe9009e467f70a342f0f94ead19842a783a"
integrity sha512-cmmwv9t7gBYt7hNKH5Spu7Kuu/DotGa+Ff+JGRKZ4db5eh8PnKS4LuebJ3YLUoyOyIHraTGyULn23YtEAm0VSg==
"@types/jest@^27.4.1":
version "27.4.1"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.4.1.tgz#185cbe2926eaaf9662d340cc02e548ce9e11ab6d"
integrity sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw==
dependencies:
jest-diff "^27.0.0"
jest-matcher-utils "^27.0.0"
pretty-format "^27.0.0"
"@types/json-schema@^7.0.3":
@ -1568,6 +1596,14 @@ buffer@6.0.1:
base64-js "^1.3.1"
ieee754 "^1.2.1"
buffer@~6.0.3:
version "6.0.3"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6"
integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==
dependencies:
base64-js "^1.3.1"
ieee754 "^1.2.1"
bufferutil@^4.0.1:
version "4.0.3"
resolved "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.3.tgz"
@ -1836,7 +1872,7 @@ create-require@^1.1.0:
resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
cross-fetch@^3.1.5:
cross-fetch@^3.1.4, cross-fetch@^3.1.5:
version "3.1.5"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f"
integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==
@ -1852,7 +1888,7 @@ cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
shebang-command "^2.0.0"
which "^2.0.1"
crypto-hash@*, crypto-hash@^1.2.2, crypto-hash@^1.3.0:
crypto-hash@*, crypto-hash@^1.3.0:
version "1.3.0"
resolved "https://registry.npmjs.org/crypto-hash/-/crypto-hash-1.3.0.tgz"
integrity sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg==
@ -1964,6 +2000,11 @@ diff-sequences@^27.0.6:
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.0.6.tgz#3305cb2e55a033924054695cc66019fd7f8e5723"
integrity sha512-ag6wfpBFyNXZ0p8pcuIDS//D8H062ZQJ3fzYxjpmeKjnz8W4pekL3AI8VohmyZmsWW2PWaHgjsmqR6L13101VQ==
diff-sequences@^27.5.1:
version "27.5.1"
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.5.1.tgz#eaecc0d327fd68c8d9672a1e64ab8dccb2ef5327"
integrity sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==
diff@^4.0.1:
version "4.0.2"
resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz"
@ -2569,7 +2610,7 @@ has@^1.0.3:
dependencies:
function-bind "^1.1.1"
hash.js@^1.0.0, hash.js@^1.0.3:
hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3:
version "1.1.7"
resolved "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz"
integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==
@ -2976,7 +3017,7 @@ jest-config@27.3.1, jest-config@^27.3.1:
micromatch "^4.0.4"
pretty-format "^27.3.1"
jest-diff@^27.0.0, jest-diff@^27.3.1:
jest-diff@^27.3.1:
version "27.3.1"
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.3.1.tgz#d2775fea15411f5f5aeda2a5e02c2f36440f6d55"
integrity sha512-PCeuAH4AWUo2O5+ksW4pL9v5xJAcIKPUPfIhZBcG1RKv/0+dvaWTQK1Nrau8d67dp65fOqbeMdoil+6PedyEPQ==
@ -2986,6 +3027,16 @@ jest-diff@^27.0.0, jest-diff@^27.3.1:
jest-get-type "^27.3.1"
pretty-format "^27.3.1"
jest-diff@^27.5.1:
version "27.5.1"
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-27.5.1.tgz#a07f5011ac9e6643cf8a95a462b7b1ecf6680def"
integrity sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==
dependencies:
chalk "^4.0.0"
diff-sequences "^27.5.1"
jest-get-type "^27.5.1"
pretty-format "^27.5.1"
jest-docblock@^27.0.6:
version "27.0.6"
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-27.0.6.tgz#cc78266acf7fe693ca462cbbda0ea4e639e4e5f3"
@ -3034,6 +3085,11 @@ jest-get-type@^27.3.1:
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.3.1.tgz#a8a2b0a12b50169773099eee60a0e6dd11423eff"
integrity sha512-+Ilqi8hgHSAdhlQ3s12CAVNd8H96ZkQBfYoXmArzZnOfAtVAJEiPDBirjByEblvG/4LPJmkL+nBqPO3A1YJAEg==
jest-get-type@^27.5.1:
version "27.5.1"
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-27.5.1.tgz#3cd613c507b0f7ace013df407a1c1cd578bcb4f1"
integrity sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==
jest-haste-map@^27.3.1:
version "27.3.1"
resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-27.3.1.tgz#7656fbd64bf48bda904e759fc9d93e2c807353ee"
@ -3086,6 +3142,16 @@ jest-leak-detector@^27.3.1:
jest-get-type "^27.3.1"
pretty-format "^27.3.1"
jest-matcher-utils@^27.0.0:
version "27.5.1"
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab"
integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==
dependencies:
chalk "^4.0.0"
jest-diff "^27.5.1"
jest-get-type "^27.5.1"
pretty-format "^27.5.1"
jest-matcher-utils@^27.3.1:
version "27.3.1"
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.3.1.tgz#257ad61e54a6d4044e080d85dbdc4a08811e9c1c"
@ -3746,7 +3812,7 @@ node-addon-api@^2.0.0:
resolved "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz"
integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==
node-fetch@2.6.7, node-fetch@^2.6.1:
node-fetch@2.6.7:
version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
@ -4006,6 +4072,15 @@ pretty-format@^27.0.0, pretty-format@^27.3.1:
ansi-styles "^5.0.0"
react-is "^17.0.1"
pretty-format@^27.5.1:
version "27.5.1"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e"
integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==
dependencies:
ansi-regex "^5.0.1"
ansi-styles "^5.0.0"
react-is "^17.0.1"
process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz"