lang: Framework defined error codes (#354)
This commit is contained in:
parent
39d0c62a2c
commit
ba99c9c920
|
@ -20,6 +20,10 @@ incremented for features.
|
|||
|
||||
* lang: Allows one to use `remaining_accounts` with `CpiContext` by implementing the `ToAccountMetas` trait on `CpiContext` ([#351](https://github.com/project-serum/anchor/pull/351/files)).
|
||||
|
||||
### Breaking
|
||||
|
||||
* lang, ts: Framework defined error codes are introduced, reserving error codes 0-300 for Anchor, and 300 and up for user defined error codes ([#354](https://github.com/project-serum/anchor/pull/354)).
|
||||
|
||||
## [0.7.0] - 2021-05-31
|
||||
|
||||
### Features
|
||||
|
|
|
@ -4066,9 +4066,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.2.1"
|
||||
version = "2.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b"
|
||||
checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
|
|
|
@ -89,7 +89,7 @@ describe("chat", () => {
|
|||
assert.ok(msg.from.equals(user));
|
||||
assert.ok(data.startsWith(messages[idx]));
|
||||
} else {
|
||||
assert.ok(new anchor.web3.PublicKey());
|
||||
assert.ok(anchor.web3.PublicKey.default);
|
||||
assert.ok(
|
||||
JSON.stringify(msg.data) === JSON.stringify(new Array(280).fill(0))
|
||||
);
|
||||
|
|
|
@ -6,6 +6,7 @@ use anchor_lang::prelude::*;
|
|||
#[program]
|
||||
mod errors {
|
||||
use super::*;
|
||||
|
||||
pub fn hello(_ctx: Context<Hello>) -> Result<()> {
|
||||
Err(MyError::Hello.into())
|
||||
}
|
||||
|
@ -17,11 +18,48 @@ mod errors {
|
|||
pub fn hello_next(_ctx: Context<Hello>) -> Result<()> {
|
||||
Err(MyError::HelloNext.into())
|
||||
}
|
||||
|
||||
pub fn mut_error(_ctx: Context<MutError>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn belongs_to_error(_ctx: Context<BelongsToError>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn signer_error(_ctx: Context<SignerError>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct Hello {}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct MutError<'info> {
|
||||
#[account(mut)]
|
||||
my_account: AccountInfo<'info>,
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct BelongsToError<'info> {
|
||||
#[account(init, belongs_to = owner)]
|
||||
my_account: ProgramAccount<'info, BelongsToAccount>,
|
||||
owner: AccountInfo<'info>,
|
||||
rent: Sysvar<'info, Rent>,
|
||||
}
|
||||
|
||||
#[derive(Accounts)]
|
||||
pub struct SignerError<'info> {
|
||||
#[account(signer)]
|
||||
my_account: AccountInfo<'info>,
|
||||
}
|
||||
|
||||
#[account]
|
||||
pub struct BelongsToAccount {
|
||||
owner: Pubkey,
|
||||
}
|
||||
|
||||
#[error]
|
||||
pub enum MyError {
|
||||
#[msg("This is an error message clients will automatically display")]
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const assert = require("assert");
|
||||
const anchor = require('@project-serum/anchor');
|
||||
const { Account, Transaction, TransactionInstruction } = anchor.web3;
|
||||
|
||||
describe("errors", () => {
|
||||
// Configure the client to use the local cluster.
|
||||
|
@ -16,7 +17,7 @@ describe("errors", () => {
|
|||
"This is an error message clients will automatically display";
|
||||
assert.equal(err.toString(), errMsg);
|
||||
assert.equal(err.msg, errMsg);
|
||||
assert.equal(err.code, 100);
|
||||
assert.equal(err.code, 300);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -28,7 +29,7 @@ describe("errors", () => {
|
|||
const errMsg = "HelloNoMsg";
|
||||
assert.equal(err.toString(), errMsg);
|
||||
assert.equal(err.msg, errMsg);
|
||||
assert.equal(err.code, 100 + 123);
|
||||
assert.equal(err.code, 300 + 123);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -40,7 +41,75 @@ describe("errors", () => {
|
|||
const errMsg = "HelloNext";
|
||||
assert.equal(err.toString(), errMsg);
|
||||
assert.equal(err.msg, errMsg);
|
||||
assert.equal(err.code, 100 + 124);
|
||||
assert.equal(err.code, 300 + 124);
|
||||
}
|
||||
});
|
||||
|
||||
it("Emits a mut error", async () => {
|
||||
try {
|
||||
const tx = await program.rpc.mutError({
|
||||
accounts: {
|
||||
myAccount: anchor.web3.SYSVAR_RENT_PUBKEY,
|
||||
},
|
||||
});
|
||||
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, 140);
|
||||
}
|
||||
});
|
||||
|
||||
it("Emits a belongs to error", async () => {
|
||||
try {
|
||||
const account = new Account();
|
||||
const tx = await program.rpc.belongsToError({
|
||||
accounts: {
|
||||
myAccount: account.publicKey,
|
||||
owner: anchor.web3.SYSVAR_RENT_PUBKEY,
|
||||
rent: anchor.web3.SYSVAR_RENT_PUBKEY,
|
||||
},
|
||||
instructions: [
|
||||
await program.account.belongsToAccount.createInstruction(account),
|
||||
],
|
||||
signers: [account],
|
||||
});
|
||||
assert.ok(false);
|
||||
} catch (err) {
|
||||
const errMsg = "A belongs_to constraint was violated";
|
||||
assert.equal(err.toString(), errMsg);
|
||||
assert.equal(err.msg, errMsg);
|
||||
assert.equal(err.code, 141);
|
||||
}
|
||||
});
|
||||
|
||||
// This test uses a raw transaction and provider instead of a program
|
||||
// instance since the client won't allow one to send a transaction
|
||||
// with an invalid signer account.
|
||||
it("Emits a signer error", async () => {
|
||||
try {
|
||||
const account = new Account();
|
||||
const tx = new Transaction();
|
||||
tx.add(
|
||||
new TransactionInstruction({
|
||||
keys: [
|
||||
{
|
||||
pubkey: anchor.web3.SYSVAR_RENT_PUBKEY,
|
||||
isWritable: false,
|
||||
isSigner: false,
|
||||
},
|
||||
],
|
||||
programId: program.programId,
|
||||
data: program.coder.instruction.encode("signer_error", {}),
|
||||
})
|
||||
);
|
||||
await program.provider.send(tx);
|
||||
assert.ok(false);
|
||||
} catch (err) {
|
||||
const errMsg =
|
||||
"Error: failed to send transaction: Transaction simulation failed: Error processing Instruction 0: custom program error: 0x8e";
|
||||
assert.equal(err.toString(), errMsg);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -31,7 +31,7 @@ module.exports = async function (provider) {
|
|||
});
|
||||
|
||||
// Delete the default whitelist entries.
|
||||
const defaultEntry = { programId: new anchor.web3.PublicKey() };
|
||||
const defaultEntry = { programId: new anchor.web3.PublicKey.default };
|
||||
await lockup.state.rpc.whitelistDelete(defaultEntry, {
|
||||
accounts: {
|
||||
authority: provider.wallet.publicKey,
|
||||
|
|
|
@ -42,12 +42,12 @@ describe("Lockup and Registry", () => {
|
|||
assert.ok(lockupAccount.authority.equals(provider.wallet.publicKey));
|
||||
assert.ok(lockupAccount.whitelist.length === WHITELIST_SIZE);
|
||||
lockupAccount.whitelist.forEach((e) => {
|
||||
assert.ok(e.programId.equals(new anchor.web3.PublicKey()));
|
||||
assert.ok(e.programId.equals(anchor.web3.PublicKey.default));
|
||||
});
|
||||
});
|
||||
|
||||
it("Deletes the default whitelisted addresses", async () => {
|
||||
const defaultEntry = { programId: new anchor.web3.PublicKey() };
|
||||
const defaultEntry = { programId: anchor.web3.PublicKey.default };
|
||||
await lockup.state.rpc.whitelistDelete(defaultEntry, {
|
||||
accounts: {
|
||||
authority: provider.wallet.publicKey,
|
||||
|
@ -116,7 +116,7 @@ describe("Lockup and Registry", () => {
|
|||
await lockup.state.rpc.whitelistAdd(e, { accounts });
|
||||
},
|
||||
(err) => {
|
||||
assert.equal(err.code, 108);
|
||||
assert.equal(err.code, 308);
|
||||
assert.equal(err.msg, "Whitelist is full");
|
||||
return true;
|
||||
}
|
||||
|
@ -216,7 +216,7 @@ describe("Lockup and Registry", () => {
|
|||
});
|
||||
},
|
||||
(err) => {
|
||||
assert.equal(err.code, 107);
|
||||
assert.equal(err.code, 307);
|
||||
assert.equal(err.msg, "Insufficient withdrawal balance.");
|
||||
return true;
|
||||
}
|
||||
|
@ -389,7 +389,7 @@ describe("Lockup and Registry", () => {
|
|||
|
||||
assert.ok(memberAccount.registrar.equals(registrar.publicKey));
|
||||
assert.ok(memberAccount.beneficiary.equals(provider.wallet.publicKey));
|
||||
assert.ok(memberAccount.metadata.equals(new anchor.web3.PublicKey()));
|
||||
assert.ok(memberAccount.metadata.equals(anchor.web3.PublicKey.default));
|
||||
assert.equal(
|
||||
JSON.stringify(memberAccount.balances),
|
||||
JSON.stringify(balances)
|
||||
|
@ -781,7 +781,7 @@ 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: 0x78";
|
||||
const errorCode = "custom program error: 0x140";
|
||||
assert.ok(err.toString().split(errorCode).length === 2);
|
||||
return true;
|
||||
}
|
||||
|
@ -863,7 +863,7 @@ describe("Lockup and Registry", () => {
|
|||
await tryEndUnstake();
|
||||
},
|
||||
(err) => {
|
||||
assert.equal(err.code, 109);
|
||||
assert.equal(err.code, 309);
|
||||
assert.equal(err.msg, "The unstake timelock has not yet expired.");
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -21,14 +21,14 @@ describe("zero-copy", () => {
|
|||
assert.ok(state.authority.equals(program.provider.wallet.publicKey));
|
||||
assert.ok(state.events.length === 250);
|
||||
state.events.forEach((event, idx) => {
|
||||
assert.ok(event.from.equals(new PublicKey()));
|
||||
assert.ok(event.from.equals(PublicKey.default));
|
||||
assert.ok(event.data.toNumber() === 0);
|
||||
});
|
||||
});
|
||||
|
||||
it("Updates zero copy state", async () => {
|
||||
let event = {
|
||||
from: new PublicKey(),
|
||||
from: PublicKey.default,
|
||||
data: new BN(1234),
|
||||
};
|
||||
await program.state.rpc.setEvent(5, event, {
|
||||
|
@ -44,7 +44,7 @@ describe("zero-copy", () => {
|
|||
assert.ok(event.from.equals(event.from));
|
||||
assert.ok(event.data.eq(event.data));
|
||||
} else {
|
||||
assert.ok(event.from.equals(new PublicKey()));
|
||||
assert.ok(event.from.equals(PublicKey.default));
|
||||
assert.ok(event.data.toNumber() === 0);
|
||||
}
|
||||
});
|
||||
|
@ -175,7 +175,7 @@ describe("zero-copy", () => {
|
|||
const account = await program.account.eventQ.fetch(eventQ.publicKey);
|
||||
assert.ok(account.events.length === 25000);
|
||||
account.events.forEach((event) => {
|
||||
assert.ok(event.from.equals(new PublicKey()));
|
||||
assert.ok(event.from.equals(PublicKey.default));
|
||||
assert.ok(event.data.toNumber() === 0);
|
||||
});
|
||||
});
|
||||
|
@ -196,7 +196,7 @@ describe("zero-copy", () => {
|
|||
assert.ok(event.from.equals(program.provider.wallet.publicKey));
|
||||
assert.ok(event.data.toNumber() === 48);
|
||||
} else {
|
||||
assert.ok(event.from.equals(new PublicKey()));
|
||||
assert.ok(event.from.equals(PublicKey.default));
|
||||
assert.ok(event.data.toNumber() === 0);
|
||||
}
|
||||
});
|
||||
|
@ -219,7 +219,7 @@ describe("zero-copy", () => {
|
|||
assert.ok(event.from.equals(program.provider.wallet.publicKey));
|
||||
assert.ok(event.data.toNumber() === 1234);
|
||||
} else {
|
||||
assert.ok(event.from.equals(new PublicKey()));
|
||||
assert.ok(event.from.equals(PublicKey.default));
|
||||
assert.ok(event.data.toNumber() === 0);
|
||||
}
|
||||
});
|
||||
|
@ -245,7 +245,7 @@ describe("zero-copy", () => {
|
|||
assert.ok(event.from.equals(program.provider.wallet.publicKey));
|
||||
assert.ok(event.data.toNumber() === 99);
|
||||
} else {
|
||||
assert.ok(event.from.equals(new PublicKey()));
|
||||
assert.ok(event.from.equals(PublicKey.default));
|
||||
assert.ok(event.data.toNumber() === 0);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -119,11 +119,11 @@ pub fn account(
|
|||
impl anchor_lang::AccountDeserialize for #account_name {
|
||||
fn try_deserialize(buf: &mut &[u8]) -> std::result::Result<Self, ProgramError> {
|
||||
if buf.len() < #discriminator.len() {
|
||||
return Err(ProgramError::AccountDataTooSmall);
|
||||
return Err(anchor_lang::__private::ErrorCode::AccountDiscriminatorNotFound.into());
|
||||
}
|
||||
let given_disc = &buf[..8];
|
||||
if &#discriminator != given_disc {
|
||||
return Err(ProgramError::InvalidInstructionData);
|
||||
return Err(anchor_lang::__private::ErrorCode::AccountDiscriminatorMismatch.into());
|
||||
}
|
||||
Self::try_deserialize_unchecked(buf)
|
||||
}
|
||||
|
@ -144,12 +144,12 @@ pub fn account(
|
|||
|
||||
impl anchor_lang::AccountSerialize for #account_name {
|
||||
fn try_serialize<W: std::io::Write>(&self, writer: &mut W) -> std::result::Result<(), ProgramError> {
|
||||
writer.write_all(&#discriminator).map_err(|_| ProgramError::InvalidAccountData)?;
|
||||
writer.write_all(&#discriminator).map_err(|_| anchor_lang::__private::ErrorCode::AccountDidNotSerialize)?;
|
||||
AnchorSerialize::serialize(
|
||||
self,
|
||||
writer
|
||||
)
|
||||
.map_err(|_| ProgramError::InvalidAccountData)?;
|
||||
.map_err(|_| anchor_lang::__private::ErrorCode::AccountDidNotSerialize)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -157,11 +157,11 @@ pub fn account(
|
|||
impl anchor_lang::AccountDeserialize for #account_name {
|
||||
fn try_deserialize(buf: &mut &[u8]) -> std::result::Result<Self, ProgramError> {
|
||||
if buf.len() < #discriminator.len() {
|
||||
return Err(ProgramError::AccountDataTooSmall);
|
||||
return Err(anchor_lang::__private::ErrorCode::AccountDiscriminatorNotFound.into());
|
||||
}
|
||||
let given_disc = &buf[..8];
|
||||
if &#discriminator != given_disc {
|
||||
return Err(ProgramError::InvalidInstructionData);
|
||||
return Err(anchor_lang::__private::ErrorCode::AccountDiscriminatorMismatch.into());
|
||||
}
|
||||
Self::try_deserialize_unchecked(buf)
|
||||
}
|
||||
|
@ -169,7 +169,7 @@ pub fn account(
|
|||
fn try_deserialize_unchecked(buf: &mut &[u8]) -> std::result::Result<Self, ProgramError> {
|
||||
let mut data: &[u8] = &buf[8..];
|
||||
AnchorDeserialize::deserialize(&mut data)
|
||||
.map_err(|_| ProgramError::InvalidAccountData)
|
||||
.map_err(|_| anchor_lang::__private::ErrorCode::AccountDidNotDeserialize.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -327,8 +327,8 @@ pub fn zero_copy(
|
|||
let account_strct = parse_macro_input!(item as syn::ItemStruct);
|
||||
|
||||
proc_macro::TokenStream::from(quote! {
|
||||
#[derive(anchor_lang::__private::ZeroCopyAccessor, Copy, Clone)]
|
||||
#[repr(packed)]
|
||||
#account_strct
|
||||
#[derive(anchor_lang::__private::ZeroCopyAccessor, Copy, Clone)]
|
||||
#[repr(packed)]
|
||||
#account_strct
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ extern crate proc_macro;
|
|||
|
||||
use anchor_syn::codegen::error as error_codegen;
|
||||
use anchor_syn::parser::error as error_parser;
|
||||
use anchor_syn::ErrorArgs;
|
||||
use syn::parse_macro_input;
|
||||
|
||||
/// Generates `Error` and `type Result<T> = Result<T, Error>` types to be
|
||||
|
@ -47,10 +48,14 @@ use syn::parse_macro_input;
|
|||
/// parsers and IDLs can map error codes to error messages.
|
||||
#[proc_macro_attribute]
|
||||
pub fn error(
|
||||
_args: proc_macro::TokenStream,
|
||||
args: proc_macro::TokenStream,
|
||||
input: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
let args = match args.is_empty() {
|
||||
true => None,
|
||||
false => Some(parse_macro_input!(args as ErrorArgs)),
|
||||
};
|
||||
let mut error_enum = parse_macro_input!(input as syn::ItemEnum);
|
||||
let error = error_codegen::generate(error_parser::parse(&mut error_enum));
|
||||
let error = error_codegen::generate(error_parser::parse(&mut error_enum, args));
|
||||
proc_macro::TokenStream::from(error)
|
||||
}
|
||||
|
|
|
@ -101,15 +101,7 @@ use syn::parse_macro_input;
|
|||
/// use super::*;
|
||||
///
|
||||
/// #[state]
|
||||
/// pub struct CounterAuth {}
|
||||
///
|
||||
/// // TODO: remove this impl block after addressing
|
||||
/// // https://github.com/project-serum/anchor/issues/71.
|
||||
/// impl CounterAuth {
|
||||
/// pub fn new(_ctx: Context<Empty>) -> Result<Self, ProgramError> {
|
||||
/// Ok(Self {})
|
||||
/// }
|
||||
/// }
|
||||
/// pub struct CounterAuth;
|
||||
///
|
||||
/// impl<'info> Auth<'info, Empty> for CounterAuth {
|
||||
/// fn is_authorized(_ctx: Context<Empty>, current: u64, new: u64) -> ProgramResult {
|
||||
|
@ -216,7 +208,7 @@ pub fn interface(
|
|||
#(#args_no_tys),*
|
||||
};
|
||||
let mut ix_data = anchor_lang::AnchorSerialize::try_to_vec(&ix)
|
||||
.map_err(|_| anchor_lang::solana_program::program_error::ProgramError::InvalidInstructionData)?;
|
||||
.map_err(|_| anchor_lang::__private::ErrorCode::InstructionDidNotSerialize)?;
|
||||
let mut data = #sighash_tts.to_vec();
|
||||
data.append(&mut ix_data);
|
||||
let accounts = ctx.accounts.to_account_metas(None);
|
||||
|
|
|
@ -41,7 +41,7 @@ pub fn state(
|
|||
fn size(&self) -> std::result::Result<u64, anchor_lang::solana_program::program_error::ProgramError> {
|
||||
Ok(8 + self
|
||||
.try_to_vec()
|
||||
.map_err(|_| ProgramError::Custom(1))?
|
||||
.map_err(|_| anchor_lang::__private::ErrorCode::AccountDidNotSerialize)?
|
||||
.len() as u64)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::error::ErrorCode;
|
||||
use crate::{Accounts, AccountsExit, AccountsInit, ToAccountInfo, ToAccountInfos, ToAccountMetas};
|
||||
use solana_program::account_info::AccountInfo;
|
||||
use solana_program::entrypoint::ProgramResult;
|
||||
|
@ -11,7 +12,7 @@ impl<'info> Accounts<'info> for AccountInfo<'info> {
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
) -> Result<Self, ProgramError> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ProgramError::NotEnoughAccountKeys);
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
let account = &accounts[0];
|
||||
*accounts = &accounts[1..];
|
||||
|
@ -25,7 +26,7 @@ impl<'info> AccountsInit<'info> for AccountInfo<'info> {
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
) -> Result<Self, ProgramError> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ProgramError::NotEnoughAccountKeys);
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
|
||||
let account = &accounts[0];
|
||||
|
@ -37,7 +38,7 @@ impl<'info> AccountsInit<'info> for AccountInfo<'info> {
|
|||
disc_bytes.copy_from_slice(&data[..8]);
|
||||
let discriminator = u64::from_le_bytes(disc_bytes);
|
||||
if discriminator != 0 {
|
||||
return Err(ProgramError::InvalidAccountData);
|
||||
return Err(ErrorCode::AccountDiscriminatorAlreadySet.into());
|
||||
}
|
||||
|
||||
Ok(account.clone())
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::error::ErrorCode;
|
||||
use crate::{
|
||||
AccountDeserialize, Accounts, AccountsExit, ToAccountInfo, ToAccountInfos, ToAccountMetas,
|
||||
};
|
||||
|
@ -51,7 +52,7 @@ where
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
) -> Result<Self, ProgramError> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ProgramError::NotEnoughAccountKeys);
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
let account = &accounts[0];
|
||||
*accounts = &accounts[1..];
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::error::ErrorCode;
|
||||
use crate::{
|
||||
AccountDeserialize, AccountSerialize, Accounts, AccountsExit, CpiStateContext, ProgramState,
|
||||
ToAccountInfo, ToAccountInfos, ToAccountMetas,
|
||||
|
@ -67,7 +68,7 @@ where
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
) -> Result<Self, ProgramError> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ProgramError::NotEnoughAccountKeys);
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
let account = &accounts[0];
|
||||
*accounts = &accounts[1..];
|
||||
|
|
|
@ -1,34 +1,71 @@
|
|||
use solana_program::program_error::ProgramError;
|
||||
use crate::error;
|
||||
|
||||
// Error type that can be returned by internal framework code.
|
||||
#[doc(hidden)]
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
ProgramError(#[from] ProgramError),
|
||||
#[error("{0:?}")]
|
||||
ErrorCode(#[from] ErrorCode),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(u32)]
|
||||
// Error codes that can be returned by internal framework code.
|
||||
#[error(offset = 0)]
|
||||
pub enum ErrorCode {
|
||||
WrongSerialization = 1,
|
||||
}
|
||||
// Instructions.
|
||||
#[msg("8 byte instruction identifier not provided")]
|
||||
InstructionMissing = 100,
|
||||
#[msg("Fallback functions are not supported")]
|
||||
InstructionFallbackNotFound,
|
||||
#[msg("The program could not deserialize the given instruction")]
|
||||
InstructionDidNotDeserialize,
|
||||
#[msg("The program could not serialize the given instruction")]
|
||||
InstructionDidNotSerialize,
|
||||
|
||||
impl std::fmt::Display for ErrorCode {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
<Self as std::fmt::Debug>::fmt(self, fmt)
|
||||
}
|
||||
}
|
||||
// IDL instructions.
|
||||
#[msg("The program was compiled without idl instructions")]
|
||||
IdlInstructionStub = 120,
|
||||
#[msg("Invalid program given to the IDL instruction")]
|
||||
IdlInstructionInvalidProgram,
|
||||
|
||||
impl std::error::Error for ErrorCode {}
|
||||
// Constraints.
|
||||
#[msg("A mut constraint was violated")]
|
||||
ConstraintMut = 140,
|
||||
#[msg("A belongs to constraint was violated")]
|
||||
ConstraintBelongsTo,
|
||||
#[msg("A signer constraint as violated")]
|
||||
ConstraintSigner,
|
||||
#[msg("A raw constraint was violated")]
|
||||
ConstraintRaw,
|
||||
#[msg("An owner constraint was violated")]
|
||||
ConstraintOwner,
|
||||
#[msg("A rent exemption constraint was violated")]
|
||||
ConstraintRentExempt,
|
||||
#[msg("A seeds constraint was violated")]
|
||||
ConstraintSeeds,
|
||||
#[msg("An executable constraint was violated")]
|
||||
ConstraintExecutable,
|
||||
#[msg("A state constraint was violated")]
|
||||
ConstraintState,
|
||||
#[msg("An associated constraint was violated")]
|
||||
ConstraintAssociated,
|
||||
#[msg("An associated init constraint was violated")]
|
||||
ConstraintAssociatedInit,
|
||||
|
||||
impl std::convert::From<Error> for ProgramError {
|
||||
fn from(e: Error) -> ProgramError {
|
||||
match e {
|
||||
Error::ProgramError(e) => e,
|
||||
Error::ErrorCode(c) => ProgramError::Custom(c as u32),
|
||||
}
|
||||
}
|
||||
// Accounts.
|
||||
#[msg("The account discriminator was already set on this account")]
|
||||
AccountDiscriminatorAlreadySet = 160,
|
||||
#[msg("No 8 byte discriminator was found on the account")]
|
||||
AccountDiscriminatorNotFound,
|
||||
#[msg("8 byte discriminator did not match what was expected")]
|
||||
AccountDiscriminatorMismatch,
|
||||
#[msg("Failed to deserialize the account")]
|
||||
AccountDidNotDeserialize,
|
||||
#[msg("Failed to serialize the account")]
|
||||
AccountDidNotSerialize,
|
||||
#[msg("Not enough account keys given to the instruction")]
|
||||
AccountNotEnoughKeys,
|
||||
#[msg("The given account is not mutable")]
|
||||
AccountNotMutable,
|
||||
#[msg("The given account is not owned by the executing program")]
|
||||
AccountNotProgramOwned,
|
||||
|
||||
// State.
|
||||
#[msg("The given state account does not have the correct address")]
|
||||
StateInvalidAddress = 180,
|
||||
|
||||
// Used for APIs that shouldn't be used anymore.
|
||||
#[msg("The API being used is deprecated and should no longer be used")]
|
||||
Deprecated = 299,
|
||||
}
|
||||
|
|
|
@ -239,7 +239,7 @@ pub mod __private {
|
|||
use solana_program::pubkey::Pubkey;
|
||||
|
||||
pub use crate::ctor::Ctor;
|
||||
pub use crate::error::Error;
|
||||
pub use crate::error::{Error, ErrorCode};
|
||||
pub use anchor_attribute_account::ZeroCopyAccessor;
|
||||
pub use anchor_attribute_event::EventIndex;
|
||||
pub use base64;
|
||||
|
@ -249,6 +249,9 @@ pub mod __private {
|
|||
pub use crate::state::*;
|
||||
}
|
||||
|
||||
// The starting point for user defined error codes.
|
||||
pub const ERROR_CODE_OFFSET: u32 = 300;
|
||||
|
||||
// Calculates the size of an account, which may be larger than the deserialized
|
||||
// data in it. This trait is currently only used for `#[state]` accounts.
|
||||
#[doc(hidden)]
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::error::ErrorCode;
|
||||
use crate::{
|
||||
Accounts, AccountsExit, AccountsInit, ToAccountInfo, ToAccountInfos, ToAccountMetas, ZeroCopy,
|
||||
};
|
||||
|
@ -44,7 +45,7 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
|
|||
let mut disc_bytes = [0u8; 8];
|
||||
disc_bytes.copy_from_slice(&data[..8]);
|
||||
if disc_bytes != T::discriminator() {
|
||||
return Err(ProgramError::InvalidAccountData);
|
||||
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
|
||||
}
|
||||
|
||||
Ok(Loader::new(acc_info.clone()))
|
||||
|
@ -60,7 +61,7 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
|
|||
disc_bytes.copy_from_slice(&data[..8]);
|
||||
let discriminator = u64::from_le_bytes(disc_bytes);
|
||||
if discriminator != 0 {
|
||||
return Err(ProgramError::InvalidAccountData);
|
||||
return Err(ErrorCode::AccountDiscriminatorAlreadySet.into());
|
||||
}
|
||||
|
||||
Ok(Loader::new(acc_info.clone()))
|
||||
|
@ -73,7 +74,7 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
|
|||
let mut disc_bytes = [0u8; 8];
|
||||
disc_bytes.copy_from_slice(&data[..8]);
|
||||
if disc_bytes != T::discriminator() {
|
||||
return Err(ProgramError::InvalidAccountData);
|
||||
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
|
||||
}
|
||||
|
||||
Ok(Ref::map(data, |data| bytemuck::from_bytes(&data[8..])))
|
||||
|
@ -84,7 +85,7 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
|
|||
// AccountInfo api allows you to borrow mut even if the account isn't
|
||||
// writable, so add this check for a better dev experience.
|
||||
if !self.acc_info.is_writable {
|
||||
return Err(ProgramError::Custom(87)); // todo: proper error
|
||||
return Err(ErrorCode::AccountNotMutable.into());
|
||||
}
|
||||
|
||||
let data = self.acc_info.try_borrow_mut_data()?;
|
||||
|
@ -92,7 +93,7 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
|
|||
let mut disc_bytes = [0u8; 8];
|
||||
disc_bytes.copy_from_slice(&data[..8]);
|
||||
if disc_bytes != T::discriminator() {
|
||||
return Err(ProgramError::InvalidAccountData);
|
||||
return Err(ErrorCode::AccountDiscriminatorMismatch.into());
|
||||
}
|
||||
|
||||
Ok(RefMut::map(data, |data| {
|
||||
|
@ -106,7 +107,7 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
|
|||
// AccountInfo api allows you to borrow mut even if the account isn't
|
||||
// writable, so add this check for a better dev experience.
|
||||
if !self.acc_info.is_writable {
|
||||
return Err(ProgramError::Custom(87)); // todo: proper error
|
||||
return Err(ErrorCode::AccountNotMutable.into());
|
||||
}
|
||||
|
||||
let data = self.acc_info.try_borrow_mut_data()?;
|
||||
|
@ -116,7 +117,7 @@ impl<'info, T: ZeroCopy> Loader<'info, T> {
|
|||
disc_bytes.copy_from_slice(&data[..8]);
|
||||
let discriminator = u64::from_le_bytes(disc_bytes);
|
||||
if discriminator != 0 {
|
||||
return Err(ProgramError::InvalidAccountData);
|
||||
return Err(ErrorCode::AccountDiscriminatorAlreadySet.into());
|
||||
}
|
||||
|
||||
Ok(RefMut::map(data, |data| {
|
||||
|
@ -132,13 +133,13 @@ impl<'info, T: ZeroCopy> Accounts<'info> for Loader<'info, T> {
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
) -> Result<Self, ProgramError> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ProgramError::NotEnoughAccountKeys);
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
let account = &accounts[0];
|
||||
*accounts = &accounts[1..];
|
||||
let l = Loader::try_from(account)?;
|
||||
if l.acc_info.owner != program_id {
|
||||
return Err(ProgramError::Custom(1)); // todo: proper error
|
||||
return Err(ErrorCode::AccountNotProgramOwned.into());
|
||||
}
|
||||
Ok(l)
|
||||
}
|
||||
|
@ -151,13 +152,13 @@ impl<'info, T: ZeroCopy> AccountsInit<'info> for Loader<'info, T> {
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
) -> Result<Self, ProgramError> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ProgramError::NotEnoughAccountKeys);
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
let account = &accounts[0];
|
||||
*accounts = &accounts[1..];
|
||||
let l = Loader::try_from_init(account)?;
|
||||
if l.acc_info.owner != program_id {
|
||||
return Err(ProgramError::Custom(1)); // todo: proper error
|
||||
return Err(ErrorCode::AccountNotProgramOwned.into());
|
||||
}
|
||||
Ok(l)
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::error::ErrorCode;
|
||||
use crate::{
|
||||
AccountDeserialize, AccountSerialize, Accounts, AccountsExit, AccountsInit, CpiAccount,
|
||||
ToAccountInfo, ToAccountInfos, ToAccountMetas,
|
||||
|
@ -52,7 +53,7 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Clone> ProgramAccount<'a, T>
|
|||
disc_bytes.copy_from_slice(&data[..8]);
|
||||
let discriminator = u64::from_le_bytes(disc_bytes);
|
||||
if discriminator != 0 {
|
||||
return Err(ProgramError::InvalidAccountData);
|
||||
return Err(ErrorCode::AccountDiscriminatorAlreadySet.into());
|
||||
}
|
||||
|
||||
Ok(ProgramAccount::new(
|
||||
|
@ -72,13 +73,13 @@ where
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
) -> Result<Self, ProgramError> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ProgramError::NotEnoughAccountKeys);
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
let account = &accounts[0];
|
||||
*accounts = &accounts[1..];
|
||||
let pa = ProgramAccount::try_from(account)?;
|
||||
if pa.inner.info.owner != program_id {
|
||||
return Err(ProgramError::Custom(1)); // todo: proper error
|
||||
return Err(ErrorCode::AccountNotProgramOwned.into());
|
||||
}
|
||||
Ok(pa)
|
||||
}
|
||||
|
@ -94,13 +95,13 @@ where
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
) -> Result<Self, ProgramError> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ProgramError::NotEnoughAccountKeys);
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
let account = &accounts[0];
|
||||
*accounts = &accounts[1..];
|
||||
let pa = ProgramAccount::try_from_init(account)?;
|
||||
if pa.inner.info.owner != program_id {
|
||||
return Err(ProgramError::Custom(1)); // todo: proper error
|
||||
return Err(ErrorCode::AccountNotProgramOwned.into());
|
||||
}
|
||||
Ok(pa)
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::error::ErrorCode;
|
||||
use crate::{
|
||||
AccountDeserialize, AccountSerialize, Accounts, AccountsExit, CpiAccount, ToAccountInfo,
|
||||
ToAccountInfos, ToAccountMetas,
|
||||
|
@ -59,20 +60,20 @@ where
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
) -> Result<Self, ProgramError> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ProgramError::NotEnoughAccountKeys);
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
let account = &accounts[0];
|
||||
*accounts = &accounts[1..];
|
||||
|
||||
if account.key != &Self::address(program_id) {
|
||||
solana_program::msg!("Invalid state address");
|
||||
return Err(ProgramError::Custom(1)); // todo: proper error.
|
||||
return Err(ErrorCode::StateInvalidAddress.into());
|
||||
}
|
||||
|
||||
let pa = ProgramState::try_from(account)?;
|
||||
if pa.inner.info.owner != program_id {
|
||||
solana_program::msg!("Invalid state owner");
|
||||
return Err(ProgramError::Custom(1)); // todo: proper error.
|
||||
return Err(ErrorCode::AccountNotProgramOwned.into());
|
||||
}
|
||||
Ok(pa)
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::error::ErrorCode;
|
||||
use crate::{Accounts, AccountsExit, ToAccountInfo, ToAccountInfos, ToAccountMetas};
|
||||
use solana_program::account_info::AccountInfo;
|
||||
use solana_program::entrypoint::ProgramResult;
|
||||
|
@ -38,7 +39,7 @@ impl<'info, T: solana_program::sysvar::Sysvar> Accounts<'info> for Sysvar<'info,
|
|||
accounts: &mut &[AccountInfo<'info>],
|
||||
) -> Result<Self, ProgramError> {
|
||||
if accounts.is_empty() {
|
||||
return Err(ProgramError::NotEnoughAccountKeys);
|
||||
return Err(ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
let account = &accounts[0];
|
||||
*accounts = &accounts[1..];
|
||||
|
|
|
@ -130,7 +130,7 @@ pub fn generate_constraint_mut(f: &Field, _c: &ConstraintMut) -> proc_macro2::To
|
|||
let ident = &f.ident;
|
||||
quote! {
|
||||
if !#ident.to_account_info().is_writable {
|
||||
return Err(anchor_lang::solana_program::program_error::ProgramError::Custom(36)); // todo: error codes
|
||||
return Err(anchor_lang::__private::ErrorCode::ConstraintMut.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -147,7 +147,7 @@ pub fn generate_constraint_belongs_to(
|
|||
};
|
||||
quote! {
|
||||
if &#field.#target != #target.to_account_info().key {
|
||||
return Err(anchor_lang::solana_program::program_error::ProgramError::Custom(1)); // todo: error codes
|
||||
return Err(anchor_lang::__private::ErrorCode::ConstraintBelongsTo.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ pub fn generate_constraint_signer(f: &Field, _c: &ConstraintSigner) -> proc_macr
|
|||
// This check will be performed on the other end of the invocation.
|
||||
if cfg!(not(feature = "cpi")) {
|
||||
if !#info.to_account_info().is_signer {
|
||||
return Err(anchor_lang::solana_program::program_error::ProgramError::MissingRequiredSignature);
|
||||
return Err(anchor_lang::__private::ErrorCode::ConstraintSigner.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -181,7 +181,7 @@ pub fn generate_constraint_literal(c: &ConstraintLiteral) -> proc_macro2::TokenS
|
|||
};
|
||||
quote! {
|
||||
if !(#lit) {
|
||||
return Err(anchor_lang::solana_program::program_error::ProgramError::Custom(1)); // todo: error codes
|
||||
return Err(anchor_lang::__private::ErrorCode::Deprecated.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -190,7 +190,7 @@ pub fn generate_constraint_raw(c: &ConstraintRaw) -> proc_macro2::TokenStream {
|
|||
let raw = &c.raw;
|
||||
quote! {
|
||||
if !(#raw) {
|
||||
return Err(anchor_lang::solana_program::program_error::ProgramError::Custom(14)); // todo: error codes
|
||||
return Err(anchor_lang::__private::ErrorCode::ConstraintRaw.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -200,7 +200,7 @@ pub fn generate_constraint_owner(f: &Field, c: &ConstraintOwner) -> proc_macro2:
|
|||
let owner_target = c.owner_target.clone();
|
||||
quote! {
|
||||
if #ident.to_account_info().owner != #owner_target.to_account_info().key {
|
||||
return Err(ProgramError::Custom(76)); // todo: proper error.
|
||||
return Err(anchor_lang::__private::ErrorCode::ConstraintOwner.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -220,7 +220,7 @@ pub fn generate_constraint_rent_exempt(
|
|||
ConstraintRentExempt::Skip => quote! {},
|
||||
ConstraintRentExempt::Enforce => quote! {
|
||||
if !rent.is_exempt(#info.lamports(), #info.try_data_len()?) {
|
||||
return Err(anchor_lang::solana_program::program_error::ProgramError::Custom(2)); // todo: error codes
|
||||
return Err(anchor_lang::__private::ErrorCode::ConstraintRentExempt.into());
|
||||
}
|
||||
},
|
||||
}
|
||||
|
@ -233,9 +233,9 @@ pub fn generate_constraint_seeds(f: &Field, c: &ConstraintSeeds) -> proc_macro2:
|
|||
let program_signer = Pubkey::create_program_address(
|
||||
&[#seeds],
|
||||
program_id,
|
||||
).map_err(|_| anchor_lang::solana_program::program_error::ProgramError::Custom(1))?; // todo
|
||||
).map_err(|_| anchor_lang::__private::ErrorCode::ConstraintSeeds)?;
|
||||
if #name.to_account_info().key != &program_signer {
|
||||
return Err(anchor_lang::solana_program::program_error::ProgramError::Custom(1)); // todo
|
||||
return Err(anchor_lang::__private::ErrorCode::ConstraintSeeds.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -247,7 +247,7 @@ pub fn generate_constraint_executable(
|
|||
let name = &f.ident;
|
||||
quote! {
|
||||
if !#name.to_account_info().executable {
|
||||
return Err(anchor_lang::solana_program::program_error::ProgramError::Custom(5)) // todo
|
||||
return Err(anchor_lang::__private::ErrorCode::ConstraintExecutable.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -263,10 +263,10 @@ pub fn generate_constraint_state(f: &Field, c: &ConstraintState) -> proc_macro2:
|
|||
// Checks the given state account is the canonical state account for
|
||||
// the target program.
|
||||
if #ident.to_account_info().key != &anchor_lang::CpiState::<#account_ty>::address(#program_target.to_account_info().key) {
|
||||
return Err(ProgramError::Custom(1)); // todo: proper error.
|
||||
return Err(anchor_lang::__private::ErrorCode::ConstraintState.into());
|
||||
}
|
||||
if #ident.to_account_info().owner != #program_target.to_account_info().key {
|
||||
return Err(ProgramError::Custom(1)); // todo: proper error.
|
||||
return Err(anchor_lang::__private::ErrorCode::ConstraintState.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -371,7 +371,7 @@ pub fn generate_constraint_associated_init(
|
|||
#associated_pubkey_and_nonce
|
||||
|
||||
if &__associated_field != #field.key {
|
||||
return Err(ProgramError::Custom(45)); // todo: proper error.
|
||||
return Err(anchor_lang::__private::ErrorCode::ConstraintAssociatedInit.into());
|
||||
}
|
||||
let lamports = rent.minimum_balance(space);
|
||||
let ix = anchor_lang::solana_program::system_instruction::create_account(
|
||||
|
@ -417,8 +417,7 @@ pub fn generate_constraint_associated_seeds(
|
|||
quote! {
|
||||
#generated_associated_pubkey_and_nonce
|
||||
if #name.to_account_info().key != &__associated_field {
|
||||
// TODO: proper error.
|
||||
return Err(anchor_lang::solana_program::program_error::ProgramError::Custom(45));
|
||||
return Err(anchor_lang::__private::ErrorCode::ConstraintAssociated.into());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,6 +32,14 @@ pub fn generate(error: Error) -> proc_macro2::TokenStream {
|
|||
})
|
||||
.collect();
|
||||
|
||||
let offset = match error.args {
|
||||
None => quote! { anchor_lang::__private::ERROR_CODE_OFFSET},
|
||||
Some(args) => {
|
||||
let offset = &args.offset;
|
||||
quote! { #offset }
|
||||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
/// Anchor generated Result to be used as the return type for the
|
||||
/// program.
|
||||
|
@ -40,15 +48,16 @@ pub fn generate(error: Error) -> proc_macro2::TokenStream {
|
|||
/// Anchor generated error allowing one to easily return a
|
||||
/// `ProgramError` or a custom, user defined error code by utilizing
|
||||
/// its `From` implementation.
|
||||
#[doc(hidden)]
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
ProgramError(#[from] ProgramError),
|
||||
ProgramError(#[from] anchor_lang::solana_program::program_error::ProgramError),
|
||||
#[error(transparent)]
|
||||
ErrorCode(#[from] #enum_name),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(std::fmt::Debug, Clone, Copy)]
|
||||
#[repr(u32)]
|
||||
#error_enum
|
||||
|
||||
|
@ -62,19 +71,17 @@ pub fn generate(error: Error) -> proc_macro2::TokenStream {
|
|||
|
||||
impl std::error::Error for #enum_name {}
|
||||
|
||||
impl std::convert::From<Error> for ProgramError {
|
||||
fn from(e: Error) -> ProgramError {
|
||||
// Errors 0-100 are reserved for the framework.
|
||||
let error_offset = 100u32;
|
||||
impl std::convert::From<Error> for anchor_lang::solana_program::program_error::ProgramError {
|
||||
fn from(e: Error) -> anchor_lang::solana_program::program_error::ProgramError {
|
||||
match e {
|
||||
Error::ProgramError(e) => e,
|
||||
Error::ErrorCode(c) => ProgramError::Custom(c as u32 + error_offset),
|
||||
Error::ErrorCode(c) => anchor_lang::solana_program::program_error::ProgramError::Custom(c as u32 + #offset),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::convert::From<#enum_name> for ProgramError {
|
||||
fn from(e: #enum_name) -> ProgramError {
|
||||
impl std::convert::From<#enum_name> for anchor_lang::solana_program::program_error::ProgramError {
|
||||
fn from(e: #enum_name) -> anchor_lang::solana_program::program_error::ProgramError {
|
||||
let err: Error = e.into();
|
||||
err.into()
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
let ix = {
|
||||
let ix = instruction::#ix_variant;
|
||||
let mut ix_data = AnchorSerialize::try_to_vec(&ix)
|
||||
.map_err(|_| ProgramError::InvalidInstructionData)?;
|
||||
.map_err(|_| anchor_lang::__private::ErrorCode::InstructionDidNotSerialize)?;
|
||||
let mut data = #sighash_tts.to_vec();
|
||||
data.append(&mut ix_data);
|
||||
let accounts = ctx.to_account_metas(None);
|
||||
|
|
|
@ -20,7 +20,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
quote! {
|
||||
#sighash_tts => {
|
||||
let ix = instruction::state::#ix_name::deserialize(&mut ix_data)
|
||||
.map_err(|_| ProgramError::Custom(1))?; // todo: error code
|
||||
.map_err(|_| anchor_lang::__private::ErrorCode::InstructionDidNotDeserialize)?;
|
||||
let instruction::state::#variant_arm = ix;
|
||||
__private::__state::__ctor(program_id, accounts, #(#ctor_args),*)
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
quote! {
|
||||
#sighash_tts => {
|
||||
let ix = instruction::state::#ix_name::deserialize(&mut ix_data)
|
||||
.map_err(|_| ProgramError::Custom(1))?; // todo: error code
|
||||
.map_err(|_| anchor_lang::__private::ErrorCode::InstructionDidNotDeserialize)?;
|
||||
let instruction::state::#variant_arm = ix;
|
||||
__private::__state::#ix_method_name(program_id, accounts, #(#ix_arg_names),*)
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
#sighash_tts => {
|
||||
#args_struct
|
||||
let ix = Args::deserialize(&mut ix_data)
|
||||
.map_err(|_| ProgramError::Custom(1))?; // todo: error code
|
||||
.map_err(|_| anchor_lang::__private::ErrorCode::InstructionDidNotDeserialize)?;
|
||||
let Args {
|
||||
#(#ix_arg_names),*
|
||||
} = ix;
|
||||
|
@ -139,7 +139,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
quote! {
|
||||
#sighash_tts => {
|
||||
let ix = instruction::#ix_name::deserialize(&mut ix_data)
|
||||
.map_err(|_| ProgramError::Custom(1))?; // todo: error code
|
||||
.map_err(|_| anchor_lang::__private::ErrorCode::InstructionDidNotDeserialize)?;
|
||||
let instruction::#variant_arm = ix;
|
||||
__private::__global::#ix_method_name(program_id, accounts, #(#ix_arg_names),*)
|
||||
}
|
||||
|
@ -182,7 +182,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
#(#global_dispatch_arms)*
|
||||
_ => {
|
||||
msg!("Fallback functions are not supported. If you have a use case, please file an issue.");
|
||||
Err(ProgramError::Custom(99))
|
||||
Err(anchor_lang::__private::ErrorCode::InstructionFallbackNotFound.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ pub fn generate(_program: &Program) -> proc_macro2::TokenStream {
|
|||
msg!("anchor-debug is active");
|
||||
}
|
||||
if ix_data.len() < 8 {
|
||||
return Err(ProgramError::Custom(99));
|
||||
return Err(anchor_lang::__private::ErrorCode::InstructionMissing.into());
|
||||
}
|
||||
|
||||
// Split the instruction data into the first 8 byte method
|
||||
|
|
|
@ -20,7 +20,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
let mut data: &[u8] = idl_ix_data;
|
||||
|
||||
let ix = anchor_lang::idl::IdlInstruction::deserialize(&mut data)
|
||||
.map_err(|_| ProgramError::Custom(2))?; // todo
|
||||
.map_err(|_| anchor_lang::__private::ErrorCode::InstructionDidNotDeserialize)?;
|
||||
|
||||
match ix {
|
||||
anchor_lang::idl::IdlInstruction::Create { data_len } => {
|
||||
|
@ -55,7 +55,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
#[inline(never)]
|
||||
#[cfg(feature = "no-idl")]
|
||||
pub fn __idl_dispatch(program_id: &Pubkey, accounts: &[AccountInfo], idl_ix_data: &[u8]) -> ProgramResult {
|
||||
Err(anchor_lang::solana_program::program_error::ProgramError::Custom(99))
|
||||
Err(anchor_lang::__private::ErrorCode::IdlInstructionStub.into())
|
||||
}
|
||||
|
||||
// One time IDL account initializer. Will faill on subsequent
|
||||
|
@ -67,7 +67,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
data_len: u64,
|
||||
) -> ProgramResult {
|
||||
if program_id != accounts.program.key {
|
||||
return Err(anchor_lang::solana_program::program_error::ProgramError::Custom(98)); // todo proper error
|
||||
return Err(anchor_lang::__private::ErrorCode::IdlInstructionInvalidProgram.into());
|
||||
}
|
||||
// Create the IDL's account.
|
||||
let from = accounts.from.key;
|
||||
|
@ -336,7 +336,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
) -> ProgramResult {
|
||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||
if remaining_accounts.is_empty() {
|
||||
return Err(ProgramError::Custom(1)); // todo
|
||||
return Err(anchor_lang::__private::ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
|
||||
let state_account = &remaining_accounts[0];
|
||||
|
@ -374,7 +374,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
) -> ProgramResult {
|
||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||
if remaining_accounts.is_empty() {
|
||||
return Err(ProgramError::Custom(1)); // todo
|
||||
return Err(anchor_lang::__private::ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
|
||||
// Deserialize the program state account.
|
||||
|
@ -459,7 +459,7 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
|
|||
|
||||
let mut remaining_accounts: &[AccountInfo] = accounts;
|
||||
if remaining_accounts.is_empty() {
|
||||
return Err(ProgramError::Custom(1)); // todo
|
||||
return Err(anchor_lang::__private::ErrorCode::AccountNotEnoughKeys.into());
|
||||
}
|
||||
|
||||
// Deserialize the program state account.
|
||||
|
|
|
@ -11,6 +11,8 @@ use std::iter::FromIterator;
|
|||
use std::path::Path;
|
||||
|
||||
const DERIVE_NAME: &str = "Accounts";
|
||||
// TODO: sharee this with `anchor_lang` crate.
|
||||
const ERROR_CODE_OFFSET: u32 = 300;
|
||||
|
||||
// Parse an entire interface file.
|
||||
pub fn parse(filename: impl AsRef<Path>) -> Result<Idl> {
|
||||
|
@ -128,12 +130,12 @@ pub fn parse(filename: impl AsRef<Path>) -> Result<Idl> {
|
|||
}
|
||||
},
|
||||
};
|
||||
let error = parse_error_enum(&f).map(|mut e| error::parse(&mut e));
|
||||
let error = parse_error_enum(&f).map(|mut e| error::parse(&mut e, None));
|
||||
let error_codes = error.as_ref().map(|e| {
|
||||
e.codes
|
||||
.iter()
|
||||
.map(|code| IdlErrorCode {
|
||||
code: 100 + code.id,
|
||||
code: ERROR_CODE_OFFSET + code.id,
|
||||
name: code.ident.to_string(),
|
||||
msg: code.msg.clone(),
|
||||
})
|
||||
|
|
|
@ -5,7 +5,8 @@ use parser::program as program_parser;
|
|||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::ToTokens;
|
||||
use std::ops::Deref;
|
||||
use syn::parse::{Parse, ParseStream, Result as ParseResult};
|
||||
use syn::ext::IdentExt;
|
||||
use syn::parse::{Error as ParseError, Parse, ParseStream, Result as ParseResult};
|
||||
use syn::punctuated::Punctuated;
|
||||
use syn::spanned::Spanned;
|
||||
use syn::{
|
||||
|
@ -212,6 +213,26 @@ pub struct Error {
|
|||
pub raw_enum: ItemEnum,
|
||||
pub ident: Ident,
|
||||
pub codes: Vec<ErrorCode>,
|
||||
pub args: Option<ErrorArgs>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ErrorArgs {
|
||||
pub offset: LitInt,
|
||||
}
|
||||
|
||||
impl Parse for ErrorArgs {
|
||||
fn parse(stream: ParseStream) -> ParseResult<Self> {
|
||||
let offset_span = stream.span();
|
||||
let offset = stream.call(Ident::parse_any)?;
|
||||
if offset.to_string().as_str() != "offset" {
|
||||
return Err(ParseError::new(offset_span, "expected keyword offset"));
|
||||
}
|
||||
stream.parse::<Token![=]>()?;
|
||||
Ok(ErrorArgs {
|
||||
offset: stream.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::{Error, ErrorCode};
|
||||
use crate::{Error, ErrorArgs, ErrorCode};
|
||||
|
||||
// Removes any internal #[msg] attributes, as they are inert.
|
||||
pub fn parse(error_enum: &mut syn::ItemEnum) -> Error {
|
||||
pub fn parse(error_enum: &mut syn::ItemEnum, args: Option<ErrorArgs>) -> Error {
|
||||
let ident = error_enum.ident.clone();
|
||||
let mut last_discriminant = 0;
|
||||
let codes: Vec<ErrorCode> = error_enum
|
||||
|
@ -30,12 +30,12 @@ pub fn parse(error_enum: &mut syn::ItemEnum) -> Error {
|
|||
ErrorCode { id, ident, msg }
|
||||
})
|
||||
.collect();
|
||||
|
||||
Error {
|
||||
name: error_enum.ident.to_string(),
|
||||
raw_enum: error_enum.clone(),
|
||||
ident,
|
||||
codes,
|
||||
args,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@project-serum/borsh": "^0.2.2",
|
||||
"@solana/web3.js": "^1.11.0",
|
||||
"@solana/web3.js": "^1.17.0",
|
||||
"base64-js": "^1.5.1",
|
||||
"bn.js": "^5.1.2",
|
||||
"bs58": "^4.0.1",
|
||||
|
|
158
ts/src/error.ts
158
ts/src/error.ts
|
@ -6,7 +6,165 @@ export class ProgramError extends Error {
|
|||
super(...params);
|
||||
}
|
||||
|
||||
public static parse(
|
||||
err: any,
|
||||
idlErrors: Map<number, string>
|
||||
): ProgramError | null {
|
||||
// TODO: don't rely on the error string. web3.js should preserve the error
|
||||
// code information instead of giving us an untyped string.
|
||||
let components = err.toString().split("custom program error: ");
|
||||
if (components.length !== 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let errorCode: number;
|
||||
try {
|
||||
errorCode = parseInt(components[1]);
|
||||
} catch (parseErr) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Parse user error.
|
||||
let errorMsg = idlErrors.get(errorCode);
|
||||
if (errorMsg !== undefined) {
|
||||
return new ProgramError(errorCode, errorMsg);
|
||||
}
|
||||
|
||||
// Parse framework internal error.
|
||||
errorMsg = LangErrorMessage.get(errorCode);
|
||||
if (errorMsg !== undefined) {
|
||||
return new ProgramError(errorCode, errorMsg);
|
||||
}
|
||||
|
||||
// Unable to parse the error. Just return the untranslated error.
|
||||
return null;
|
||||
}
|
||||
|
||||
public toString(): string {
|
||||
return this.msg;
|
||||
}
|
||||
}
|
||||
|
||||
const LangErrorCode = {
|
||||
// Instructions.
|
||||
InstructionMissing: 100,
|
||||
InstructionFallbackNotFound: 101,
|
||||
InstructionDidNotDeserialize: 102,
|
||||
InstructionDidNotSerialize: 103,
|
||||
|
||||
// IDL instructions.
|
||||
IdlInstructionStub: 120,
|
||||
IdlInstructionInvalidProgram: 121,
|
||||
|
||||
// Constraints.
|
||||
ConstraintMut: 140,
|
||||
ConstraintBelongsTo: 141,
|
||||
ConstraintSigner: 142,
|
||||
ConstraintRaw: 143,
|
||||
ConstraintOwner: 144,
|
||||
ConstraintRentExempt: 145,
|
||||
ConstraintSeeds: 146,
|
||||
ConstraintExecutable: 147,
|
||||
ConstraintState: 148,
|
||||
ConstraintAssociated: 149,
|
||||
ConstraintAssociatedInit: 150,
|
||||
|
||||
// Accounts.
|
||||
AccountDiscriminatorAlreadySet: 160,
|
||||
AccountDiscriminatorNotFound: 161,
|
||||
AccountDiscriminatorMismatch: 162,
|
||||
AccountDidNotDeserialize: 163,
|
||||
AccountDidNotSerialize: 164,
|
||||
AccountNotEnoughKeys: 165,
|
||||
AccountNotMutable: 166,
|
||||
AccountNotProgramOwned: 167,
|
||||
|
||||
// State.
|
||||
StateInvalidAddress: 180,
|
||||
|
||||
// Used for APIs that shouldn't be used anymore.
|
||||
Deprecated: 299,
|
||||
};
|
||||
|
||||
const LangErrorMessage = new Map([
|
||||
// Instructions.
|
||||
[
|
||||
LangErrorCode.InstructionMissing,
|
||||
"8 byte instruction identifier not provided",
|
||||
],
|
||||
[
|
||||
LangErrorCode.InstructionFallbackNotFound,
|
||||
"Fallback functions are not supported",
|
||||
],
|
||||
[
|
||||
LangErrorCode.InstructionDidNotDeserialize,
|
||||
"The program could not deserialize the given instruction",
|
||||
],
|
||||
[
|
||||
LangErrorCode.InstructionDidNotSerialize,
|
||||
"The program could not serialize the given instruction",
|
||||
],
|
||||
|
||||
// Idl instructions.
|
||||
[
|
||||
LangErrorCode.IdlInstructionStub,
|
||||
"The program was compiled without idl instructions",
|
||||
],
|
||||
[
|
||||
LangErrorCode.IdlInstructionInvalidProgram,
|
||||
"The transaction was given an invalid program for the IDL instruction",
|
||||
],
|
||||
|
||||
// Constraints.
|
||||
[LangErrorCode.ConstraintMut, "A mut constraint was violated"],
|
||||
[LangErrorCode.ConstraintBelongsTo, "A belongs_to constraint was violated"],
|
||||
[LangErrorCode.ConstraintSigner, "A signer constraint was violated"],
|
||||
[LangErrorCode.ConstraintRaw, "A raw constraint as violated"],
|
||||
[LangErrorCode.ConstraintOwner, "An owner constraint was violated"],
|
||||
[LangErrorCode.ConstraintRentExempt, "A rent exempt constraint was violated"],
|
||||
[LangErrorCode.ConstraintSeeds, "A seeds constraint was violated"],
|
||||
[LangErrorCode.ConstraintExecutable, "An executable constraint was violated"],
|
||||
[LangErrorCode.ConstraintState, "A state constraint was violated"],
|
||||
[LangErrorCode.ConstraintAssociated, "An associated constraint was violated"],
|
||||
[
|
||||
LangErrorCode.ConstraintAssociatedInit,
|
||||
"An associated init constraint was violated",
|
||||
],
|
||||
|
||||
// Accounts.
|
||||
[
|
||||
LangErrorCode.AccountDiscriminatorAlreadySet,
|
||||
"The account discriminator was already set on this account",
|
||||
],
|
||||
[
|
||||
LangErrorCode.AccountDiscriminatorNotFound,
|
||||
"No 8 byte discriminator was found on the account",
|
||||
],
|
||||
[
|
||||
LangErrorCode.AccountDiscriminatorMismatch,
|
||||
"8 byte discriminator did not match what was expected",
|
||||
],
|
||||
[LangErrorCode.AccountDidNotDeserialize, "Failed to deserialize the account"],
|
||||
[LangErrorCode.AccountDidNotSerialize, "Failed to serialize the account"],
|
||||
[
|
||||
LangErrorCode.AccountNotEnoughKeys,
|
||||
"Not enough account keys given to the instruction",
|
||||
],
|
||||
[LangErrorCode.AccountNotMutable, "The given account is not mutable"],
|
||||
[
|
||||
LangErrorCode.AccountNotProgramOwned,
|
||||
"The given account is not owned by the executing program",
|
||||
],
|
||||
|
||||
// State.
|
||||
[
|
||||
LangErrorCode.StateInvalidAddress,
|
||||
"The given state account does not have the correct address",
|
||||
],
|
||||
|
||||
// Misc.
|
||||
[
|
||||
LangErrorCode.Deprecated,
|
||||
"The API being used is deprecated and should no longer be used",
|
||||
],
|
||||
]);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import EventEmitter from "eventemitter3";
|
||||
import { PublicKey } from "@solana/web3.js";
|
||||
import { Idl, IdlInstruction, IdlAccountItem, IdlStateMethod } from "../idl";
|
||||
import { ProgramError } from "../error";
|
||||
import { Accounts } from "./context";
|
||||
|
||||
export type Subscription = {
|
||||
|
@ -56,29 +55,6 @@ export function validateAccounts(
|
|||
});
|
||||
}
|
||||
|
||||
export function translateError(
|
||||
idlErrors: Map<number, string>,
|
||||
err: any
|
||||
): Error | null {
|
||||
// TODO: don't rely on the error string. web3.js should preserve the error
|
||||
// code information instead of giving us an untyped string.
|
||||
let components = err.toString().split("custom program error: ");
|
||||
if (components.length === 2) {
|
||||
try {
|
||||
const errorCode = parseInt(components[1]);
|
||||
let errorMsg = idlErrors.get(errorCode);
|
||||
if (errorMsg === undefined) {
|
||||
// Unexpected error code so just throw the untranslated error.
|
||||
return null;
|
||||
}
|
||||
return new ProgramError(errorCode, errorMsg);
|
||||
} catch (parseErr) {
|
||||
// Unable to parse the error. Just return the untranslated error.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Translates an address to a Pubkey.
|
||||
export function translateAddress(address: Address): PublicKey {
|
||||
if (typeof address === "string") {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { TransactionSignature } from "@solana/web3.js";
|
||||
import Provider from "../../provider";
|
||||
import { IdlInstruction } from "../../idl";
|
||||
import { translateError } from "../common";
|
||||
import { splitArgsAndCtx } from "../context";
|
||||
import { TransactionFn } from "./transaction";
|
||||
import { ProgramError } from "../../error";
|
||||
|
||||
export default class RpcFactory {
|
||||
public static build(
|
||||
|
@ -20,7 +20,7 @@ export default class RpcFactory {
|
|||
return txSig;
|
||||
} catch (err) {
|
||||
console.log("Translating error", err);
|
||||
let translatedErr = translateError(idlErrors, err);
|
||||
let translatedErr = ProgramError.parse(err, idlErrors);
|
||||
if (translatedErr === null) {
|
||||
throw err;
|
||||
}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { PublicKey } from "@solana/web3.js";
|
||||
import Provider from "../../provider";
|
||||
import { IdlInstruction } from "../../idl";
|
||||
import { translateError } from "../common";
|
||||
import { splitArgsAndCtx } from "../context";
|
||||
import { TransactionFn } from "./transaction";
|
||||
import { EventParser } from "../event";
|
||||
import Coder from "../../coder";
|
||||
import { Idl } from "../../idl";
|
||||
import { ProgramError } from "../../error";
|
||||
|
||||
export default class SimulateFactory {
|
||||
public static build(
|
||||
|
@ -26,7 +26,7 @@ export default class SimulateFactory {
|
|||
resp = await provider.simulate(tx, ctx.signers, ctx.options);
|
||||
} catch (err) {
|
||||
console.log("Translating error", err);
|
||||
let translatedErr = translateError(idlErrors, err);
|
||||
let translatedErr = ProgramError.parse(err, idlErrors);
|
||||
if (translatedErr === null) {
|
||||
throw err;
|
||||
}
|
||||
|
|
28
ts/yarn.lock
28
ts/yarn.lock
|
@ -676,13 +676,14 @@
|
|||
dependencies:
|
||||
"@sinonjs/commons" "^1.7.0"
|
||||
|
||||
"@solana/web3.js@^1.11.0":
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.11.0.tgz#1cc9a25381687c82e444ad0633f028e050a06753"
|
||||
integrity sha512-kmngWxntzp0HNhWInd7/3g2uqxdOrahvaHOyjilcRe+WCiC777gERz3+eIAbxIYx2zAZPjy02MZzLgoRHccZoQ==
|
||||
"@solana/web3.js@^1.17.0":
|
||||
version "1.17.0"
|
||||
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.17.0.tgz#51775bd17af77132450c22ac175870d4a9721b9b"
|
||||
integrity sha512-PBOHY260CudciLwBgwt1U8upwCS1Jq0BbS6EVyX0tz6Tj14Dp4i87dQNyntentNiGQQ+yWBIk4vJEm+PMCSd/A==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
bn.js "^5.0.0"
|
||||
borsh "^0.4.0"
|
||||
bs58 "^4.0.1"
|
||||
buffer "6.0.1"
|
||||
buffer-layout "^1.2.0"
|
||||
|
@ -728,7 +729,7 @@
|
|||
dependencies:
|
||||
"@babel/types" "^7.3.0"
|
||||
|
||||
"@types/bn.js@^4.11.6":
|
||||
"@types/bn.js@^4.11.5", "@types/bn.js@^4.11.6":
|
||||
version "4.11.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c"
|
||||
integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==
|
||||
|
@ -1261,6 +1262,16 @@ bn.js@^5.1.2:
|
|||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.3.tgz#beca005408f642ebebea80b042b4d18d2ac0ee6b"
|
||||
integrity sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==
|
||||
|
||||
borsh@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.4.0.tgz#9dd6defe741627f1315eac2a73df61421f6ddb9f"
|
||||
integrity sha512-aX6qtLya3K0AkT66CmYWCCDr77qsE9arV05OmdFpmat9qu8Pg9J5tBUPDztAW5fNh/d/MyVG/OYziP52Ndzx1g==
|
||||
dependencies:
|
||||
"@types/bn.js" "^4.11.5"
|
||||
bn.js "^5.0.0"
|
||||
bs58 "^4.0.0"
|
||||
text-encoding-utf-8 "^1.0.2"
|
||||
|
||||
brace-expansion@^1.1.7:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
|
||||
|
@ -1309,7 +1320,7 @@ bs-logger@0.x:
|
|||
dependencies:
|
||||
fast-json-stable-stringify "2.x"
|
||||
|
||||
bs58@^4.0.1:
|
||||
bs58@^4.0.0, bs58@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a"
|
||||
integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo=
|
||||
|
@ -5035,6 +5046,11 @@ test-exclude@^6.0.0:
|
|||
glob "^7.1.4"
|
||||
minimatch "^3.0.4"
|
||||
|
||||
text-encoding-utf-8@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13"
|
||||
integrity sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg==
|
||||
|
||||
text-extensions@^1.0.0:
|
||||
version "1.9.0"
|
||||
resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26"
|
||||
|
|
Loading…
Reference in New Issue