From 4c87b9f8045d3573cc102ef4751d4ed1492cda29 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Fri, 7 Aug 2020 23:58:18 -0700 Subject: [PATCH 001/248] Reorganize crates based on program --- program/Cargo.toml | 35 + program/README.md | 10 + program/Xargo.toml | 2 + program/build.rs | 65 + program/inc/token.h | 359 + program/js/.eslintignore | 1 + program/js/.eslintrc.js | 56 + program/js/.flowconfig | 20 + program/js/.gitignore | 1 + program/js/.prettierrc.yaml | 7 + program/js/README.md | 69 + program/js/babel.config.json | 17 + program/js/babel.rollup.config.json | 18 + program/js/cli/main.js | 58 + program/js/cli/token-test.js | 500 + program/js/client/layout.js | 47 + program/js/client/token.js | 1242 +++ .../client/util/new-account-with-lamports.js | 25 + .../util/new-system-account-with-airdrop.js | 17 + .../util/send-and-confirm-transaction.js | 21 + program/js/client/util/sleep.js | 6 + program/js/client/util/store.js | 26 + program/js/cluster-devnet.env | 2 + program/js/cluster-mainnet-beta.env | 2 + program/js/cluster-testnet.env | 2 + program/js/flow-typed/bn.js.js | 4 + program/js/flow-typed/bs58.js | 6 + program/js/flow-typed/buffer-layout.js | 4 + .../js/flow-typed/mkdirp-promise_vx.x.x.js | 32 + program/js/flow-typed/npm/mz_vx.x.x.js | 73 + program/js/flow-typed/semver.js | 3 + program/js/module.d.ts | 160 + program/js/module.flow.js | 163 + program/js/package-lock.json | 8383 +++++++++++++++++ program/js/package.json | 78 + program/js/rollup.config.js | 74 + program/js/url.js | 31 + program/program-id.md | 1 + program/src/entrypoint.rs | 24 + program/src/error.rs | 56 + program/src/instruction.rs | 593 ++ program/src/lib.rs | 28 + program/src/native_mint.rs | 25 + program/src/option.rs | 965 ++ program/src/processor.rs | 2232 +++++ program/src/state.rs | 92 + 46 files changed, 15635 insertions(+) create mode 100644 program/Cargo.toml create mode 100644 program/README.md create mode 100644 program/Xargo.toml create mode 100644 program/build.rs create mode 100644 program/inc/token.h create mode 100644 program/js/.eslintignore create mode 100644 program/js/.eslintrc.js create mode 100644 program/js/.flowconfig create mode 100644 program/js/.gitignore create mode 100644 program/js/.prettierrc.yaml create mode 100644 program/js/README.md create mode 100644 program/js/babel.config.json create mode 100644 program/js/babel.rollup.config.json create mode 100644 program/js/cli/main.js create mode 100644 program/js/cli/token-test.js create mode 100644 program/js/client/layout.js create mode 100644 program/js/client/token.js create mode 100644 program/js/client/util/new-account-with-lamports.js create mode 100644 program/js/client/util/new-system-account-with-airdrop.js create mode 100644 program/js/client/util/send-and-confirm-transaction.js create mode 100644 program/js/client/util/sleep.js create mode 100644 program/js/client/util/store.js create mode 100644 program/js/cluster-devnet.env create mode 100644 program/js/cluster-mainnet-beta.env create mode 100644 program/js/cluster-testnet.env create mode 100644 program/js/flow-typed/bn.js.js create mode 100644 program/js/flow-typed/bs58.js create mode 100644 program/js/flow-typed/buffer-layout.js create mode 100644 program/js/flow-typed/mkdirp-promise_vx.x.x.js create mode 100644 program/js/flow-typed/npm/mz_vx.x.x.js create mode 100644 program/js/flow-typed/semver.js create mode 100644 program/js/module.d.ts create mode 100644 program/js/module.flow.js create mode 100644 program/js/package-lock.json create mode 100644 program/js/package.json create mode 100644 program/js/rollup.config.js create mode 100644 program/js/url.js create mode 100644 program/program-id.md create mode 100644 program/src/entrypoint.rs create mode 100644 program/src/error.rs create mode 100644 program/src/instruction.rs create mode 100644 program/src/lib.rs create mode 100644 program/src/native_mint.rs create mode 100644 program/src/option.rs create mode 100644 program/src/processor.rs create mode 100644 program/src/state.rs diff --git a/program/Cargo.toml b/program/Cargo.toml new file mode 100644 index 0000000..f370b73 --- /dev/null +++ b/program/Cargo.toml @@ -0,0 +1,35 @@ + +# Note: This crate must be built using do.sh + +[package] +name = "spl-token" +version = "1.0.6" +description = "Solana Program Library Token" +authors = ["Solana Maintainers "] +repository = "https://github.com/solana-labs/solana-program-library" +license = "Apache-2.0" +edition = "2018" +exclude = ["js/**"] + +[features] +no-entrypoint = [] +skip-no-mangle = ["solana-sdk/skip-no-mangle"] +program = ["solana-sdk/program"] +default = ["solana-sdk/default"] + +[dependencies] +num-derive = "0.3" +num-traits = "0.2" +remove_dir_all = "=0.5.0" +solana-sdk = { version = "1.2.17", default-features = false, optional = true } +thiserror = "1.0" + +[dev-dependencies] +rand = { version = "0.7.0"} + +[build-dependencies] +cbindgen = "=0.14.2" + +[lib] +name = "spl_token" +crate-type = ["cdylib", "lib"] diff --git a/program/README.md b/program/README.md new file mode 100644 index 0000000..53b4e01 --- /dev/null +++ b/program/README.md @@ -0,0 +1,10 @@ +# Token program + +A Fungible Token program on the Solana blockchain. + +This program provides an interface and implementation that third parties can +utilize to create and use their tokens. + +Full documentation is available at https://spl.solana.com + +Javascript binding are available in the `./js` directory. diff --git a/program/Xargo.toml b/program/Xargo.toml new file mode 100644 index 0000000..1744f09 --- /dev/null +++ b/program/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] \ No newline at end of file diff --git a/program/build.rs b/program/build.rs new file mode 100644 index 0000000..161616c --- /dev/null +++ b/program/build.rs @@ -0,0 +1,65 @@ +extern crate cbindgen; + +use std::env; + +fn main() { + println!("cargo:rerun-if-env-changed=SPL_CBINDGEN"); + println!("cargo:rerun-if-changed=inc/token.h"); + if std::path::Path::new("inc/token.h").exists() && env::var("SPL_CBINDGEN").is_err() { + return; + } + + println!("cargo:warning=Generating inc/token.h"); + let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let config = cbindgen::Config { + header: Some("/* Autogenerated SPL Token program C Bindings */".to_string()), + after_includes: Some(format!( + "{}{}{}", + format!( + "\n#define TOKEN_MAJOR_VERSION {}", + env!("CARGO_PKG_VERSION_MAJOR") + ), + format!( + "\n#define TOKEN_MINOR_VERSION {}", + env!("CARGO_PKG_VERSION_MINOR") + ), + format!( + "\n#define TOKEN_PATCH_VERSION {}", + env!("CARGO_PKG_VERSION_PATCH") + ) + )), + language: cbindgen::Language::C, + line_length: 80, + style: cbindgen::Style::Both, + tab_width: 4, + cpp_compat: true, + pragma_once: true, + enumeration: cbindgen::EnumConfig { + prefix_with_name: true, + ..cbindgen::EnumConfig::default() + }, + export: cbindgen::ExportConfig { + prefix: Some("Token_".to_string()), + include: vec![ + "TokenInstruction".to_string(), + "TokenInstruction".to_string(), + "Mint".to_string(), + "Account".to_string(), + "Multisig".to_string(), + ], + ..cbindgen::ExportConfig::default() + }, + parse: cbindgen::ParseConfig { + parse_deps: true, + include: Some(vec!["solana-sdk".to_string()]), + ..cbindgen::ParseConfig::default() + }, + ..cbindgen::Config::default() + }; + cbindgen::Builder::new() + .with_crate(crate_dir) + .with_config(config) + .generate() + .unwrap() + .write_to_file("inc/token.h"); +} diff --git a/program/inc/token.h b/program/inc/token.h new file mode 100644 index 0000000..2efb6c3 --- /dev/null +++ b/program/inc/token.h @@ -0,0 +1,359 @@ +/* Autogenerated SPL Token program C Bindings */ + +#pragma once + +#include +#include +#include +#include + +#define TOKEN_MAJOR_VERSION 1 +#define TOKEN_MINOR_VERSION 0 +#define TOKEN_PATCH_VERSION 6 + +/** + * Maximum number of multisignature signers (max N) + */ +#define Token_MAX_SIGNERS 11 + +/** + * Minimum number of multisignature signers (min N) + */ +#define Token_MIN_SIGNERS 1 + +/** + * Instructions supported by the token program. + */ +typedef enum Token_TokenInstruction_Tag { + /** + * Initializes a new mint and optionally deposits all the newly minted tokens in an account. + * + * The `InitializeMint` instruction requires no signers and MUST be included within + * the same Transaction as the system program's `CreateInstruction` that creates the account + * being initialized. Otherwise another party can acquire ownership of the uninitialized account. + * + * Accounts expected by this instruction: + * + * 0. `[writable]` The mint to initialize. + * 1. + * * If supply is non-zero: `[writable]` The account to hold all the newly minted tokens. + * * If supply is zero: `[]` The owner/multisignature of the mint. + * 2. `[]` (optional) The owner/multisignature of the mint if supply is non-zero, if + * present then further minting is supported. + * + */ + Token_TokenInstruction_InitializeMint, + /** + * Initializes a new account to hold tokens. If this account is associated with the native mint + * then the token balance of the initialized account will be equal to the amount of SOL in the account. + * + * The `InitializeAccount` instruction requires no signers and MUST be included within + * the same Transaction as the system program's `CreateInstruction` that creates the account + * being initialized. Otherwise another party can acquire ownership of the uninitialized account. + * + * Accounts expected by this instruction: + * + * 0. `[writable]` The account to initialize. + * 1. `[]` The mint this account will be associated with. + * 2. `[]` The new account's owner/multisignature. + */ + Token_TokenInstruction_InitializeAccount, + /** + * Initializes a multisignature account with N provided signers. + * + * Multisignature accounts can used in place of any single owner/delegate accounts in any + * token instruction that require an owner/delegate to be present. The variant field represents the + * number of signers (M) required to validate this multisignature account. + * + * The `InitializeMultisig` instruction requires no signers and MUST be included within + * the same Transaction as the system program's `CreateInstruction` that creates the account + * being initialized. Otherwise another party can acquire ownership of the uninitialized account. + * + * Accounts expected by this instruction: + * + * 0. `[writable]` The multisignature account to initialize. + * 1. ..1+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. + */ + Token_TokenInstruction_InitializeMultisig, + /** + * Transfers tokens from one account to another either directly or via a delegate. If this + * account is associated with the native mint then equal amounts of SOL and Tokens will be + * transferred to the destination account. + * + * Accounts expected by this instruction: + * + * * Single owner/delegate + * 0. `[writable]` The source account. + * 1. `[writable]` The destination account. + * 2. '[signer]' The source account's owner/delegate. + * + * * Multisignature owner/delegate + * 0. `[writable]` The source account. + * 1. `[writable]` The destination account. + * 2. '[]' The source account's multisignature owner/delegate. + * 3. ..3+M '[signer]' M signer accounts. + */ + Token_TokenInstruction_Transfer, + /** + * Approves a delegate. A delegate is given the authority over + * tokens on behalf of the source account's owner. + * Accounts expected by this instruction: + * + * * Single owner + * 0. `[writable]` The source account. + * 1. `[]` The delegate. + * 2. `[signer]` The source account owner. + * + * * Multisignature owner + * 0. `[writable]` The source account. + * 1. `[]` The delegate. + * 2. '[]' The source account's multisignature owner. + * 3. ..3+M '[signer]' M signer accounts + */ + Token_TokenInstruction_Approve, + /** + * Revokes the delegate's authority. + * + * Accounts expected by this instruction: + * + * * Single owner + * 0. `[writable]` The source account. + * 1. `[signer]` The source account owner. + * + * * Multisignature owner + * 0. `[writable]` The source account. + * 1. '[]' The source account's multisignature owner. + * 2. ..2+M '[signer]' M signer accounts + */ + Token_TokenInstruction_Revoke, + /** + * Sets a new owner of a mint or account. + * + * Accounts expected by this instruction: + * + * * Single owner + * 0. `[writable]` The mint or account to change the owner of. + * 1. `[]` The new owner/delegate/multisignature. + * 2. `[signer]` The owner of the mint or account. + * + * * Multisignature owner + * 0. `[writable]` The mint or account to change the owner of. + * 1. `[]` The new owner/delegate/multisignature. + * 2. `[]` The mint's or account's multisignature owner. + * 3. ..3+M '[signer]' M signer accounts + */ + Token_TokenInstruction_SetOwner, + /** + * Mints new tokens to an account. The native mint does not support minting. + * + * Accounts expected by this instruction: + * + * * Single owner + * 0. `[writable]` The mint. + * 1. `[writable]` The account to mint tokens to. + * 2. `[signer]` The mint's owner. + * + * * Multisignature owner + * 0. `[writable]` The mint. + * 1. `[writable]` The account to mint tokens to. + * 2. `[]` The mint's multisignature owner. + * 3. ..3+M '[signer]' M signer accounts. + */ + Token_TokenInstruction_MintTo, + /** + * Burns tokens by removing them from an account. `Burn` does not support accounts + * associated with the native mint, use `CloseAccount` instead. + * + * Accounts expected by this instruction: + * + * * Single owner/delegate + * 0. `[writable]` The account to burn from. + * 1. `[signer]` The account's owner/delegate. + * + * * Multisignature owner/delegate + * 0. `[writable]` The account to burn from. + * 1. `[]` The account's multisignature owner/delegate. + * 2. ..2+M '[signer]' M signer accounts. + */ + Token_TokenInstruction_Burn, + /** + * Close an account by transferring all its SOL to the destination account. + * Non-native accounts may only be closed if its token amount is zero. + * + * Accounts expected by this instruction: + * + * * Single owner + * 0. `[writable]` The account to close. + * 1. '[writable]' The destination account. + * 2. `[signer]` The account's owner. + * + * * Multisignature owner + * 0. `[writable]` The account to close. + * 1. '[writable]' The destination account. + * 2. `[]` The account's multisignature owner. + * 3. ..3+M '[signer]' M signer accounts. + */ + Token_TokenInstruction_CloseAccount, +} Token_TokenInstruction_Tag; + +typedef struct Token_TokenInstruction_Token_InitializeMint_Body { + /** + * Initial amount of tokens to mint. + */ + uint64_t amount; + /** + * Number of base 10 digits to the right of the decimal place. + */ + uint8_t decimals; +} Token_TokenInstruction_Token_InitializeMint_Body; + +typedef struct Token_TokenInstruction_Token_InitializeMultisig_Body { + /** + * The number of signers (M) required to validate this multisignature account. + */ + uint8_t m; +} Token_TokenInstruction_Token_InitializeMultisig_Body; + +typedef struct Token_TokenInstruction_Token_Transfer_Body { + /** + * The amount of tokens to transfer. + */ + uint64_t amount; +} Token_TokenInstruction_Token_Transfer_Body; + +typedef struct Token_TokenInstruction_Token_Approve_Body { + /** + * The amount of tokens the delegate is approved for. + */ + uint64_t amount; +} Token_TokenInstruction_Token_Approve_Body; + +typedef struct Token_TokenInstruction_Token_MintTo_Body { + /** + * The amount of new tokens to mint. + */ + uint64_t amount; +} Token_TokenInstruction_Token_MintTo_Body; + +typedef struct Token_TokenInstruction_Token_Burn_Body { + /** + * The amount of tokens to burn. + */ + uint64_t amount; +} Token_TokenInstruction_Token_Burn_Body; + +typedef struct Token_TokenInstruction { + Token_TokenInstruction_Tag tag; + union { + Token_TokenInstruction_Token_InitializeMint_Body initialize_mint; + Token_TokenInstruction_Token_InitializeMultisig_Body initialize_multisig; + Token_TokenInstruction_Token_Transfer_Body transfer; + Token_TokenInstruction_Token_Approve_Body approve; + Token_TokenInstruction_Token_MintTo_Body mint_to; + Token_TokenInstruction_Token_Burn_Body burn; + }; +} Token_TokenInstruction; + +typedef uint8_t Token_Pubkey[32]; + +/** + * A C representation of Rust's `std::option::Option` + */ +typedef enum Token_COption_Pubkey_Tag { + /** + * No value + */ + Token_COption_Pubkey_None_Pubkey, + /** + * Some value `T` + */ + Token_COption_Pubkey_Some_Pubkey, +} Token_COption_Pubkey_Tag; + +typedef struct Token_COption_Pubkey_Token_Some_Body_Pubkey { + Token_Pubkey _0; +} Token_COption_Pubkey_Token_Some_Body_Pubkey; + +typedef struct Token_COption_Pubkey { + Token_COption_Pubkey_Tag tag; + union { + Token_COption_Pubkey_Token_Some_Body_Pubkey some; + }; +} Token_COption_Pubkey; + +/** + * Mint data. + */ +typedef struct Token_Mint { + /** + * Optional owner, used to mint new tokens. The owner may only + * be provided during mint creation. If no owner is present then the mint + * has a fixed supply and no further tokens may be minted. + */ + Token_COption_Pubkey owner; + /** + * Number of base 10 digits to the right of the decimal place. + */ + uint8_t decimals; + /** + * Is `true` if this structure has been initialized + */ + bool is_initialized; +} Token_Mint; + +/** + * Account data. + */ +typedef struct Token_Account { + /** + * The mint associated with this account + */ + Token_Pubkey mint; + /** + * The owner of this account. + */ + Token_Pubkey owner; + /** + * The amount of tokens this account holds. + */ + uint64_t amount; + /** + * If `delegate` is `Some` then `delegated_amount` represents + * the amount authorized by the delegate + */ + Token_COption_Pubkey delegate; + /** + * Is `true` if this structure has been initialized + */ + bool is_initialized; + /** + * Is this a native token + */ + bool is_native; + /** + * The amount delegated + */ + uint64_t delegated_amount; +} Token_Account; + +/** + * Multisignature data. + */ +typedef struct Token_Multisig { + /** + * Number of signers required + */ + uint8_t m; + /** + * Number of valid signers + */ + uint8_t n; + /** + * Is `true` if this structure has been initialized + */ + bool is_initialized; + /** + * Signer public keys + */ + Token_Pubkey signers[Token_MAX_SIGNERS]; +} Token_Multisig; diff --git a/program/js/.eslintignore b/program/js/.eslintignore new file mode 100644 index 0000000..502167f --- /dev/null +++ b/program/js/.eslintignore @@ -0,0 +1 @@ +/lib diff --git a/program/js/.eslintrc.js b/program/js/.eslintrc.js new file mode 100644 index 0000000..96d3aae --- /dev/null +++ b/program/js/.eslintrc.js @@ -0,0 +1,56 @@ +// eslint-disable-next-line +module.exports = { + // eslint-disable-line import/no-commonjs + env: { + browser: true, + es6: true, + node: true, + }, + extends: [ + 'eslint:recommended', + 'plugin:import/errors', + 'plugin:import/warnings', + ], + parser: 'babel-eslint', + parserOptions: { + sourceType: 'module', + ecmaVersion: 8, + }, + rules: { + 'no-trailing-spaces': ['error'], + 'import/first': ['error'], + 'import/no-commonjs': ['error'], + 'import/order': [ + 'error', + { + groups: [ + ['internal', 'external', 'builtin'], + ['index', 'sibling', 'parent'], + ], + 'newlines-between': 'always', + }, + ], + indent: [ + 'error', + 2, + { + MemberExpression: 1, + SwitchCase: 1, + }, + ], + 'linebreak-style': ['error', 'unix'], + 'no-console': [0], + quotes: [ + 'error', + 'single', + {avoidEscape: true, allowTemplateLiterals: true}, + ], + 'require-await': ['error'], + semi: ['error', 'always'], + }, + settings: { + react: { + version: 'detect', + }, + }, +}; diff --git a/program/js/.flowconfig b/program/js/.flowconfig new file mode 100644 index 0000000..486f7e7 --- /dev/null +++ b/program/js/.flowconfig @@ -0,0 +1,20 @@ +[ignore] +/node_modules/* + +[include] + +[libs] +node_modules/@solana/web3.js/module.flow.js +flow-typed/ + +[options] + +emoji=true +esproposal.class_instance_fields=enable +esproposal.class_static_fields=enable +esproposal.decorators=ignore +esproposal.export_star_as=enable +module.system.node.resolve_dirname=./src +module.use_strict=true +experimental.const_params=true +include_warnings=true diff --git a/program/js/.gitignore b/program/js/.gitignore new file mode 100644 index 0000000..a65b417 --- /dev/null +++ b/program/js/.gitignore @@ -0,0 +1 @@ +lib diff --git a/program/js/.prettierrc.yaml b/program/js/.prettierrc.yaml new file mode 100644 index 0000000..8deef5d --- /dev/null +++ b/program/js/.prettierrc.yaml @@ -0,0 +1,7 @@ +arrowParens: "avoid" +bracketSpacing: false +jsxBracketSameLine: false +semi: true +singleQuote: true +tabWidth: 2 +trailingComma: "all" diff --git a/program/js/README.md b/program/js/README.md new file mode 100644 index 0000000..5deb943 --- /dev/null +++ b/program/js/README.md @@ -0,0 +1,69 @@ +# Token Javascript API + +The Token JavaScript library comprises: + +* A library to interact with the on-chain program +* A test client that exercises the program +* Scripts to facilitate building the program + +## Getting Started + +First fetch the npm dependencies, including `@solana/web3.js`, by running: +```bash +$ npm install +``` + +### Select a Network + +The client connects to a local Solana cluster by default. + +To enable on-chain program logs, set the `RUST_LOG` environment variable: + +```bash +$ export RUST_LOG=solana_runtime::native_loader=trace,solana_runtime::system_instruction_processor=trace,solana_runtime::bank=debug,solana_bpf_loader=debug,solana_rbpf=debug +``` + +To start a local Solana cluster run: +```bash +$ npm run localnet:update +$ npm run localnet:up +``` + +Solana cluster logs are available with: +```bash +$ npm run localnet:logs +``` + +For more details on working with a local cluster, see the [full +instructions](https://github.com/solana-labs/solana-web3.js#local-network). + +### Build the on-chain program + +```bash +$ npm run build:program +``` + +### Run the test client + +```bash +$ npm run start +``` + +## Pointing to a public Solana cluster + +Solana maintains three public clusters: +- `devnet` - Development cluster with airdrops enabled +- `testnet` - Tour De Sol test cluster without airdrops enabled +- `mainnet-beta` - Main cluster + +Use npm scripts to configure which cluster. + +To point to `devnet`: +```bash +$ npm run cluster:devnet +``` + +To point back to the local cluster: +```bash +$ npm run cluster:localnet +``` diff --git a/program/js/babel.config.json b/program/js/babel.config.json new file mode 100644 index 0000000..9c47e88 --- /dev/null +++ b/program/js/babel.config.json @@ -0,0 +1,17 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "targets": { + "node": "10" + } + } + ], + "@babel/preset-flow" + ], + "plugins": [ + "@babel/plugin-transform-runtime", + "@babel/plugin-proposal-class-properties" + ] +} diff --git a/program/js/babel.rollup.config.json b/program/js/babel.rollup.config.json new file mode 100644 index 0000000..ee7e695 --- /dev/null +++ b/program/js/babel.rollup.config.json @@ -0,0 +1,18 @@ +{ + "presets": [ + [ + "@babel/preset-env", + { + "modules": false, + "targets": { + "node": "10" + } + } + ], + "@babel/preset-flow" + ], + "plugins": [ + "@babel/plugin-transform-runtime", + "@babel/plugin-proposal-class-properties" + ] +} diff --git a/program/js/cli/main.js b/program/js/cli/main.js new file mode 100644 index 0000000..ee36e4f --- /dev/null +++ b/program/js/cli/main.js @@ -0,0 +1,58 @@ +/** + * Exercises the token program + * + * @flow + */ + +import { + loadTokenProgram, + createMint, + createAccount, + transfer, + approveRevoke, + invalidApprove, + failOnApproveOverspend, + setOwner, + mintTo, + multisig, + burn, + failOnCloseAccount, + nativeToken, +} from './token-test'; + +async function main() { + console.log('Run test: loadTokenProgram'); + await loadTokenProgram(); + console.log('Run test: createMint'); + await createMint(); + console.log('Run test: createAccount'); + await createAccount(); + console.log('Run test: transfer'); + await transfer(); + console.log('Run test: approveRevoke'); + await approveRevoke(); + console.log('Run test: invalidApprove'); + await invalidApprove(); + console.log('Run test: failOnApproveOverspend'); + await failOnApproveOverspend(); + console.log('Run test: setOwner'); + await setOwner(); + console.log('Run test: mintTo'); + await mintTo(); + console.log('Run test: multisig'); + await multisig(); + console.log('Run test: burn'); + await burn(); + console.log('Run test: failOnCloseAccount'); + await failOnCloseAccount(); + console.log('Run test: nativeToken'); + await nativeToken(); + console.log('Success\n'); +} + +main() + .catch(err => { + console.error(err); + process.exit(-1); + }) + .then(() => process.exit()); diff --git a/program/js/cli/token-test.js b/program/js/cli/token-test.js new file mode 100644 index 0000000..c0523d9 --- /dev/null +++ b/program/js/cli/token-test.js @@ -0,0 +1,500 @@ +// @flow + +import fs from 'mz/fs'; +import {Account, Connection, BpfLoader, PublicKey} from '@solana/web3.js'; +import semver from 'semver'; + +import {Token, u64} from '../client/token'; +import {url} from '../url'; +import {newAccountWithLamports} from '../client/util/new-account-with-lamports'; +import {sleep} from '../client/util/sleep'; +import {Store} from '../client/util/store'; + +// Loaded token program's program id +let programId: PublicKey; + +// A token created by the next test and used by all subsequent tests +let mintOwner: Account; +let testToken: Token; +// Initial token account +let testAccountOwner: Account; +let testAccount: PublicKey; + +// A mintable token used by multiple tests +let mintableOwner: Account; +let testMintableToken: Token; +// Initial token account +let testMintableAccountOwner: Account; +let testMintableAccount: PublicKey; + +function assert(condition, message) { + if (!condition) { + console.log(Error().stack + ':token-test.js'); + throw message || 'Assertion failed'; + } +} + +async function didThrow(func, args): Promise { + try { + await func.apply(args); + } catch (e) { + return true; + } + return false; +} + +let connection; +async function getConnection(): Promise { + if (connection) return connection; + + let newConnection = new Connection(url, 'recent'); + const version = await newConnection.getVersion(); + + // commitment params are only supported >= 0.21.0 + const solanaCoreVersion = version['solana-core'].split(' ')[0]; + if (semver.gte(solanaCoreVersion, '0.21.0')) { + newConnection = new Connection(url, 'recent'); + } + + // eslint-disable-next-line require-atomic-updates + connection = newConnection; + console.log('Connection to cluster established:', url, version); + return connection; +} + +async function loadProgram( + connection: Connection, + path: string, +): Promise { + const NUM_RETRIES = 500; /* allow some number of retries */ + const data = await fs.readFile(path); + const {feeCalculator} = await connection.getRecentBlockhash(); + const balanceNeeded = + feeCalculator.lamportsPerSignature * + (BpfLoader.getMinNumSignatures(data.length) + NUM_RETRIES) + + (await connection.getMinimumBalanceForRentExemption(data.length)); + + const from = await newAccountWithLamports(connection, balanceNeeded); + const program_account = new Account(); + console.log('Loading program:', path); + await BpfLoader.load(connection, from, program_account, data); + return program_account.publicKey; +} + +async function GetPrograms(connection: Connection): Promise { + const store = new Store(); + let tokenProgramId = null; + try { + const config = await store.load('config.json'); + console.log('Using pre-loaded Token program'); + console.log( + ' Note: To reload program remove client/util/store/config.json', + ); + tokenProgramId = new PublicKey(config.tokenProgramId); + } catch (err) { + tokenProgramId = await loadProgram( + connection, + '../../target/bpfel-unknown-unknown/release/spl_token.so', + ); + await store.save('config.json', { + tokenProgramId: tokenProgramId.toString(), + }); + } + return tokenProgramId; +} + +export async function loadTokenProgram(): Promise { + const connection = await getConnection(); + programId = await GetPrograms(connection); + + console.log('Token Program ID', programId.toString()); +} + +export async function createMint(): Promise { + const connection = await getConnection(); + const payer = await newAccountWithLamports( + connection, + 100000000000 /* wag */, + ); + mintOwner = new Account(); + testAccountOwner = new Account(); + [testToken, testAccount] = await Token.createMint( + connection, + payer, + mintOwner.publicKey, + testAccountOwner.publicKey, + new u64(10000), + 2, + programId, + false, + ); + + const mintInfo = await testToken.getMintInfo(); + assert(mintInfo.decimals == 2); + assert(mintInfo.owner == null); + + const accountInfo = await testToken.getAccountInfo(testAccount); + assert(accountInfo.mint.equals(testToken.publicKey)); + assert(accountInfo.owner.equals(testAccountOwner.publicKey)); + assert(accountInfo.amount.toNumber() == 10000); + assert(accountInfo.delegate == null); + assert(accountInfo.delegatedAmount.toNumber() == 0); +} + +export async function createAccount(): Promise { + const destOwner = new Account(); + const account = await testToken.createAccount(destOwner.publicKey); + const accountInfo = await testToken.getAccountInfo(account); + assert(accountInfo.mint.equals(testToken.publicKey)); + assert(accountInfo.owner.equals(destOwner.publicKey)); + assert(accountInfo.amount.toNumber() == 0); + assert(accountInfo.delegate == null); +} + +export async function transfer(): Promise { + const destOwner = new Account(); + const dest = await testToken.createAccount(destOwner.publicKey); + + await testToken.transfer(testAccount, dest, testAccountOwner, [], 123); + await sleep(500); + + let destAccountInfo = await testToken.getAccountInfo(dest); + assert(destAccountInfo.amount.toNumber() == 123); +} + +export async function approveRevoke(): Promise { + if (programId == null) { + console.log('test skipped, requires "load token program" to succeed'); + return; + } + + const delegate = new PublicKey(); + await testToken.approve(testAccount, delegate, testAccountOwner, [], 456); + let testAccountInfo = await testToken.getAccountInfo(testAccount); + assert(testAccountInfo.delegatedAmount.toNumber() == 456); + if (testAccountInfo.delegate === null) { + throw new Error('deleage should not be null'); + } else { + assert(testAccountInfo.delegate.equals(delegate)); + } + + await testToken.revoke(testAccount, testAccountOwner, []); + testAccountInfo = await testToken.getAccountInfo(testAccount); + assert(testAccountInfo.delegatedAmount.toNumber() == 0); + if (testAccountInfo.delegate != null) { + throw new Error('delegate should be null'); + } +} + +export async function invalidApprove(): Promise { + const owner = new Account(); + const account1 = await testToken.createAccount(owner.publicKey); + const account2 = await testToken.createAccount(owner.publicKey); + const delegate = new Account(); + + // account2 is not a delegate account of account1 + assert(didThrow(testToken.approve, [account1, account2, owner, [], 123])); + // account1Delegate is not a delegate account of account2 + assert(didThrow(testToken.approve, [account2, delegate, owner, [], 123])); +} + +export async function failOnApproveOverspend(): Promise { + const owner = new Account(); + const account1 = await testToken.createAccount(owner.publicKey); + const account2 = await testToken.createAccount(owner.publicKey); + const delegate = new Account(); + + await testToken.transfer(testAccount, account1, testAccountOwner, [], 10); + + await testToken.approve(account1, delegate.publicKey, owner, [], 2); + + let account1Info = await testToken.getAccountInfo(account1); + assert(account1Info.amount.toNumber() == 10); + assert(account1Info.delegatedAmount.toNumber() == 2); + if (account1Info.delegate === null) { + throw new Error('deleage should not be null'); + } else { + assert(account1Info.delegate.equals(delegate.publicKey)); + } + + await testToken.transfer(account1, account2, delegate, [], 1); + + account1Info = await testToken.getAccountInfo(account1); + assert(account1Info.amount.toNumber() == 9); + assert(account1Info.delegatedAmount.toNumber() == 1); + + await testToken.transfer(account1, account2, delegate, [], 1); + + account1Info = await testToken.getAccountInfo(account1); + assert(account1Info.amount.toNumber() == 8); + assert(account1Info.delegate === null); + assert(account1Info.delegatedAmount.toNumber() == 0); + + assert(didThrow(testToken.transfer, [account1, account2, delegate, [], 1])); +} + +export async function setOwner(): Promise { + const owner = new Account(); + const newOwner = new Account(); + const owned = await testToken.createAccount(owner.publicKey); + + await testToken.setOwner(owned, newOwner.publicKey, owner, []); + assert(didThrow(testToken.setOwner, [owned, newOwner.publicKey, owner, []])); + await testToken.setOwner(owned, owner.publicKey, newOwner, []); +} + +export async function mintTo(): Promise { + const connection = await getConnection(); + const payer = await newAccountWithLamports( + connection, + 100000000000 /* wag */, + ); + mintableOwner = new Account(); + testMintableAccountOwner = new Account(); + [testMintableToken, testMintableAccount] = await Token.createMint( + connection, + payer, + mintableOwner.publicKey, + testMintableAccountOwner.publicKey, + new u64(10000), + 2, + programId, + true, + ); + + { + const mintInfo = await testMintableToken.getMintInfo(); + assert(mintInfo.decimals == 2); + if (mintInfo.owner === null) { + throw new Error('owner should not be null'); + } else { + assert(mintInfo.owner.equals(mintableOwner.publicKey)); + } + + const accountInfo = await testMintableToken.getAccountInfo( + testMintableAccount, + ); + assert(accountInfo.mint.equals(testMintableToken.publicKey)); + assert(accountInfo.owner.equals(testMintableAccountOwner.publicKey)); + assert(accountInfo.amount.toNumber() == 10000); + assert(accountInfo.delegate == null); + assert(accountInfo.delegatedAmount.toNumber() == 0); + } + + const dest = await testMintableToken.createAccount( + testMintableAccountOwner.publicKey, + ); + await testMintableToken.mintTo(dest, mintableOwner, [], 42); + + { + const mintInfo = await testMintableToken.getMintInfo(); + assert(mintInfo.decimals == 2); + if (mintInfo.owner === null) { + throw new Error('owner should not be null'); + } else { + assert(mintInfo.owner.equals(mintableOwner.publicKey)); + } + + const accountInfo = await testMintableToken.getAccountInfo(dest); + assert(accountInfo.mint.equals(testMintableToken.publicKey)); + assert(accountInfo.owner.equals(testMintableAccountOwner.publicKey)); + assert(accountInfo.amount.toNumber() == 42); + assert(accountInfo.delegate == null); + assert(accountInfo.delegatedAmount.toNumber() == 0); + } +} + +export async function burn(): Promise { + let accountInfo = await testToken.getAccountInfo(testAccount); + const amount = accountInfo.amount.toNumber(); + + await testToken.burn(testAccount, testAccountOwner, [], 1); + await sleep(500); + + accountInfo = await testToken.getAccountInfo(testAccount); + assert(accountInfo.amount.toNumber() == amount - 1); +} + +export async function multisig(): Promise { + const m = 2; + const n = 5; + + let signerAccounts = []; + for (var i = 0; i < n; i++) { + signerAccounts.push(new Account()); + } + let signerPublicKeys = []; + signerAccounts.forEach(account => signerPublicKeys.push(account.publicKey)); + const multisig = await testToken.createMultisig(m, signerPublicKeys); + + const multisigInfo = await testToken.getMultisigInfo(multisig); + assert(multisigInfo.m === m); + assert(multisigInfo.n === n); + assert(multisigInfo.signer1.equals(signerPublicKeys[0])); + assert(multisigInfo.signer2.equals(signerPublicKeys[1])); + assert(multisigInfo.signer3.equals(signerPublicKeys[2])); + assert(multisigInfo.signer4.equals(signerPublicKeys[3])); + assert(multisigInfo.signer5.equals(signerPublicKeys[4])); + + const multisigOwnedAccount = await testToken.createAccount(multisig); + const finalDest = await testToken.createAccount(multisig); + await testToken.transfer( + testAccount, + multisigOwnedAccount, + testAccountOwner, + [], + 2, + ); + + // Transfer via multisig + await testToken.transfer( + multisigOwnedAccount, + finalDest, + multisig, + signerAccounts, + 1, + ); + await sleep(500); + let accountInfo = await testToken.getAccountInfo(finalDest); + assert(accountInfo.amount.toNumber() == 1); + + // Approve via multisig + { + const delegate = new PublicKey(); + await testToken.approve( + multisigOwnedAccount, + delegate, + multisig, + signerAccounts, + 1, + ); + const accountInfo = await testToken.getAccountInfo(multisigOwnedAccount); + assert(accountInfo.delegate != null); + if (accountInfo.delegate != null) { + assert(accountInfo.delegate.equals(delegate)); + assert(accountInfo.delegatedAmount.toNumber() == 1); + } + } + + // MintTo via multisig + { + let accountInfo = await testMintableToken.getAccountInfo( + testMintableAccount, + ); + const initialAmount = accountInfo.amount.toNumber(); + await testMintableToken.setOwner( + testMintableToken.publicKey, + multisig, + mintableOwner, + [], + ); + await testMintableToken.mintTo( + testMintableAccount, + multisig, + signerAccounts, + 42, + ); + accountInfo = await testMintableToken.getAccountInfo(testMintableAccount); + assert(accountInfo.amount.toNumber() == initialAmount + 42); + } + + // SetOwner of mint via multisig + { + await testMintableToken.setOwner( + testMintableToken.publicKey, + mintableOwner.publicKey, + multisig, + signerAccounts, + ); + const mintInfo = await testMintableToken.getMintInfo(); + assert(mintInfo.owner != null); + if (mintInfo.owner != null) { + assert(mintInfo.owner.equals(mintableOwner.publicKey)); + } + } + + // SetOwner of account via multisig + { + const newOwner = new PublicKey(); + await testToken.setOwner( + multisigOwnedAccount, + newOwner, + multisig, + signerAccounts, + ); + const accountInfo = await testToken.getAccountInfo(multisigOwnedAccount); + assert(accountInfo.owner.equals(newOwner)); + } +} + +export async function failOnCloseAccount(): Promise { + const connection = await getConnection(); + const owner = new Account(); + const close = await testToken.createAccount(owner.publicKey); + + let close_balance; + let info = await connection.getAccountInfo(close); + if (info != null) { + close_balance = info.lamports; + } else { + throw new Error('Account not found'); + } + + // Initialize destination account to isolate source of failure + const balanceNeeded = await connection.getMinimumBalanceForRentExemption(0); + const dest = await newAccountWithLamports(connection, balanceNeeded); + + info = await connection.getAccountInfo(dest.publicKey); + if (info != null) { + assert(info.lamports == balanceNeeded); + } else { + throw new Error('Account not found'); + } + + assert(didThrow(testToken.closeAccount, [close, dest.publicKey, owner, []])); + + info = await connection.getAccountInfo(close); + if (info != null) { + assert(info.lamports == close_balance); + } else { + throw new Error('Account not found'); + } +} + +export async function nativeToken(): Promise { + const connection = await getConnection(); + + const mintPublicKey = new PublicKey( + 'So11111111111111111111111111111111111111111', + ); + const payer = await newAccountWithLamports( + connection, + 100000000000 /* wag */, + ); + const token = new Token(connection, mintPublicKey, programId, payer); + const owner = new Account(); + const native = await token.createAccount(owner.publicKey); + let accountInfo = await token.getAccountInfo(native); + assert(accountInfo.isNative); + let balance; + let info = await connection.getAccountInfo(native); + if (info != null) { + balance = info.lamports; + } else { + throw new Error('Account not found'); + } + + const balanceNeeded = await connection.getMinimumBalanceForRentExemption(0); + const dest = await newAccountWithLamports(connection, balanceNeeded); + await token.closeAccount(native, dest.publicKey, owner, []); + info = await connection.getAccountInfo(native); + if (info != null) { + throw new Error('Account not burned'); + } + info = await connection.getAccountInfo(dest.publicKey); + if (info != null) { + assert(info.lamports == balanceNeeded + balance); + } else { + throw new Error('Account not found'); + } +} diff --git a/program/js/client/layout.js b/program/js/client/layout.js new file mode 100644 index 0000000..6c3e06b --- /dev/null +++ b/program/js/client/layout.js @@ -0,0 +1,47 @@ +// @flow + +import * as BufferLayout from 'buffer-layout'; + +/** + * Layout for a public key + */ +export const publicKey = (property: string = 'publicKey'): Object => { + return BufferLayout.blob(32, property); +}; + +/** + * Layout for a 64bit unsigned value + */ +export const uint64 = (property: string = 'uint64'): Object => { + return BufferLayout.blob(8, property); +}; + +/** + * Layout for a Rust String type + */ +export const rustString = (property: string = 'string') => { + const rsl = BufferLayout.struct( + [ + BufferLayout.u32('length'), + BufferLayout.u32('lengthPadding'), + BufferLayout.blob(BufferLayout.offset(BufferLayout.u32(), -8), 'chars'), + ], + property, + ); + const _decode = rsl.decode.bind(rsl); + const _encode = rsl.encode.bind(rsl); + + rsl.decode = (buffer, offset) => { + const data = _decode(buffer, offset); + return data.chars.toString('utf8'); + }; + + rsl.encode = (str, buffer, offset) => { + const data = { + chars: Buffer.from(str, 'utf8'), + }; + return _encode(data, buffer, offset); + }; + + return rsl; +}; diff --git a/program/js/client/token.js b/program/js/client/token.js new file mode 100644 index 0000000..e59a2ab --- /dev/null +++ b/program/js/client/token.js @@ -0,0 +1,1242 @@ +/** + * @flow + */ + +import assert from 'assert'; +import BN from 'bn.js'; +import * as BufferLayout from 'buffer-layout'; +import { + Account, + PublicKey, + SystemProgram, + Transaction, + TransactionInstruction, +} from '@solana/web3.js'; +import type {Connection, TransactionSignature} from '@solana/web3.js'; + +import * as Layout from './layout'; +import {sendAndConfirmTransaction} from './util/send-and-confirm-transaction'; + +/** + * 64-bit value + */ +export class u64 extends BN { + /** + * Convert to Buffer representation + */ + toBuffer(): Buffer { + const a = super.toArray().reverse(); + const b = Buffer.from(a); + if (b.length === 8) { + return b; + } + assert(b.length < 8, 'u64 too large'); + + const zeroPad = Buffer.alloc(8); + b.copy(zeroPad); + return zeroPad; + } + + /** + * Construct a u64 from Buffer representation + */ + static fromBuffer(buffer: Buffer): u64 { + assert(buffer.length === 8, `Invalid buffer length: ${buffer.length}`); + return new BN( + [...buffer] + .reverse() + .map(i => `00${i.toString(16)}`.slice(-2)) + .join(''), + 16, + ); + } +} + +function isAccount(accountOrPublicKey: any): boolean { + return 'publicKey' in accountOrPublicKey; +} + +/** + * Information about the mint + */ +type MintInfo = {| + /** + * Owner of the mint, given authority to mint new tokens + */ + owner: null | PublicKey, + + /** + * Number of base 10 digits to the right of the decimal place + */ + decimals: number, + + /** + * Is this mint initialized + */ + initialized: boolean, +|}; + +const MintLayout = BufferLayout.struct([ + BufferLayout.u32('option'), + Layout.publicKey('owner'), + BufferLayout.u8('decimals'), + BufferLayout.u8('is_initialized'), + BufferLayout.u16('padding'), +]); + +/** + * Information about an account + */ +type AccountInfo = {| + /** + * The mint associated with this account + */ + mint: PublicKey, + + /** + * Owner of this account + */ + owner: PublicKey, + + /** + * Amount of tokens this account holds + */ + amount: u64, + + /** + * The delegate for this account + */ + delegate: null | PublicKey, + + /** + * The amount of tokens the delegate authorized to the delegate + */ + delegatedAmount: u64, + + /** + * Is this account initialized + */ + isInitialized: boolean, + + /** + * Is this a native token account + */ + isNative: boolean, +|}; + +/** + * @private + */ +const AccountLayout = BufferLayout.struct([ + Layout.publicKey('mint'), + Layout.publicKey('owner'), + Layout.uint64('amount'), + BufferLayout.u32('option'), + Layout.publicKey('delegate'), + BufferLayout.u8('is_initialized'), + BufferLayout.u8('is_native'), + BufferLayout.u16('padding'), + Layout.uint64('delegatedAmount'), +]); + +/** + * Information about an multisig + */ +type MultisigInfo = {| + /** + * The number of signers required + */ + m: number, + + /** + * Number of possible signers, corresponds to the + * number of `signers` that are valid. + */ + n: number, + + /** + * Is this mint initialized + */ + initialized: boolean, + + /** + * The signers + */ + signer1: PublicKey, + signer2: PublicKey, + signer3: PublicKey, + signer4: PublicKey, + signer5: PublicKey, + signer6: PublicKey, + signer7: PublicKey, + signer8: PublicKey, + signer9: PublicKey, + signer10: PublicKey, + signer11: PublicKey, +|}; + +/** + * @private + */ +const MultisigLayout = BufferLayout.struct([ + BufferLayout.u8('m'), + BufferLayout.u8('n'), + BufferLayout.u8('is_initialized'), + Layout.publicKey('signer1'), + Layout.publicKey('signer2'), + Layout.publicKey('signer3'), + Layout.publicKey('signer4'), + Layout.publicKey('signer5'), + Layout.publicKey('signer6'), + Layout.publicKey('signer7'), + Layout.publicKey('signer8'), + Layout.publicKey('signer9'), + Layout.publicKey('signer10'), + Layout.publicKey('signer11'), +]); + +type TokenAndPublicKey = [Token, PublicKey]; // This type exists to workaround an esdoc parse error + +/** + * An ERC20-like Token + */ +export class Token { + /** + * @private + */ + connection: Connection; + + /** + * The public key identifying this mint + */ + publicKey: PublicKey; + + /** + * Program Identifier for the Token program + */ + programId: PublicKey; + + /** + * Fee payer + */ + payer: Account; + + /** + * Create a Token object attached to the specific mint + * + * @param connection The connection to use + * @param token Public key of the mint + * @param programId token programId + * @param payer Payer of fees + */ + constructor( + connection: Connection, + publicKey: PublicKey, + programId: PublicKey, + payer: Account, + ) { + Object.assign(this, {connection, publicKey, programId, payer}); + } + + /** + * Get the minimum balance for the mint to be rent exempt + * + * @return Number of lamports required + */ + static async getMinBalanceRentForExemptMint( + connection: Connection, + ): Promise { + return await connection.getMinimumBalanceForRentExemption(MintLayout.span); + } + + /** + * Get the minimum balance for the account to be rent exempt + * + * @return Number of lamports required + */ + static async getMinBalanceRentForExemptAccount( + connection: Connection, + ): Promise { + return await connection.getMinimumBalanceForRentExemption( + AccountLayout.span, + ); + } + + /** + * Get the minimum balance for the multsig to be rent exempt + * + * @return Number of lamports required + */ + static async getMinBalanceRentForExemptMultisig( + connection: Connection, + ): Promise { + return await connection.getMinimumBalanceForRentExemption( + MultisigLayout.span, + ); + } + + /** + * Creates and initializes a token. + * + * @param connection The connection to use + * @param owner User account that will own the returned account + * @param supply Initial supply to mint + * @param decimals Location of the decimal place + * @param programId Optional token programId, uses the system programId by default + * @return Token object for the newly minted token, Public key of the account + * holding the total amount of new tokens + */ + static async createMint( + connection: Connection, + payer: Account, + mintOwner: PublicKey, + accountOwner: PublicKey, + supply: u64, + decimals: number, + programId: PublicKey, + is_owned: boolean = false, + ): Promise { + let transaction; + const mintAccount = new Account(); + const token = new Token( + connection, + mintAccount.publicKey, + programId, + payer, + ); + const initialAccountPublicKey = await token.createAccount(accountOwner); + + // Allocate memory for the account + const balanceNeeded = await Token.getMinBalanceRentForExemptMint( + connection, + ); + transaction = SystemProgram.createAccount({ + fromPubkey: payer.publicKey, + newAccountPubkey: mintAccount.publicKey, + lamports: balanceNeeded, + space: MintLayout.span, + programId, + }); + + // Create the mint + let keys = [ + {pubkey: mintAccount.publicKey, isSigner: false, isWritable: true}, + ]; + if (supply.toNumber() != 0) { + keys.push({ + pubkey: initialAccountPublicKey, + isSigner: false, + isWritable: true, + }); + } + if (is_owned) { + keys.push({pubkey: mintOwner, isSigner: false, isWritable: false}); + } + const commandDataLayout = BufferLayout.struct([ + BufferLayout.u8('instruction'), + Layout.uint64('supply'), + BufferLayout.u8('decimals'), + ]); + let data = Buffer.alloc(1024); + { + const encodeLength = commandDataLayout.encode( + { + instruction: 0, // InitializeMint instruction + supply: supply.toBuffer(), + decimals, + }, + data, + ); + data = data.slice(0, encodeLength); + } + transaction.add({ + keys, + programId, + data, + }); + + // Send the two instructions + await sendAndConfirmTransaction( + 'createAccount and InitializeMint', + connection, + transaction, + payer, + mintAccount, + ); + + return [token, initialAccountPublicKey]; + } + + /** + * Create and initializes a new account. + * + * This account may then be used as a `transfer()` or `approve()` destination + * + * @param owner User account that will own the new account + * @return Public key of the new empty account + */ + async createAccount(owner: PublicKey): Promise { + const mintAccount = new Account(); + let transaction; + + // Allocate memory for the account + const balanceNeeded = await Token.getMinBalanceRentForExemptAccount( + this.connection, + ); + transaction = SystemProgram.createAccount({ + fromPubkey: this.payer.publicKey, + newAccountPubkey: mintAccount.publicKey, + lamports: balanceNeeded, + space: AccountLayout.span, + programId: this.programId, + }); + + // create the new account + const keys = [ + {pubkey: mintAccount.publicKey, isSigner: false, isWritable: true}, + {pubkey: this.publicKey, isSigner: false, isWritable: false}, + {pubkey: owner, isSigner: false, isWritable: false}, + ]; + const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]); + const data = Buffer.alloc(dataLayout.span); + dataLayout.encode( + { + instruction: 1, // InitializeAccount instruction + }, + data, + ); + transaction.add({ + keys, + programId: this.programId, + data, + }); + + // Send the two instructions + await sendAndConfirmTransaction( + 'createAccount and InitializeAccount', + this.connection, + transaction, + this.payer, + mintAccount, + ); + + return mintAccount.publicKey; + } + + /** + * Create and initializes a new multisig. + * + * This account may then be used for multisignature verification + * + * @param owner User account that will own the multsig account + * @return Public key of the new multisig account + */ + async createMultisig( + m: number, + signers: Array, + ): Promise { + const multisigAccount = new Account(); + let transaction; + + // Allocate memory for the account + const balanceNeeded = await Token.getMinBalanceRentForExemptMultisig( + this.connection, + ); + transaction = SystemProgram.createAccount({ + fromPubkey: this.payer.publicKey, + newAccountPubkey: multisigAccount.publicKey, + lamports: balanceNeeded, + space: MultisigLayout.span, + programId: this.programId, + }); + + // create the new account + let keys = [ + {pubkey: multisigAccount.publicKey, isSigner: false, isWritable: true}, + ]; + signers.forEach(signer => + keys.push({pubkey: signer, isSigner: false, isWritable: false}), + ); + const dataLayout = BufferLayout.struct([ + BufferLayout.u8('instruction'), + BufferLayout.u8('m'), + ]); + const data = Buffer.alloc(dataLayout.span); + dataLayout.encode( + { + instruction: 2, // InitializeM { + const info = await this.connection.getAccountInfo(this.publicKey); + if (info === null) { + throw new Error('Failed to find mint account'); + } + if (!info.owner.equals(this.programId)) { + throw new Error(`Invalid mint owner: ${JSON.stringify(info.owner)}`); + } + if (info.data.length != MintLayout.span) { + throw new Error(`Invalid mint size`); + } + + const data = Buffer.from(info.data); + const mintInfo = MintLayout.decode(data); + if (mintInfo.option === 0) { + mintInfo.owner = null; + } else { + mintInfo.owner = new PublicKey(mintInfo.owner); + } + return mintInfo; + } + + /** + * Retrieve account information + * + * @param account Public key of the account + */ + async getAccountInfo(account: PublicKey): Promise { + const info = await this.connection.getAccountInfo(account); + if (info === null) { + throw new Error('Failed to find account'); + } + if (!info.owner.equals(this.programId)) { + throw new Error(`Invalid account owner`); + } + if (info.data.length != AccountLayout.span) { + throw new Error(`Invalid account size`); + } + + const data = Buffer.from(info.data); + const accountInfo = AccountLayout.decode(data); + accountInfo.mint = new PublicKey(accountInfo.mint); + accountInfo.owner = new PublicKey(accountInfo.owner); + accountInfo.amount = u64.fromBuffer(accountInfo.amount); + accountInfo.isInitialized = accountInfo.isInitialized != 0; + accountInfo.isNative = accountInfo.isNative != 0; + if (accountInfo.option === 0) { + accountInfo.delegate = null; + accountInfo.delegatedAmount = new u64(); + } else { + accountInfo.delegate = new PublicKey(accountInfo.delegate); + accountInfo.delegatedAmount = u64.fromBuffer(accountInfo.delegatedAmount); + } + + if (!accountInfo.mint.equals(this.publicKey)) { + throw new Error( + `Invalid account mint: ${JSON.stringify( + accountInfo.mint, + )} !== ${JSON.stringify(this.publicKey)}`, + ); + } + return accountInfo; + } + + /** + * Retrieve Multisig information + * + * @param multisig Public key of the account + */ + async getMultisigInfo(multisig: PublicKey): Promise { + const info = await this.connection.getAccountInfo(multisig); + if (info === null) { + throw new Error('Failed to find multisig'); + } + if (!info.owner.equals(this.programId)) { + throw new Error(`Invalid multisig owner`); + } + if (info.data.length != MultisigLayout.span) { + throw new Error(`Invalid multisig size`); + } + + const data = Buffer.from(info.data); + const multisigInfo = MultisigLayout.decode(data); + multisigInfo.signer1 = new PublicKey(multisigInfo.signer1); + multisigInfo.signer2 = new PublicKey(multisigInfo.signer2); + multisigInfo.signer3 = new PublicKey(multisigInfo.signer3); + multisigInfo.signer4 = new PublicKey(multisigInfo.signer4); + multisigInfo.signer5 = new PublicKey(multisigInfo.signer5); + multisigInfo.signer6 = new PublicKey(multisigInfo.signer6); + multisigInfo.signer7 = new PublicKey(multisigInfo.signer7); + multisigInfo.signer8 = new PublicKey(multisigInfo.signer8); + multisigInfo.signer9 = new PublicKey(multisigInfo.signer9); + multisigInfo.signer10 = new PublicKey(multisigInfo.signer10); + multisigInfo.signer11 = new PublicKey(multisigInfo.signer11); + + return multisigInfo; + } + + /** + * Transfer tokens to another account + * + * @param source Source account + * @param destination Destination account + * @param authority Owner of the source account + * @param multiSigners Signing accounts if `authority` is a multiSig + * @param amount Number of tokens to transfer + */ + async transfer( + source: PublicKey, + destination: PublicKey, + authority: any, + multiSigners: Array, + amount: number | u64, + ): Promise { + let ownerPublicKey; + let signers; + if (isAccount(authority)) { + ownerPublicKey = authority.publicKey; + signers = [authority]; + } else { + ownerPublicKey = authority; + signers = multiSigners; + } + return await sendAndConfirmTransaction( + 'Transfer', + this.connection, + new Transaction().add( + Token.createTransferInstruction( + this.programId, + source, + destination, + ownerPublicKey, + multiSigners, + amount, + ), + ), + this.payer, + ...signers, + ); + } + + /** + * Grant a third-party permission to transfer up the specified number of tokens from an account + * + * @param account Public key of the account + * @param delegate Account authorized to perform a transfer tokens from the source account + * @param owner Owner of the source account + * @param multiSigners Signing accounts if `owner` is a multiSig + * @param amount Maximum number of tokens the delegate may transfer + */ + async approve( + account: PublicKey, + delegate: PublicKey, + owner: any, + multiSigners: Array, + amount: number | u64, + ): Promise { + let ownerPublicKey; + let signers; + if (isAccount(owner)) { + ownerPublicKey = owner.publicKey; + signers = [owner]; + } else { + ownerPublicKey = owner; + signers = multiSigners; + } + await sendAndConfirmTransaction( + 'Approve', + this.connection, + new Transaction().add( + Token.createApproveInstruction( + this.programId, + account, + delegate, + ownerPublicKey, + multiSigners, + amount, + ), + ), + this.payer, + ...signers, + ); + } + + /** + * Remove approval for the transfer of any remaining tokens + * + * @param account Public key of the account + * @param owner Owner of the source account + * @param multiSigners Signing accounts if `owner` is a multiSig + */ + async revoke( + account: PublicKey, + owner: any, + multiSigners: Array, + ): Promise { + let ownerPublicKey; + let signers; + if (isAccount(owner)) { + ownerPublicKey = owner.publicKey; + signers = [owner]; + } else { + ownerPublicKey = owner; + signers = multiSigners; + } + await sendAndConfirmTransaction( + 'Revoke', + this.connection, + new Transaction().add( + Token.createRevokeInstruction( + this.programId, + account, + ownerPublicKey, + multiSigners, + ), + ), + this.payer, + ...signers, + ); + } + + /** + * Assign a new owner to the account + * + * @param account Public key of the account + * @param newOwner New owner of the account + * @param owner Owner of the account + * @param multiSigners Signing accounts if `owner` is a multiSig + */ + async setOwner( + owned: PublicKey, + newOwner: PublicKey, + owner: any, + multiSigners: Array, + ): Promise { + let ownerPublicKey; + let signers; + if (isAccount(owner)) { + ownerPublicKey = owner.publicKey; + signers = [owner]; + } else { + ownerPublicKey = owner; + signers = multiSigners; + } + await sendAndConfirmTransaction( + 'SetOwner', + this.connection, + new Transaction().add( + Token.createSetOwnerInstruction( + this.programId, + owned, + newOwner, + ownerPublicKey, + multiSigners, + ), + ), + this.payer, + ...signers, + ); + } + + /** + * Mint new tokens + * + * @param mint Public key of the mint + * @param dest Public key of the account to mint to + * @param authority Owner of the mint + * @param multiSigners Signing accounts if `authority` is a multiSig + * @param amount ammount to mint + */ + async mintTo( + dest: PublicKey, + authority: any, + multiSigners: Array, + amount: number, + ): Promise { + let ownerPublicKey; + let signers; + if (isAccount(authority)) { + ownerPublicKey = authority.publicKey; + signers = [authority]; + } else { + ownerPublicKey = authority; + signers = multiSigners; + } + await sendAndConfirmTransaction( + 'MintTo', + this.connection, + new Transaction().add( + Token.createMintToInstruction( + this.programId, + this.publicKey, + dest, + ownerPublicKey, + multiSigners, + amount, + ), + ), + this.payer, + ...signers, + ); + } + + /** + * Burn tokens + * + * @param account Account to burn tokens from + * @param authority Public key account owner + * @param multiSigners Signing accounts if `authority` is a multiSig + * @param amount ammount to burn + */ + async burn( + account: PublicKey, + authority: any, + multiSigners: Array, + amount: number, + ): Promise { + let ownerPublicKey; + let signers; + if (isAccount(authority)) { + ownerPublicKey = authority.publicKey; + signers = [authority]; + } else { + ownerPublicKey = authority; + signers = multiSigners; + } + await sendAndConfirmTransaction( + 'Burn', + this.connection, + new Transaction().add( + Token.createBurnInstruction( + this.programId, + account, + ownerPublicKey, + multiSigners, + amount, + ), + ), + this.payer, + ...signers, + ); + } + + /** + * Burn account + * + * @param account Account to burn + * @param authority account owner + * @param multiSigners Signing accounts if `owner` is a multiSig + */ + async closeAccount( + account: PublicKey, + dest: PublicKey, + owner: any, + multiSigners: Array, + ): Promise { + let ownerPublicKey; + let signers; + if (isAccount(owner)) { + ownerPublicKey = owner.publicKey; + signers = [owner]; + } else { + ownerPublicKey = owner; + signers = multiSigners; + } + await sendAndConfirmTransaction( + 'CloseAccount', + this.connection, + new Transaction().add( + Token.createCloseAccountInstruction( + this.programId, + account, + dest, + ownerPublicKey, + multiSigners, + ), + ), + this.payer, + ...signers, + ); + } + + /** + * Construct a Transfer instruction + * + * @param source Source account + * @param destination Destination account + * @param authority Owner of the source account + * @param multiSigners Signing accounts if `authority` is a multiSig + * @param amount Number of tokens to transfer + */ + static createTransferInstruction( + programId: PublicKey, + source: PublicKey, + destination: PublicKey, + authority: any, + multiSigners: Array, + amount: number | u64, + ): TransactionInstruction { + const dataLayout = BufferLayout.struct([ + BufferLayout.u8('instruction'), + Layout.uint64('amount'), + ]); + + const data = Buffer.alloc(dataLayout.span); + dataLayout.encode( + { + instruction: 3, // Transfer instruction + amount: new u64(amount).toBuffer(), + }, + data, + ); + + let keys = [ + {pubkey: source, isSigner: false, isWritable: true}, + {pubkey: destination, isSigner: false, isWritable: true}, + ]; + if (isAccount(authority)) { + keys.push({ + pubkey: authority.publicKey, + isSigner: true, + isWritable: false, + }); + } else { + keys.push({pubkey: authority, isSigner: false, isWritable: false}); + multiSigners.forEach(signer => + keys.push({ + pubkey: signer.publicKey, + isSigner: true, + isWritable: false, + }), + ); + } + return new TransactionInstruction({ + keys, + programId: programId, + data, + }); + } + + /** + * Construct an Approve instruction + * + * @param account Public key of the account + * @param delegate Account authorized to perform a transfer of tokens from the source account + * @param owner Owner of the source account + * @param multiSigners Signing accounts if `owner` is a multiSig + * @param amount Maximum number of tokens the delegate may transfer + */ + static createApproveInstruction( + programId: PublicKey, + account: PublicKey, + delegate: PublicKey, + owner: any, + multiSigners: Array, + amount: number | u64, + ): TransactionInstruction { + const dataLayout = BufferLayout.struct([ + BufferLayout.u8('instruction'), + Layout.uint64('amount'), + ]); + + const data = Buffer.alloc(dataLayout.span); + dataLayout.encode( + { + instruction: 4, // Approve instruction + amount: new u64(amount).toBuffer(), + }, + data, + ); + + let keys = [ + {pubkey: account, isSigner: false, isWritable: true}, + {pubkey: delegate, isSigner: false, isWritable: false}, + ]; + if (isAccount(owner)) { + keys.push({pubkey: owner.publicKey, isSigner: true, isWritable: false}); + } else { + keys.push({pubkey: owner, isSigner: false, isWritable: false}); + multiSigners.forEach(signer => + keys.push({ + pubkey: signer.publicKey, + isSigner: true, + isWritable: false, + }), + ); + } + + return new TransactionInstruction({ + keys, + programId: programId, + data, + }); + } + + /** + * Construct an Approve instruction + * + * @param account Public key of the account + * @param delegate Account authorized to perform a transfer of tokens from the source account + * @param owner Owner of the source account + * @param multiSigners Signing accounts if `owner` is a multiSig + * @param amount Maximum number of tokens the delegate may transfer + */ + static createRevokeInstruction( + programId: PublicKey, + account: PublicKey, + owner: any, + multiSigners: Array, + ): TransactionInstruction { + const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]); + + const data = Buffer.alloc(dataLayout.span); + dataLayout.encode( + { + instruction: 5, // Approve instruction + }, + data, + ); + + let keys = [{pubkey: account, isSigner: false, isWritable: true}]; + if (isAccount(owner)) { + keys.push({pubkey: owner.publicKey, isSigner: true, isWritable: false}); + } else { + keys.push({pubkey: owner, isSigner: false, isWritable: false}); + multiSigners.forEach(signer => + keys.push({ + pubkey: signer.publicKey, + isSigner: true, + isWritable: false, + }), + ); + } + + return new TransactionInstruction({ + keys, + programId: programId, + data, + }); + } + + /** + * Construct a SetOwner instruction + * + * @param account Public key of the account + * @param newOwner New owner of the account + * @param owner Owner of the account + * @param multiSigners Signing accounts if `owner` is a multiSig + */ + static createSetOwnerInstruction( + programId: PublicKey, + owned: PublicKey, + newOwner: PublicKey, + owner: any, + multiSigners: Array, + ): TransactionInstruction { + const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]); + + const data = Buffer.alloc(dataLayout.span); + dataLayout.encode( + { + instruction: 6, // SetOwner instruction + }, + data, + ); + + let keys = [ + {pubkey: owned, isSigner: false, isWritable: true}, + {pubkey: newOwner, isSigner: false, isWritable: false}, + ]; + if (isAccount(owner)) { + keys.push({pubkey: owner.publicKey, isSigner: true, isWritable: false}); + } else { + keys.push({pubkey: owner, isSigner: false, isWritable: false}); + multiSigners.forEach(signer => + keys.push({ + pubkey: signer.publicKey, + isSigner: true, + isWritable: false, + }), + ); + } + + return new TransactionInstruction({ + keys, + programId: programId, + data, + }); + } + + /** + * Construct a MintTo instruction + * + * @param dest Public key of the account to mint to + * @param authority Owner of the mint + * @param multiSigners Signing accounts if `authority` is a multiSig + + * @param amount amount to mint + */ + static createMintToInstruction( + programId: PublicKey, + mint: PublicKey, + dest: PublicKey, + authority: any, + multiSigners: Array, + amount: number, + ): TransactionInstruction { + const dataLayout = BufferLayout.struct([ + BufferLayout.u8('instruction'), + Layout.uint64('amount'), + ]); + + const data = Buffer.alloc(dataLayout.span); + dataLayout.encode( + { + instruction: 7, // MintTo instruction + amount: new u64(amount).toBuffer(), + }, + data, + ); + + let keys = [ + {pubkey: mint, isSigner: false, isWritable: true}, + {pubkey: dest, isSigner: false, isWritable: true}, + ]; + if (isAccount(authority)) { + keys.push({ + pubkey: authority.publicKey, + isSigner: true, + isWritable: false, + }); + } else { + keys.push({pubkey: authority, isSigner: false, isWritable: false}); + multiSigners.forEach(signer => + keys.push({ + pubkey: signer.publicKey, + isSigner: true, + isWritable: false, + }), + ); + } + + return new TransactionInstruction({ + keys, + programId: programId, + data, + }); + } + + /** + * Construct a Burn instruction + * + * @param account Account to burn tokens from + * @param authority Public key account owner + * @param multiSigners Signing accounts if `authority` is a multiSig + * @param amount ammount to burn + */ + static createBurnInstruction( + programId: PublicKey, + account: PublicKey, + authority: any, + multiSigners: Array, + amount: number, + ): TransactionInstruction { + const dataLayout = BufferLayout.struct([ + BufferLayout.u8('instruction'), + Layout.uint64('amount'), + ]); + + const data = Buffer.alloc(dataLayout.span); + dataLayout.encode( + { + instruction: 8, // Burn instruction + amount: new u64(amount).toBuffer(), + }, + data, + ); + + let keys = [{pubkey: account, isSigner: false, isWritable: true}]; + if (isAccount(authority)) { + keys.push({ + pubkey: authority.publicKey, + isSigner: true, + isWritable: false, + }); + } else { + keys.push({pubkey: authority, isSigner: false, isWritable: false}); + multiSigners.forEach(signer => + keys.push({ + pubkey: signer.publicKey, + isSigner: true, + isWritable: false, + }), + ); + } + + return new TransactionInstruction({ + keys, + programId: programId, + data, + }); + } + + /** + * Construct a Burn instruction + * + * @param account Account to burn tokens from + * @param owner account owner + * @param multiSigners Signing accounts if `owner` is a multiSig + */ + static createCloseAccountInstruction( + programId: PublicKey, + account: PublicKey, + dest: PublicKey, + owner: any, + multiSigners: Array, + ): TransactionInstruction { + const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]); + const data = Buffer.alloc(dataLayout.span); + dataLayout.encode( + { + instruction: 9, // CloseAccount instruction + }, + data, + ); + + let keys = [ + {pubkey: account, isSigner: false, isWritable: true}, + {pubkey: dest, isSigner: false, isWritable: true}, + ]; + if (isAccount(owner)) { + keys.push({pubkey: owner.publicKey, isSigner: true, isWritable: false}); + } else { + keys.push({pubkey: owner, isSigner: false, isWritable: false}); + multiSigners.forEach(signer => + keys.push({ + pubkey: signer.publicKey, + isSigner: true, + isWritable: false, + }), + ); + } + + return new TransactionInstruction({ + keys, + programId: programId, + data, + }); + } +} diff --git a/program/js/client/util/new-account-with-lamports.js b/program/js/client/util/new-account-with-lamports.js new file mode 100644 index 0000000..423972f --- /dev/null +++ b/program/js/client/util/new-account-with-lamports.js @@ -0,0 +1,25 @@ +// @flow + +import {Account, Connection} from '@solana/web3.js'; + +import {sleep} from './sleep'; + +export async function newAccountWithLamports( + connection: Connection, + lamports: number = 1000000, +): Promise { + const account = new Account(); + + let retries = 30; + await connection.requestAirdrop(account.publicKey, lamports); + for (;;) { + await sleep(500); + if (lamports == (await connection.getBalance(account.publicKey))) { + return account; + } + if (--retries <= 0) { + break; + } + } + throw new Error(`Airdrop of ${lamports} failed`); +} diff --git a/program/js/client/util/new-system-account-with-airdrop.js b/program/js/client/util/new-system-account-with-airdrop.js new file mode 100644 index 0000000..2df5779 --- /dev/null +++ b/program/js/client/util/new-system-account-with-airdrop.js @@ -0,0 +1,17 @@ +// @flow + +import {Account, Connection} from '@solana/web3.js'; + +/** + * Create a new system account and airdrop it some lamports + * + * @private + */ +export async function newSystemAccountWithAirdrop( + connection: Connection, + lamports: number = 1, +): Promise { + const account = new Account(); + await connection.requestAirdrop(account.publicKey, lamports); + return account; +} diff --git a/program/js/client/util/send-and-confirm-transaction.js b/program/js/client/util/send-and-confirm-transaction.js new file mode 100644 index 0000000..1088c55 --- /dev/null +++ b/program/js/client/util/send-and-confirm-transaction.js @@ -0,0 +1,21 @@ +// @flow + +import {sendAndConfirmTransaction as realSendAndConfirmTransaction} from '@solana/web3.js'; +import type { + Account, + Connection, + Transaction, + TransactionSignature, +} from '@solana/web3.js'; + +export function sendAndConfirmTransaction( + title: string, + connection: Connection, + transaction: Transaction, + ...signers: Array +): Promise { + return realSendAndConfirmTransaction(connection, transaction, signers, { + confirmations: 1, + skipPreflight: true, + }); +} diff --git a/program/js/client/util/sleep.js b/program/js/client/util/sleep.js new file mode 100644 index 0000000..961d48c --- /dev/null +++ b/program/js/client/util/sleep.js @@ -0,0 +1,6 @@ +// @flow + +// zzz +export function sleep(ms: number): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); +} diff --git a/program/js/client/util/store.js b/program/js/client/util/store.js new file mode 100644 index 0000000..fdafb9d --- /dev/null +++ b/program/js/client/util/store.js @@ -0,0 +1,26 @@ +/** + * Simple file-based datastore + * + * @flow + */ + +import path from 'path'; +import fs from 'mz/fs'; +import mkdirp from 'mkdirp-promise'; + +export class Store { + dir = path.join(__dirname, 'store'); + + async load(uri: string): Promise { + const filename = path.join(this.dir, uri); + const data = await fs.readFile(filename, 'utf8'); + const config = JSON.parse(data); + return config; + } + + async save(uri: string, config: Object): Promise { + await mkdirp(this.dir); + const filename = path.join(this.dir, uri); + await fs.writeFile(filename, JSON.stringify(config), 'utf8'); + } +} diff --git a/program/js/cluster-devnet.env b/program/js/cluster-devnet.env new file mode 100644 index 0000000..eccad41 --- /dev/null +++ b/program/js/cluster-devnet.env @@ -0,0 +1,2 @@ +LIVE=1 +CLUSTER=devnet diff --git a/program/js/cluster-mainnet-beta.env b/program/js/cluster-mainnet-beta.env new file mode 100644 index 0000000..17239e5 --- /dev/null +++ b/program/js/cluster-mainnet-beta.env @@ -0,0 +1,2 @@ +LIVE=1 +CLUSTER=mainnet-beta diff --git a/program/js/cluster-testnet.env b/program/js/cluster-testnet.env new file mode 100644 index 0000000..9bcfb25 --- /dev/null +++ b/program/js/cluster-testnet.env @@ -0,0 +1,2 @@ +LIVE=1 +CLUSTER=testnet diff --git a/program/js/flow-typed/bn.js.js b/program/js/flow-typed/bn.js.js new file mode 100644 index 0000000..5412142 --- /dev/null +++ b/program/js/flow-typed/bn.js.js @@ -0,0 +1,4 @@ +declare module 'bn.js' { + // TODO: Fill in types + declare module.exports: any; +} diff --git a/program/js/flow-typed/bs58.js b/program/js/flow-typed/bs58.js new file mode 100644 index 0000000..9223fad --- /dev/null +++ b/program/js/flow-typed/bs58.js @@ -0,0 +1,6 @@ +declare module 'bs58' { + declare module.exports: { + encode(input: Buffer): string; + decode(input: string): Buffer; + }; +} diff --git a/program/js/flow-typed/buffer-layout.js b/program/js/flow-typed/buffer-layout.js new file mode 100644 index 0000000..9dd66fe --- /dev/null +++ b/program/js/flow-typed/buffer-layout.js @@ -0,0 +1,4 @@ +declare module 'buffer-layout' { + // TODO: Fill in types + declare module.exports: any; +} diff --git a/program/js/flow-typed/mkdirp-promise_vx.x.x.js b/program/js/flow-typed/mkdirp-promise_vx.x.x.js new file mode 100644 index 0000000..35bb720 --- /dev/null +++ b/program/js/flow-typed/mkdirp-promise_vx.x.x.js @@ -0,0 +1,32 @@ +// flow-typed signature: 65e18196703cbb222ea294226e99826d +// flow-typed version: <>/mkdirp-promise_v5.0.1/flow_v0.84.0 + +/** + * This is an autogenerated libdef stub for: + * + * 'mkdirp-promise' + * + * Fill this stub out by replacing all the `any` types. + * + * Once filled out, we encourage you to share your work with the + * community by sending a pull request to: + * https://github.com/flowtype/flow-typed + */ + +declare module 'mkdirp-promise' { + declare module.exports: any; +} + +/** + * We include stubs for each file inside this npm package in case you need to + * require those files directly. Feel free to delete any files that aren't + * needed. + */ +declare module 'mkdirp-promise/lib/index' { + declare module.exports: any; +} + +// Filename aliases +declare module 'mkdirp-promise/lib/index.js' { + declare module.exports: $Exports<'mkdirp-promise/lib/index'>; +} diff --git a/program/js/flow-typed/npm/mz_vx.x.x.js b/program/js/flow-typed/npm/mz_vx.x.x.js new file mode 100644 index 0000000..b4b12f4 --- /dev/null +++ b/program/js/flow-typed/npm/mz_vx.x.x.js @@ -0,0 +1,73 @@ +// flow-typed signature: ed29f42bf4f4916e4f3ba1f5e7343c9d +// flow-typed version: <>/mz_v2.7.0/flow_v0.81.0 + +/** + * This is an autogenerated libdef stub for: + * + * 'mz' + * + * Fill this stub out by replacing all the `any` types. + * + * Once filled out, we encourage you to share your work with the + * community by sending a pull request to: + * https://github.com/flowtype/flow-typed + */ + +declare module 'mz' { + declare module.exports: any; +} + +/** + * We include stubs for each file inside this npm package in case you need to + * require those files directly. Feel free to delete any files that aren't + * needed. + */ +declare module 'mz/child_process' { + declare module.exports: any; +} + +declare module 'mz/crypto' { + declare module.exports: any; +} + +declare module 'mz/dns' { + declare module.exports: any; +} + +declare module 'mz/fs' { + declare module.exports: any; +} + +declare module 'mz/readline' { + declare module.exports: any; +} + +declare module 'mz/zlib' { + declare module.exports: any; +} + +// Filename aliases +declare module 'mz/child_process.js' { + declare module.exports: $Exports<'mz/child_process'>; +} +declare module 'mz/crypto.js' { + declare module.exports: $Exports<'mz/crypto'>; +} +declare module 'mz/dns.js' { + declare module.exports: $Exports<'mz/dns'>; +} +declare module 'mz/fs.js' { + declare module.exports: $Exports<'mz/fs'>; +} +declare module 'mz/index' { + declare module.exports: $Exports<'mz'>; +} +declare module 'mz/index.js' { + declare module.exports: $Exports<'mz'>; +} +declare module 'mz/readline.js' { + declare module.exports: $Exports<'mz/readline'>; +} +declare module 'mz/zlib.js' { + declare module.exports: $Exports<'mz/zlib'>; +} diff --git a/program/js/flow-typed/semver.js b/program/js/flow-typed/semver.js new file mode 100644 index 0000000..3d6c800 --- /dev/null +++ b/program/js/flow-typed/semver.js @@ -0,0 +1,3 @@ +declare module 'semver' { + declare module.exports: any; +} diff --git a/program/js/module.d.ts b/program/js/module.d.ts new file mode 100644 index 0000000..4295bad --- /dev/null +++ b/program/js/module.d.ts @@ -0,0 +1,160 @@ +declare module '@solana/spl-token' { + import {Buffer} from 'buffer'; + import { PublicKey, TransactionInstruction, TransactionSignature, Connection } from "@solana/web3.js"; + import BN from 'bn.js'; + + // === client/token.js === + export class u64 extends BN { + toBuffer(): Buffer; + static fromBuffer(buffer: Buffer): u64; + } + export type MintInfo = { + owner: null | PublicKey, + decimals: number, + initialized: boolean, + }; + export type AccountInfo = { + mint: PublicKey, + owner: PublicKey, + amount: u64, + delegate: null | PublicKey, + delegatedAmount: u64, + isInitialized: boolean, + isNative: boolean, + }; + export type MultisigInfo = { + m: number, + n: number, + initialized: boolean, + signer1: PublicKey, + signer2: PublicKey, + signer3: PublicKey, + signer4: PublicKey, + signer5: PublicKey, + signer6: PublicKey, + signer7: PublicKey, + signer8: PublicKey, + signer9: PublicKey, + signer10: PublicKey, + signer11: PublicKey, + }; + export type TokenAndPublicKey = [Token, PublicKey]; + export class Token { + constructor( + connection: Connection, + publicKey: PublicKey, + programId: PublicKey, + payer: Account, + ); + static createMint( + connection: Connection, + payer: Account, + mintOwner: PublicKey, + accountOwner: PublicKey, + supply: u64, + decimals: number, + programId: PublicKey, + is_owned: boolean, + ): Promise; + static getAccount(connection: Connection): Promise; + createAccount(owner: PublicKey): Promise; + createMultisig(m: number, signers: Array): Promise; + getMintInfo(): Promise; + getAccountInfo(account: PublicKey): Promise; + getMultisigInfo(multisig: PublicKey): Promise; + transfer( + source: PublicKey, + destination: PublicKey, + authority: Account | PublicKey, + multiSigners: Array, + amount: number | u64, + ): Promise; + approve( + account: PublicKey, + delegate: PublicKey, + owner: Account | PublicKey, + multiSigners: Array, + amount: number | u64, + ): Promise; + revoke( + account: PublicKey, + owner: Account | PublicKey, + multiSigners: Array, + ): Promise; + setOwner( + owned: PublicKey, + newOwner: PublicKey, + owner: Account | PublicKey, + multiSigners: Array, + ): Promise; + mintTo( + dest: PublicKey, + authority: Account | PublicKey, + multiSigners: Array, + amount: number, + ): Promise; + burn( + account: PublicKey, + authority: Account | PublicKey, + multiSigners: Array, + amount: number, + ): Promise; + closeAccount( + account: PublicKey, + dest: PublicKey, + owner: Account | PublicKey, + multiSigners: Array, + ): Promise; + static createTransferInstruction( + programId: PublicKey, + source: PublicKey, + destination: PublicKey, + authority: Account | PublicKey, + multiSigners: Array, + amount: number | u64, + ): TransactionInstruction; + static createApproveInstruction( + programId: PublicKey, + account: PublicKey, + delegate: PublicKey, + owner: Account | PublicKey, + multiSigners: Array, + amount: number | u64, + ): TransactionInstruction; + static createRevokeInstruction( + programId: PublicKey, + account: PublicKey, + owner: Account | PublicKey, + multiSigners: Array, + ): TransactionInstruction; + static createSetOwnerInstruction( + programId: PublicKey, + owned: PublicKey, + newOwner: PublicKey, + owner: Account | PublicKey, + multiSigners: Array, + ): TransactionInstruction; + static createMintToInstruction( + programId: PublicKey, + mint: PublicKey, + dest: PublicKey, + authority: Account | PublicKey, + multiSigners: Array, + amount: number, + ): TransactionInstruction; + static createBurnInstruction( + programId: PublicKey, + account: PublicKey, + authority: Account | PublicKey, + multiSigners: Array, + amount: number, + ): TransactionInstruction; + static createCloseAccountInstruction( + programId: PublicKey, + account: PublicKey, + dest: PublicKey, + owner: Account | PublicKey, + multiSigners: Array, + ): TransactionInstruction; + } +} diff --git a/program/js/module.flow.js b/program/js/module.flow.js new file mode 100644 index 0000000..1d81229 --- /dev/null +++ b/program/js/module.flow.js @@ -0,0 +1,163 @@ +/** + * Flow Library definition for spl-token + * + * This file is manually maintained + * + */ + +declare module '@solana/spl-token' { + // === client/token.js === + declare export class u64 extends BN { + toBuffer(): Buffer; + static fromBuffer(buffer: Buffer): u64; + } + declare export type MintInfo = {| + owner: null | PublicKey, + decimals: number, + initialized: boolean, + |}; + declare export type AccountInfo = {| + mint: PublicKey, + owner: PublicKey, + amount: u64, + delegate: null | PublicKey, + delegatedAmount: u64, + isInitialized: boolean, + isNative: boolean, + |}; + declare export type MultisigInfo = {| + m: number, + n: number, + initialized: boolean, + signer1: PublicKey, + signer2: PublicKey, + signer3: PublicKey, + signer4: PublicKey, + signer5: PublicKey, + signer6: PublicKey, + signer7: PublicKey, + signer8: PublicKey, + signer9: PublicKey, + signer10: PublicKey, + signer11: PublicKey, + |}; + declare export type TokenAndPublicKey = [Token, PublicKey]; + declare export class Token { + constructor( + connection: Connection, + publicKey: PublicKey, + programId: PublicKey, + payer: Account, + ): Token; + static createMint( + connection: Connection, + payer: Account, + mintOwner: PublicKey, + accountOwner: PublicKey, + supply: u64, + decimals: number, + programId: PublicKey, + is_owned: boolean, + ): Promise; + static getAccount(connection: Connection): Promise; + createAccount(owner: PublicKey): Promise; + createMultisig(m: number, signers: Array): Promise; + getMintInfo(): Promise; + getAccountInfo(account: PublicKey): Promise; + getMultisigInfo(multisig: PublicKey): Promise; + transfer( + source: PublicKey, + destination: PublicKey, + authority: Account | PublicKey, + multiSigners: Array, + amount: number | u64, + ): Promise; + approve( + account: PublicKey, + delegate: PublicKey, + owner: Account | PublicKey, + multiSigners: Array, + amount: number | u64, + ): Promise; + revoke( + account: PublicKey, + owner: Account | PublicKey, + multiSigners: Array, + ): Promise; + setOwner( + owned: PublicKey, + newOwner: PublicKey, + owner: Account | PublicKey, + multiSigners: Array, + ): Promise; + mintTo( + dest: PublicKey, + authority: Account | PublicKey, + multiSigners: Array, + amount: number, + ): Promise; + burn( + account: PublicKey, + authority: Account | PublicKey, + multiSigners: Array, + amount: number, + ): Promise; + closeAccount( + account: PublicKey, + dest: PublicKey, + owner: Account | PublicKey, + multiSigners: Array, + ): Promise; + static createTransferInstruction( + programId: PublicKey, + source: PublicKey, + destination: PublicKey, + authority: Account | PublicKey, + multiSigners: Array, + amount: number | u64, + ): TransactionInstruction; + static createApproveInstruction( + programId: PublicKey, + account: PublicKey, + delegate: PublicKey, + owner: Account | PublicKey, + multiSigners: Array, + amount: number | u64, + ): TransactionInstruction; + static createRevokeInstruction( + programId: PublicKey, + account: PublicKey, + owner: Account | PublicKey, + multiSigners: Array, + ): TransactionInstruction; + static createSetOwnerInstruction( + programId: PublicKey, + owned: PublicKey, + newOwner: PublicKey, + owner: Account | PublicKey, + multiSigners: Array, + ): TransactionInstruction; + static createMintToInstruction( + programId: PublicKey, + mint: PublicKey, + dest: PublicKey, + authority: Account | PublicKey, + multiSigners: Array, + amount: number, + ): TransactionInstruction; + static createBurnInstruction( + programId: PublicKey, + account: PublicKey, + authority: Account | PublicKey, + multiSigners: Array, + amount: number, + ): TransactionInstruction; + static createCloseAccountInstruction( + programId: PublicKey, + account: PublicKey, + dest: PublicKey, + owner: Account | PublicKey, + multiSigners: Array, + ): TransactionInstructio; + } +} diff --git a/program/js/package-lock.json b/program/js/package-lock.json new file mode 100644 index 0000000..91be906 --- /dev/null +++ b/program/js/package-lock.json @@ -0,0 +1,8383 @@ +{ + "name": "@solana/spl-token", + "version": "0.0.5", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "101": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/101/-/101-1.6.3.tgz", + "integrity": "sha512-4dmQ45yY0Dx24Qxp+zAsNLlMF6tteCyfVzgbulvSyC7tCyd3V8sW76sS0tHq8NpcbXfWTKasfyfzU1Kd86oKzw==", + "requires": { + "clone": "^1.0.2", + "deep-eql": "^0.1.3", + "keypather": "^1.10.2" + } + }, + "@babel/cli": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.10.5.tgz", + "integrity": "sha512-j9H9qSf3kLdM0Ao3aGPbGZ73mEA9XazuupcS6cDGWuiyAcANoguhP0r2Lx32H5JGw4sSSoHG3x/mxVnHgvOoyA==", + "dev": true, + "requires": { + "chokidar": "^2.1.8", + "commander": "^4.0.1", + "convert-source-map": "^1.1.0", + "fs-readdir-recursive": "^1.1.0", + "glob": "^7.0.0", + "lodash": "^4.17.19", + "make-dir": "^2.1.0", + "slash": "^2.0.0", + "source-map": "^0.5.0" + }, + "dependencies": { + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "optional": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "optional": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "chokidar": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", + "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", + "dev": true, + "optional": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "optional": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "optional": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "optional": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/compat-data": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.11.0.tgz", + "integrity": "sha512-TPSvJfv73ng0pfnEOh17bYMPQbI95+nGWc71Ss4vZdRBHTDqmM9Z8ZV4rYz8Ks7sfzc95n30k6ODIq5UGnXcYQ==", + "dev": true, + "requires": { + "browserslist": "^4.12.0", + "invariant": "^2.2.4", + "semver": "^5.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@babel/core": { + "version": "7.11.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.1.tgz", + "integrity": "sha512-XqF7F6FWQdKGGWAzGELL+aCO1p+lRY5Tj5/tbT3St1G8NaH70jhhDIKknIZaDans0OQBG5wRAldROLHSt44BgQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.11.0", + "@babel/helper-module-transforms": "^7.11.0", + "@babel/helpers": "^7.10.4", + "@babel/parser": "^7.11.1", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.11.0", + "@babel/types": "^7.11.0", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.1", + "json5": "^2.1.2", + "lodash": "^4.17.19", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/generator": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz", + "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==", + "dev": true, + "requires": { + "@babel/types": "^7.11.0", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-module-transforms": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz", + "integrity": "sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-simple-access": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/template": "^7.10.4", + "@babel/types": "^7.11.0", + "lodash": "^4.17.19" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", + "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", + "dev": true, + "requires": { + "@babel/types": "^7.11.0" + } + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.11.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.1.tgz", + "integrity": "sha512-u9QMIRdKVF7hfEkb3nu2LgZDIzCQPv+yHD9Eg6ruoJLjkrQ9fFz4IBSlF/9XwoNri9+2F1IY+dYuOfZrXq8t3w==", + "dev": true + }, + "@babel/traverse": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz", + "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.11.0", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.11.0", + "@babel/parser": "^7.11.0", + "@babel/types": "^7.11.0", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, + "@babel/types": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", + "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "json5": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", + "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.5.tgz", + "integrity": "sha512-3vXxr3FEW7E7lJZiWQ3bM4+v/Vyr9C+hpolQ8BGFr9Y8Ri2tFLWTixmwKBafDujO1WVah4fhZBeU1bieKdghig==", + "dev": true, + "requires": { + "@babel/types": "^7.10.5", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", + "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", + "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz", + "integrity": "sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.10.4", + "browserslist": "^4.12.0", + "invariant": "^2.2.4", + "levenary": "^1.1.1", + "semver": "^5.5.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz", + "integrity": "sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-member-expression-to-functions": "^7.10.5", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.10.4" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz", + "integrity": "sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-regex": "^7.10.4", + "regexpu-core": "^4.7.0" + } + }, + "@babel/helper-define-map": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", + "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/types": "^7.10.5", + "lodash": "^4.17.19" + } + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.4.tgz", + "integrity": "sha512-4K71RyRQNPRrR85sr5QY4X3VwG4wtVoXZB9+L3r1Gp38DhELyHCtovqydRi7c1Ovb17eRGiQ/FD5s8JdU0Uy5A==", + "dev": true, + "requires": { + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz", + "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.5.tgz", + "integrity": "sha512-HiqJpYD5+WopCXIAbQDG0zye5XYVvcO9w/DHp5GsaGkRUaamLj2bEtu6i8rnGGprAhHM3qidCMgp71HF4endhA==", + "dev": true, + "requires": { + "@babel/types": "^7.10.5" + }, + "dependencies": { + "@babel/types": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.5.tgz", + "integrity": "sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + } + } + }, + "@babel/helper-module-imports": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", + "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + }, + "dependencies": { + "@babel/types": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.5.tgz", + "integrity": "sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + } + } + }, + "@babel/helper-module-transforms": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.10.5.tgz", + "integrity": "sha512-4P+CWMJ6/j1W915ITJaUkadLObmCRRSC234uctJfn/vHrsLNxsR8dwlcXv9ZhJWzl77awf+mWXSZEKt5t0OnlA==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-simple-access": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.5", + "lodash": "^4.17.19" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz", + "integrity": "sha512-pySBTeoUff56fL5CBU2hWm9TesA4r/rOkI9DyJLvvgz09MB9YtfIYe3iBriVaYNaPe+Alua0vBIOVOLs2buWhg==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.5.tgz", + "integrity": "sha512-wfryxy4bE1UivvQKSQDU4/X6dr+i8bctjUjj8Zyt3DQy7NtPizJXT8M52nqpNKL+nq2PW8lxk4ZqLj0fD4B4hQ==", + "dev": true + }, + "@babel/template": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", + "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/types": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.5.tgz", + "integrity": "sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + } + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", + "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + }, + "dependencies": { + "@babel/types": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.5.tgz", + "integrity": "sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + } + } + }, + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", + "dev": true + }, + "@babel/helper-regex": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.5.tgz", + "integrity": "sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==", + "dev": true, + "requires": { + "lodash": "^4.17.19" + } + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.4.tgz", + "integrity": "sha512-86Lsr6NNw3qTNl+TBcF1oRZMaVzJtbWTyTko+CQL/tvNvcGYEFKbLXDPxtW0HKk3McNOk4KzY55itGWCAGK5tg==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-wrap-function": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-replace-supers": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", + "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", + "dev": true, + "requires": { + "@babel/helper-member-expression-to-functions": "^7.10.4", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/generator": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.5.tgz", + "integrity": "sha512-3vXxr3FEW7E7lJZiWQ3bM4+v/Vyr9C+hpolQ8BGFr9Y8Ri2tFLWTixmwKBafDujO1WVah4fhZBeU1bieKdghig==", + "dev": true, + "requires": { + "@babel/types": "^7.10.5", + "jsesc": "^2.5.1", + "source-map": "^0.5.0" + } + }, + "@babel/helper-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", + "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", + "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz", + "integrity": "sha512-pySBTeoUff56fL5CBU2hWm9TesA4r/rOkI9DyJLvvgz09MB9YtfIYe3iBriVaYNaPe+Alua0vBIOVOLs2buWhg==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.5.tgz", + "integrity": "sha512-wfryxy4bE1UivvQKSQDU4/X6dr+i8bctjUjj8Zyt3DQy7NtPizJXT8M52nqpNKL+nq2PW8lxk4ZqLj0fD4B4hQ==", + "dev": true + }, + "@babel/template": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", + "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/traverse": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.10.5.tgz", + "integrity": "sha512-yc/fyv2gUjPqzTz0WHeRJH2pv7jA9kA7mBX2tXl/x5iOE81uaVPuGPtaYk7wmkx4b67mQ7NqI8rmT2pF47KYKQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.10.5", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.10.4", + "@babel/parser": "^7.10.5", + "@babel/types": "^7.10.5", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + } + }, + "@babel/types": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.5.tgz", + "integrity": "sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + } + } + }, + "@babel/helper-simple-access": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", + "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", + "dev": true, + "requires": { + "@babel/template": "^7.10.4", + "@babel/types": "^7.10.4" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.5.tgz", + "integrity": "sha512-wfryxy4bE1UivvQKSQDU4/X6dr+i8bctjUjj8Zyt3DQy7NtPizJXT8M52nqpNKL+nq2PW8lxk4ZqLj0fD4B4hQ==", + "dev": true + }, + "@babel/template": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", + "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/types": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.5.tgz", + "integrity": "sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + } + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz", + "integrity": "sha512-0XIdiQln4Elglgjbwo9wuJpL/K7AGCY26kmEt0+pRP0TAj4jjyNq1MjoRvikrTVqKcx4Gysxt4cXvVFXP/JO2Q==", + "dev": true, + "requires": { + "@babel/types": "^7.11.0" + }, + "dependencies": { + "@babel/types": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", + "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + } + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz", + "integrity": "sha512-pySBTeoUff56fL5CBU2hWm9TesA4r/rOkI9DyJLvvgz09MB9YtfIYe3iBriVaYNaPe+Alua0vBIOVOLs2buWhg==", + "dev": true, + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "@babel/helper-wrap-function": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz", + "integrity": "sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/helpers": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz", + "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==", + "dev": true, + "requires": { + "@babel/template": "^7.10.4", + "@babel/traverse": "^7.10.4", + "@babel/types": "^7.10.4" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", + "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/node": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/node/-/node-7.10.5.tgz", + "integrity": "sha512-suosS7zZ2roj+fYVCnDuVezUbRc0sdoyF0Gj/1FzWxD4ebbGiBGtL5qyqHH4NO34B5m4vWWYWgyNhSsrqS8vwA==", + "dev": true, + "requires": { + "@babel/register": "^7.10.5", + "commander": "^4.0.1", + "core-js": "^3.2.1", + "lodash": "^4.17.19", + "node-environment-flags": "^1.0.5", + "regenerator-runtime": "^0.13.4", + "resolve": "^1.13.1", + "v8flags": "^3.1.1" + }, + "dependencies": { + "commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true + }, + "core-js": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", + "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==", + "dev": true + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", + "dev": true + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "@babel/parser": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.5.tgz", + "integrity": "sha512-wfryxy4bE1UivvQKSQDU4/X6dr+i8bctjUjj8Zyt3DQy7NtPizJXT8M52nqpNKL+nq2PW8lxk4ZqLj0fD4B4hQ==", + "dev": true + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz", + "integrity": "sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-remap-async-to-generator": "^7.10.4", + "@babel/plugin-syntax-async-generators": "^7.8.0" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz", + "integrity": "sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz", + "integrity": "sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-dynamic-import": "^7.8.0" + } + }, + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.10.4.tgz", + "integrity": "sha512-aNdf0LY6/3WXkhh0Fdb6Zk9j1NMD8ovj3F6r0+3j837Pn1S1PdNtcwJ5EG9WkVPNHPxyJDaxMaAOVq4eki0qbg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz", + "integrity": "sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.0" + } + }, + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.11.0.tgz", + "integrity": "sha512-/f8p4z+Auz0Uaf+i8Ekf1iM7wUNLcViFUGiPxKeXvxTSl63B875YPiVdUDdem7hREcI0E0kSpEhS8tF5RphK7Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz", + "integrity": "sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz", + "integrity": "sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.11.0.tgz", + "integrity": "sha512-wzch41N4yztwoRw0ak+37wxwJM2oiIiy6huGCoqkvSTA9acYWcPfn9Y4aJqmFFJ70KTJUu29f3DQ43uJ9HXzEA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-transform-parameters": "^7.10.4" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz", + "integrity": "sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.11.0.tgz", + "integrity": "sha512-v9fZIu3Y8562RRwhm1BbMRxtqZNFmFA2EG+pT2diuU8PT3H6T/KXoZ54KgYisfOFZHV6PfvAiBIZ9Rcz+/JCxA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-skip-transparent-expression-wrappers": "^7.11.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.0" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz", + "integrity": "sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz", + "integrity": "sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz", + "integrity": "sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-dynamic-import": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", + "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-export-namespace-from": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", + "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.3" + } + }, + "@babel/plugin-syntax-flow": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.10.4.tgz", + "integrity": "sha512-yxQsX1dJixF4qEEdzVbst3SZQ58Nrooz8NV9Z9GL4byTE25BvJgl5lf0RECUf0fh28rZBb/RYTWn/eeKwCMrZQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz", + "integrity": "sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz", + "integrity": "sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz", + "integrity": "sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-remap-async-to-generator": "^7.10.4" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz", + "integrity": "sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.5.tgz", + "integrity": "sha512-6Ycw3hjpQti0qssQcA6AMSFDHeNJ++R6dIMnpRqUjFeBBTmTDPa8zgF90OVfTvAo11mXZTlVUViY1g8ffrURLg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz", + "integrity": "sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-define-map": "^7.10.4", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-optimise-call-expression": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.10.4", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz", + "integrity": "sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz", + "integrity": "sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz", + "integrity": "sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz", + "integrity": "sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz", + "integrity": "sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-flow-strip-types": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.10.4.tgz", + "integrity": "sha512-XTadyuqNst88UWBTdLjM+wEY7BFnY2sYtPyAidfC7M/QaZnSuIZpMvLxqGT7phAcnGyWh/XQFLKcGf04CnvxSQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-syntax-flow": "^7.10.4" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz", + "integrity": "sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz", + "integrity": "sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz", + "integrity": "sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz", + "integrity": "sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz", + "integrity": "sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.10.5", + "@babel/helper-plugin-utils": "^7.10.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz", + "integrity": "sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-simple-access": "^7.10.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz", + "integrity": "sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.10.4", + "@babel/helper-module-transforms": "^7.10.5", + "@babel/helper-plugin-utils": "^7.10.4", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz", + "integrity": "sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz", + "integrity": "sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.4" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz", + "integrity": "sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz", + "integrity": "sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-replace-supers": "^7.10.4" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz", + "integrity": "sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz", + "integrity": "sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz", + "integrity": "sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw==", + "dev": true, + "requires": { + "regenerator-transform": "^0.14.2" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz", + "integrity": "sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.11.0.tgz", + "integrity": "sha512-LFEsP+t3wkYBlis8w6/kmnd6Kb1dxTd+wGJ8MlxTGzQo//ehtqlVL4S9DNUa53+dtPSQobN2CXx4d81FqC58cw==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "resolve": "^1.8.1", + "semver": "^5.5.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz", + "integrity": "sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz", + "integrity": "sha512-UwQYGOqIdQJe4aWNyS7noqAnN2VbaczPLiEtln+zPowRNlD+79w3oi2TWfYe0eZgd+gjZCbsydN7lzWysDt+gw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-skip-transparent-expression-wrappers": "^7.11.0" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz", + "integrity": "sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/helper-regex": "^7.10.4" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz", + "integrity": "sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz", + "integrity": "sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz", + "integrity": "sha512-y5XJ9waMti2J+e7ij20e+aH+fho7Wb7W8rNuu72aKRwCHFqQdhkdU2lo3uZ9tQuboEJcUFayXdARhcxLQ3+6Fg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz", + "integrity": "sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/preset-env": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.0.tgz", + "integrity": "sha512-2u1/k7rG/gTh02dylX2kL3S0IJNF+J6bfDSp4DI2Ma8QN6Y9x9pmAax59fsCk6QUQG0yqH47yJWA+u1I1LccAg==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.11.0", + "@babel/helper-compilation-targets": "^7.10.4", + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-proposal-async-generator-functions": "^7.10.4", + "@babel/plugin-proposal-class-properties": "^7.10.4", + "@babel/plugin-proposal-dynamic-import": "^7.10.4", + "@babel/plugin-proposal-export-namespace-from": "^7.10.4", + "@babel/plugin-proposal-json-strings": "^7.10.4", + "@babel/plugin-proposal-logical-assignment-operators": "^7.11.0", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4", + "@babel/plugin-proposal-numeric-separator": "^7.10.4", + "@babel/plugin-proposal-object-rest-spread": "^7.11.0", + "@babel/plugin-proposal-optional-catch-binding": "^7.10.4", + "@babel/plugin-proposal-optional-chaining": "^7.11.0", + "@babel/plugin-proposal-private-methods": "^7.10.4", + "@babel/plugin-proposal-unicode-property-regex": "^7.10.4", + "@babel/plugin-syntax-async-generators": "^7.8.0", + "@babel/plugin-syntax-class-properties": "^7.10.4", + "@babel/plugin-syntax-dynamic-import": "^7.8.0", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.0", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.0", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", + "@babel/plugin-syntax-optional-chaining": "^7.8.0", + "@babel/plugin-syntax-top-level-await": "^7.10.4", + "@babel/plugin-transform-arrow-functions": "^7.10.4", + "@babel/plugin-transform-async-to-generator": "^7.10.4", + "@babel/plugin-transform-block-scoped-functions": "^7.10.4", + "@babel/plugin-transform-block-scoping": "^7.10.4", + "@babel/plugin-transform-classes": "^7.10.4", + "@babel/plugin-transform-computed-properties": "^7.10.4", + "@babel/plugin-transform-destructuring": "^7.10.4", + "@babel/plugin-transform-dotall-regex": "^7.10.4", + "@babel/plugin-transform-duplicate-keys": "^7.10.4", + "@babel/plugin-transform-exponentiation-operator": "^7.10.4", + "@babel/plugin-transform-for-of": "^7.10.4", + "@babel/plugin-transform-function-name": "^7.10.4", + "@babel/plugin-transform-literals": "^7.10.4", + "@babel/plugin-transform-member-expression-literals": "^7.10.4", + "@babel/plugin-transform-modules-amd": "^7.10.4", + "@babel/plugin-transform-modules-commonjs": "^7.10.4", + "@babel/plugin-transform-modules-systemjs": "^7.10.4", + "@babel/plugin-transform-modules-umd": "^7.10.4", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.10.4", + "@babel/plugin-transform-new-target": "^7.10.4", + "@babel/plugin-transform-object-super": "^7.10.4", + "@babel/plugin-transform-parameters": "^7.10.4", + "@babel/plugin-transform-property-literals": "^7.10.4", + "@babel/plugin-transform-regenerator": "^7.10.4", + "@babel/plugin-transform-reserved-words": "^7.10.4", + "@babel/plugin-transform-shorthand-properties": "^7.10.4", + "@babel/plugin-transform-spread": "^7.11.0", + "@babel/plugin-transform-sticky-regex": "^7.10.4", + "@babel/plugin-transform-template-literals": "^7.10.4", + "@babel/plugin-transform-typeof-symbol": "^7.10.4", + "@babel/plugin-transform-unicode-escapes": "^7.10.4", + "@babel/plugin-transform-unicode-regex": "^7.10.4", + "@babel/preset-modules": "^0.1.3", + "@babel/types": "^7.11.0", + "browserslist": "^4.12.0", + "core-js-compat": "^3.6.2", + "invariant": "^2.2.2", + "levenary": "^1.1.1", + "semver": "^5.5.0" + }, + "dependencies": { + "@babel/types": { + "version": "7.11.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", + "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "@babel/preset-flow": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.10.4.tgz", + "integrity": "sha512-XI6l1CptQCOBv+ZKYwynyswhtOKwpZZp5n0LG1QKCo8erRhqjoQV6nvx61Eg30JHpysWQSBwA2AWRU3pBbSY5g==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4", + "@babel/plugin-transform-flow-strip-types": "^7.10.4" + } + }, + "@babel/preset-modules": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz", + "integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", + "@babel/plugin-transform-dotall-regex": "^7.4.4", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + } + }, + "@babel/register": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.10.5.tgz", + "integrity": "sha512-eYHdLv43nyvmPn9bfNfrcC4+iYNwdQ8Pxk1MFJuU/U5LpSYl/PH4dFMazCYZDFVi8ueG3shvO+AQfLrxpYulQw==", + "dev": true, + "requires": { + "find-cache-dir": "^2.0.0", + "lodash": "^4.17.19", + "make-dir": "^2.1.0", + "pirates": "^4.0.0", + "source-map-support": "^0.5.16" + } + }, + "@babel/runtime": { + "version": "7.11.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz", + "integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==", + "requires": { + "regenerator-runtime": "^0.13.4" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + } + } + }, + "@babel/template": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", + "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/parser": "^7.10.4", + "@babel/types": "^7.10.4" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + } + } + }, + "@babel/traverse": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.10.5.tgz", + "integrity": "sha512-yc/fyv2gUjPqzTz0WHeRJH2pv7jA9kA7mBX2tXl/x5iOE81uaVPuGPtaYk7wmkx4b67mQ7NqI8rmT2pF47KYKQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/generator": "^7.10.5", + "@babel/helper-function-name": "^7.10.4", + "@babel/helper-split-export-declaration": "^7.10.4", + "@babel/parser": "^7.10.5", + "@babel/types": "^7.10.5", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.19" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.10.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.5.tgz", + "integrity": "sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", + "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.3", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", + "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", + "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.3", + "fastq": "^1.6.0" + } + }, + "@octokit/auth-token": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.2.tgz", + "integrity": "sha512-jE/lE/IKIz2v1+/P0u4fJqv0kYwXOTujKemJMFr6FeopsxlIK3+wKDCJGnysg81XID5TgZQbIfuJ5J0lnTiuyQ==", + "dev": true, + "requires": { + "@octokit/types": "^5.0.0" + } + }, + "@octokit/endpoint": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.5.tgz", + "integrity": "sha512-70K5u6zd45ItOny6aHQAsea8HHQjlQq85yqOMe+Aj8dkhN2qSJ9T+Q3YjUjEYfPRBcuUWNgMn62DQnP/4LAIiQ==", + "dev": true, + "requires": { + "@octokit/types": "^5.0.0", + "is-plain-object": "^4.0.0", + "universal-user-agent": "^6.0.0" + }, + "dependencies": { + "is-plain-object": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-4.1.1.tgz", + "integrity": "sha512-5Aw8LLVsDlZsETVMhoMXzqsXwQqr/0vlnBYzIXJbYo2F4yYlhLHs+Ez7Bod7IIQKWkJbJfxrWD7pA1Dw1TKrwA==", + "dev": true + }, + "universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", + "dev": true + } + } + }, + "@octokit/plugin-paginate-rest": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-1.1.2.tgz", + "integrity": "sha512-jbsSoi5Q1pj63sC16XIUboklNw+8tL9VOnJsWycWYR78TKss5PVpIPb1TUUcMQ+bBh7cY579cVAWmf5qG+dw+Q==", + "dev": true, + "requires": { + "@octokit/types": "^2.0.1" + }, + "dependencies": { + "@octokit/types": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.16.2.tgz", + "integrity": "sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q==", + "dev": true, + "requires": { + "@types/node": ">= 8" + } + } + } + }, + "@octokit/plugin-request-log": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz", + "integrity": "sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw==", + "dev": true + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-2.4.0.tgz", + "integrity": "sha512-EZi/AWhtkdfAYi01obpX0DF7U6b1VRr30QNQ5xSFPITMdLSfhcBqjamE3F+sKcxPbD7eZuMHu3Qkk2V+JGxBDQ==", + "dev": true, + "requires": { + "@octokit/types": "^2.0.1", + "deprecation": "^2.3.1" + }, + "dependencies": { + "@octokit/types": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.16.2.tgz", + "integrity": "sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q==", + "dev": true, + "requires": { + "@types/node": ">= 8" + } + } + } + }, + "@octokit/request": { + "version": "5.4.7", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.7.tgz", + "integrity": "sha512-FN22xUDP0i0uF38YMbOfx6TotpcENP5W8yJM1e/LieGXn6IoRxDMnBf7tx5RKSW4xuUZ/1P04NFZy5iY3Rax1A==", + "dev": true, + "requires": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.0.0", + "@octokit/types": "^5.0.0", + "deprecation": "^2.0.0", + "is-plain-object": "^4.0.0", + "node-fetch": "^2.3.0", + "once": "^1.4.0", + "universal-user-agent": "^6.0.0" + }, + "dependencies": { + "@octokit/request-error": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.2.tgz", + "integrity": "sha512-2BrmnvVSV1MXQvEkrb9zwzP0wXFNbPJij922kYBTLIlIafukrGOb+ABBT2+c6wZiuyWDH1K1zmjGQ0toN/wMWw==", + "dev": true, + "requires": { + "@octokit/types": "^5.0.1", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "is-plain-object": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-4.1.1.tgz", + "integrity": "sha512-5Aw8LLVsDlZsETVMhoMXzqsXwQqr/0vlnBYzIXJbYo2F4yYlhLHs+Ez7Bod7IIQKWkJbJfxrWD7pA1Dw1TKrwA==", + "dev": true + }, + "universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", + "dev": true + } + } + }, + "@octokit/request-error": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.2.1.tgz", + "integrity": "sha512-+6yDyk1EES6WK+l3viRDElw96MvwfJxCt45GvmjDUKWjYIb3PJZQkq3i46TwGwoPD4h8NmTrENmtyA1FwbmhRA==", + "dev": true, + "requires": { + "@octokit/types": "^2.0.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "dependencies": { + "@octokit/types": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.16.2.tgz", + "integrity": "sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q==", + "dev": true, + "requires": { + "@types/node": ">= 8" + } + } + } + }, + "@octokit/rest": { + "version": "16.43.2", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.43.2.tgz", + "integrity": "sha512-ngDBevLbBTFfrHZeiS7SAMAZ6ssuVmXuya+F/7RaVvlysgGa1JKJkKWY+jV6TCJYcW0OALfJ7nTIGXcBXzycfQ==", + "dev": true, + "requires": { + "@octokit/auth-token": "^2.4.0", + "@octokit/plugin-paginate-rest": "^1.1.1", + "@octokit/plugin-request-log": "^1.0.0", + "@octokit/plugin-rest-endpoint-methods": "2.4.0", + "@octokit/request": "^5.2.0", + "@octokit/request-error": "^1.0.2", + "atob-lite": "^2.0.0", + "before-after-hook": "^2.0.0", + "btoa-lite": "^1.0.0", + "deprecation": "^2.0.0", + "lodash.get": "^4.4.2", + "lodash.set": "^4.3.2", + "lodash.uniq": "^4.5.0", + "octokit-pagination-methods": "^1.1.0", + "once": "^1.4.0", + "universal-user-agent": "^4.0.0" + } + }, + "@octokit/types": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-5.2.0.tgz", + "integrity": "sha512-XjOk9y4m8xTLIKPe1NFxNWBdzA2/z3PFFA/bwf4EoH6oS8hM0Y46mEa4Cb+KCyj/tFDznJFahzQ0Aj3o1FYq4A==", + "dev": true, + "requires": { + "@types/node": ">= 8" + } + }, + "@rollup/plugin-babel": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.1.0.tgz", + "integrity": "sha512-zXBEYmfiLAMvB+ZBa6m/q9hsQYAq1sUFdjuP1F6C2pf6uQcpHwAWQveZgzS63zXdKPUYHD3Dr7BhjCqcr0bbLw==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.7.4", + "@rollup/pluginutils": "^3.0.8" + } + }, + "@rollup/plugin-commonjs": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-14.0.0.tgz", + "integrity": "sha512-+PSmD9ePwTAeU106i9FRdc+Zb3XUWyW26mo5Atr2mk82hor8+nPwkztEjFo8/B1fJKfaQDg9aM2bzQkjhi7zOw==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.0.8", + "commondir": "^1.0.1", + "estree-walker": "^1.0.1", + "glob": "^7.1.2", + "is-reference": "^1.1.2", + "magic-string": "^0.25.2", + "resolve": "^1.11.0" + } + }, + "@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "requires": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + } + }, + "@sindresorhus/is": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.1.tgz", + "integrity": "sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg==", + "dev": true + }, + "@solana/web3.js": { + "version": "0.64.0", + "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-0.64.0.tgz", + "integrity": "sha512-DlNzAXgNdk7k4Pt6CfcaAutaiXJiog9hxswtzItf0q/0/Um8JvDI1YjnMONE3IKI/jyjmTaxhsQHWAQE42KofQ==", + "requires": { + "@babel/runtime": "^7.3.1", + "bn.js": "^5.0.0", + "bs58": "^4.0.1", + "buffer": "^5.4.3", + "buffer-layout": "^1.2.0", + "crypto-hash": "^1.2.2", + "esdoc-inject-style-plugin": "^1.0.0", + "jayson": "^3.0.1", + "mz": "^2.7.0", + "node-fetch": "^2.2.0", + "npm-run-all": "^4.1.5", + "rpc-websockets": "^5.0.8", + "superstruct": "^0.8.3", + "tweetnacl": "^1.0.0", + "ws": "^7.0.0" + } + }, + "@szmarczak/http-timer": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", + "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", + "dev": true, + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "@types/cacheable-request": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", + "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", + "dev": true, + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "*", + "@types/node": "*", + "@types/responselike": "*" + } + }, + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", + "dev": true + }, + "@types/connect": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", + "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", + "requires": { + "@types/node": "*" + } + }, + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "@types/express-serve-static-core": { + "version": "4.17.9", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.9.tgz", + "integrity": "sha512-DG0BYg6yO+ePW+XoDENYz8zhNGC3jDDEpComMYn7WJc4mY1Us8Rw9ax2YhJXxpyk2SF47PQAoQ0YyVT1a0bEkA==", + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/fs-extra": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.1.tgz", + "integrity": "sha512-TcUlBem321DFQzBNuz8p0CLLKp0VvF/XH9E4KHNmgwyp4E3AfgI5cjiIVZWlbfThBop2qxFIh4+LeY6hVWWZ2w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/http-cache-semantics": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", + "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "@types/keyv": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", + "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/lodash": { + "version": "4.14.158", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.158.tgz", + "integrity": "sha512-InCEXJNTv/59yO4VSfuvNrZHt7eeNtWQEgnieIA+mIC+MOWM9arOWG2eQ8Vhk6NbOre6/BidiXhkZYeDY9U35w==" + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "@types/node": { + "version": "10.14.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.6.tgz", + "integrity": "sha512-Fvm24+u85lGmV4hT5G++aht2C5I4Z4dYlWZIh62FAfFO/TfzXtPpoLI6I7AuBWkIFqZCnhFOoTT7RjjaIL5Fjg==" + }, + "@types/qs": { + "version": "6.9.4", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.4.tgz", + "integrity": "sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ==" + }, + "@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" + }, + "@types/responselike": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", + "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "requires": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + } + }, + "acorn": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", + "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", + "dev": true + }, + "acorn-jsx": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", + "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", + "dev": true + }, + "ajv": { + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true, + "optional": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true, + "optional": true + }, + "array-includes": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", + "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0", + "is-string": "^1.0.5" + } + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "array.prototype.flat": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", + "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "assert-args": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/assert-args/-/assert-args-1.2.1.tgz", + "integrity": "sha1-QEEDoUUqMv53iYgR5U5ZCoqTc70=", + "requires": { + "101": "^1.2.0", + "compound-subject": "0.0.1", + "debug": "^2.2.0", + "get-prototype-of": "0.0.0", + "is-capitalized": "^1.0.0", + "is-class": "0.0.4" + } + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true, + "optional": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "async-each": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", + "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", + "dev": true, + "optional": true + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true, + "optional": true + }, + "atob-lite": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz", + "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=", + "dev": true + }, + "babel-eslint": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", + "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.7.0", + "@babel/traverse": "^7.7.0", + "@babel/types": "^7.7.0", + "eslint-visitor-keys": "^1.0.0", + "resolve": "^1.12.0" + }, + "dependencies": { + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "babel-plugin-dynamic-import-node": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", + "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", + "dev": true, + "requires": { + "object.assign": "^4.1.0" + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "optional": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "optional": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "optional": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "base-x": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.8.tgz", + "integrity": "sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + }, + "before-after-hook": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz", + "integrity": "sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==", + "dev": true + }, + "big-integer": { + "version": "1.6.48", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", + "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==", + "dev": true + }, + "binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", + "dev": true, + "requires": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + } + }, + "binary-extensions": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", + "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", + "dev": true, + "optional": true + }, + "bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=", + "dev": true + }, + "bn.js": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.2.tgz", + "integrity": "sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA==" + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "optional": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true, + "optional": true + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "browserslist": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.13.0.tgz", + "integrity": "sha512-MINatJ5ZNrLnQ6blGvePd/QOz9Xtu+Ne+x29iQSCHfkU5BugKVJwZKn/iiL8UbpIpa3JhviKjz+XxMo0m2caFQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001093", + "electron-to-chromium": "^1.3.488", + "escalade": "^3.0.1", + "node-releases": "^1.1.58" + } + }, + "bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", + "requires": { + "base-x": "^3.0.2" + } + }, + "btoa-lite": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", + "integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc=", + "dev": true + }, + "buffer": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "buffer-indexof-polyfill": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.1.tgz", + "integrity": "sha1-qfuAbOgUXVQoUQznLyeLs2OmOL8=", + "dev": true + }, + "buffer-layout": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-layout/-/buffer-layout-1.2.0.tgz", + "integrity": "sha512-iiyRoho/ERzBUv6kFvfsrLNgTlVwOkqQcSQN7WrO3Y+c5SeuEhCn6+y1KwhM0V3ndptF5mI/RI44zkw0qcR5Jg==" + }, + "buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=", + "dev": true + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "optional": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "cacheable-lookup": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-2.0.1.tgz", + "integrity": "sha512-EMMbsiOTcdngM/K6gV/OxF2x0t07+vMOWxZNSCRQMjO2MY2nhZQ6OYhOOpyQrbhqsgtvKGI7hcq6xjnA92USjg==", + "dev": true, + "requires": { + "@types/keyv": "^3.1.1", + "keyv": "^4.0.0" + } + }, + "cacheable-request": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", + "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^2.0.0" + }, + "dependencies": { + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + } + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "caniuse-lite": { + "version": "1.0.30001109", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001109.tgz", + "integrity": "sha512-4JIXRodHzdS3HdK8nSgIqXYLExOvG+D2/EenSvcub2Kp3QEADjo2v2oUn5g0n0D+UNwG9BtwKOyGcSq2qvQXvQ==", + "dev": true + }, + "chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", + "dev": true, + "requires": { + "traverse": ">=0.3.0 <0.4" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=", + "dev": true + }, + "cheerio": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", + "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=", + "requires": { + "css-select": "~1.2.0", + "dom-serializer": "~0.1.0", + "entities": "~1.1.1", + "htmlparser2": "^3.9.1", + "lodash.assignin": "^4.0.9", + "lodash.bind": "^4.1.4", + "lodash.defaults": "^4.0.1", + "lodash.filter": "^4.4.0", + "lodash.flatten": "^4.2.0", + "lodash.foreach": "^4.3.0", + "lodash.map": "^4.4.0", + "lodash.merge": "^4.4.0", + "lodash.pick": "^4.2.1", + "lodash.reduce": "^4.4.0", + "lodash.reject": "^4.4.0", + "lodash.some": "^4.4.0" + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "optional": true + }, + "circular-json": { + "version": "0.5.9", + "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz", + "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==" + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "optional": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "optional": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + }, + "dependencies": { + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + } + } + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "optional": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "colorette": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", + "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", + "dev": true + }, + "colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true, + "optional": true + }, + "compound-subject": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/compound-subject/-/compound-subject-0.0.1.tgz", + "integrity": "sha1-JxVUaYoVrmCLHfyv0wt7oeqJLEs=" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "contains-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", + "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "dev": true + }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true, + "optional": true + }, + "core-js": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" + }, + "core-js-compat": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz", + "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==", + "dev": true, + "requires": { + "browserslist": "^4.8.5", + "semver": "7.0.0" + }, + "dependencies": { + "semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true + } + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=", + "dev": true + }, + "crypto-hash": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crypto-hash/-/crypto-hash-1.2.2.tgz", + "integrity": "sha512-rXXMXepuKg9gIfqE7I1jtVa6saLhzIkDQ2u3kTGUWYiUGsHcUa3LTsfrjPEdOY8kxKlryQtsOmJOU0F23yRJTg==" + }, + "css-select": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", + "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", + "requires": { + "boolbase": "~1.0.0", + "css-what": "2.1", + "domutils": "1.5.1", + "nth-check": "~1.0.1" + } + }, + "css-what": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", + "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true, + "optional": true + }, + "decompress-response": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-5.0.0.tgz", + "integrity": "sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw==", + "dev": true, + "requires": { + "mimic-response": "^2.0.0" + } + }, + "deep-eql": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", + "requires": { + "type-detect": "0.1.1" + } + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "defer-to-connect": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz", + "integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "optional": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "optional": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + }, + "dependencies": { + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + } + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-serializer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", + "requires": { + "domelementtype": "^1.3.0", + "entities": "^1.1.1" + } + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" + }, + "domhandler": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", + "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "dotenv": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", + "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "dev": true + }, + "electron-to-chromium": { + "version": "1.3.514", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.514.tgz", + "integrity": "sha512-8vb8zKIeGlZigeDzNWWthmGeLzo5CC43Lc+CZshMs7UXFVMPNLtXJGa/txedpu3OJFrXXVheBwp9PqOJJlHQ8w==", + "dev": true + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + } + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es6-promise": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", + "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" + }, + "es6-promisify": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", + "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", + "requires": { + "es6-promise": "^4.0.3" + } + }, + "escalade": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.2.tgz", + "integrity": "sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "esdoc-inject-style-plugin": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/esdoc-inject-style-plugin/-/esdoc-inject-style-plugin-1.0.0.tgz", + "integrity": "sha1-oTWXNou5+4nDZeBmSVyvl6Tey7E=", + "requires": { + "cheerio": "0.22.0", + "fs-extra": "1.0.0" + } + }, + "eslint": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.6.0.tgz", + "integrity": "sha512-QlAManNtqr7sozWm5TF4wIH9gmUm2hE3vNRUvyoYAa4y1l5/jxD/PQStEjBMQtCqZmSep8UxrcecI60hOpe61w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "enquirer": "^2.3.5", + "eslint-scope": "^5.1.0", + "eslint-utils": "^2.1.0", + "eslint-visitor-keys": "^1.3.0", + "espree": "^7.2.0", + "esquery": "^1.2.0", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob-parent": "^5.0.0", + "globals": "^12.1.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-yaml": "^3.13.1", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash": "^4.17.19", + "minimatch": "^3.0.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "progress": "^2.0.0", + "regexpp": "^3.1.0", + "semver": "^7.2.1", + "strip-ansi": "^6.0.0", + "strip-json-comments": "^3.1.0", + "table": "^5.2.3", + "text-table": "^0.2.0", + "v8-compile-cache": "^2.0.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + }, + "globals": { + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", + "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", + "dev": true, + "requires": { + "type-fest": "^0.8.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "supports-color": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", + "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "eslint-import-resolver-node": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "resolve": "^1.13.1" + }, + "dependencies": { + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "eslint-module-utils": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", + "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", + "dev": true, + "requires": { + "debug": "^2.6.9", + "pkg-dir": "^2.0.0" + } + }, + "eslint-plugin-import": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz", + "integrity": "sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg==", + "dev": true, + "requires": { + "array-includes": "^3.1.1", + "array.prototype.flat": "^1.2.3", + "contains-path": "^0.1.0", + "debug": "^2.6.9", + "doctrine": "1.5.0", + "eslint-import-resolver-node": "^0.3.3", + "eslint-module-utils": "^2.6.0", + "has": "^1.0.3", + "minimatch": "^3.0.4", + "object.values": "^1.1.1", + "read-pkg-up": "^2.0.0", + "resolve": "^1.17.0", + "tsconfig-paths": "^3.9.0" + }, + "dependencies": { + "doctrine": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", + "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "isarray": "^1.0.0" + } + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + } + } + }, + "eslint-scope": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", + "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + }, + "espree": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-7.2.0.tgz", + "integrity": "sha512-H+cQ3+3JYRMEIOl87e7QdHX70ocly5iW4+dttuR8iYSPr/hXKFb+7dBsZ7+u1adC4VrnPlTkv0+OwuPnDop19g==", + "dev": true, + "requires": { + "acorn": "^7.3.1", + "acorn-jsx": "^5.2.0", + "eslint-visitor-keys": "^1.3.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "dev": true + } + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", + "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", + "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + }, + "exec-sh": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.2.tgz", + "integrity": "sha512-FIUCJz1RbuS0FKTdaAafAByGS0CPvU3R0MeHxgtl+djzCc//F8HakL8GzmVNZanasTbTAY/3DRFA0KpVqj/eAw==", + "dev": true, + "requires": { + "merge": "^1.2.0" + } + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "optional": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "optional": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "eyes": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", + "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", + "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.0", + "merge2": "^1.3.0", + "micromatch": "^4.0.2", + "picomatch": "^2.2.1" + }, + "dependencies": { + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "micromatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", + "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", + "dev": true, + "requires": { + "braces": "^3.0.1", + "picomatch": "^2.0.5" + } + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fastq": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz", + "integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "optional": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + }, + "dependencies": { + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + } + } + }, + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", + "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", + "dev": true + }, + "flow-bin": { + "version": "0.131.0", + "resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.131.0.tgz", + "integrity": "sha512-fZmoIBcDrtLhy/NNMxwJysSYzMr1ksRcAOMi3AHSoYXfcuQqTvhGJx+wqjlIOqIwz8RRYm8J4V4JrSJbIKP+Xg==", + "dev": true + }, + "flow-typed": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/flow-typed/-/flow-typed-3.2.1.tgz", + "integrity": "sha512-vWQLZmndgdEHdy1TKTeI1DkLLa4078p6vhcKz/IZ6fcpaWLElTSG7rZi3BxlcSWfEQPxsymuSBNwAT7dCpXm6g==", + "dev": true, + "requires": { + "@octokit/rest": "^16.43.1", + "colors": "^1.4.0", + "flowgen": "^1.10.0", + "fs-extra": "^8.1.0", + "glob": "^7.1.6", + "got": "^10.5.7", + "md5": "^2.2.1", + "mkdirp": "^1.0.3", + "prettier": "^1.19.1", + "rimraf": "^3.0.2", + "semver": "^7.3.2", + "table": "^5.4.6", + "through": "^2.3.8", + "unzipper": "^0.10.8", + "which": "^2.0.2", + "yargs": "^15.1.0" + }, + "dependencies": { + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "prettier": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", + "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } + } + }, + "flowgen": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/flowgen/-/flowgen-1.11.0.tgz", + "integrity": "sha512-WpoBjzcZadnAw5FatlUbvFWUWXkI2/LjrwTl5fl3MVDh+KdvYgFzgRXDDKH/O2uUlwjfpveiJJJx8TwL7Se84A==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.8.3", + "@babel/highlight": "^7.9.0", + "commander": "^5.1.0", + "lodash": "^4.17.15", + "prettier": "^2.0.5", + "shelljs": "^0.8.4", + "typescript": "^3.4", + "typescript-compiler": "^1.4.1-2" + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true + } + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true, + "optional": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "optional": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fs-extra": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", + "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0" + }, + "dependencies": { + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", + "requires": { + "graceful-fs": "^4.1.6" + } + } + } + }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.6.0" + } + }, + "fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", + "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", + "dev": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true, + "optional": true + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", + "dev": true, + "optional": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", + "dev": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "optional": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true, + "optional": true + }, + "needle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-2.3.0.tgz", + "integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==", + "dev": true, + "optional": true, + "requires": { + "debug": "^4.1.0", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz", + "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==", + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", + "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.1.tgz", + "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", + "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true, + "optional": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true, + "optional": true + } + } + }, + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "gensync": { + "version": "1.0.0-beta.1", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", + "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-prototype-of": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/get-prototype-of/-/get-prototype-of-0.0.0.tgz", + "integrity": "sha1-mHcr0QcW0W3rSzIlFsRp78oorEQ=" + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true, + "optional": true + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", + "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "globby": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz", + "integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "dependencies": { + "ignore": { + "version": "5.1.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", + "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + } + } + }, + "got": { + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/got/-/got-10.7.0.tgz", + "integrity": "sha512-aWTDeNw9g+XqEZNcTjMMZSy7B7yE9toWOFYip7ofFTLleJhvZwUxxTxkTpKvF+p1SAA4VHmuEy7PiHTHyq8tJg==", + "dev": true, + "requires": { + "@sindresorhus/is": "^2.0.0", + "@szmarczak/http-timer": "^4.0.0", + "@types/cacheable-request": "^6.0.1", + "cacheable-lookup": "^2.0.0", + "cacheable-request": "^7.0.1", + "decompress-response": "^5.0.0", + "duplexer3": "^0.1.4", + "get-stream": "^5.0.0", + "lowercase-keys": "^2.0.0", + "mimic-response": "^2.1.0", + "p-cancelable": "^2.0.0", + "p-event": "^4.0.0", + "responselike": "^2.0.0", + "to-readable-stream": "^2.0.0", + "type-fest": "^0.10.0" + }, + "dependencies": { + "get-stream": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", + "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "type-fest": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.10.0.tgz", + "integrity": "sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw==", + "dev": true + } + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "optional": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "optional": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", + "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" + }, + "htmlparser2": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", + "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", + "requires": { + "domelementtype": "^1.3.1", + "domhandler": "^2.3.0", + "domutils": "^1.5.1", + "entities": "^1.1.1", + "inherits": "^2.0.1", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "dev": true + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "dev": true + }, + "import-fresh": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", + "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "optional": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" + }, + "is-capitalized": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-capitalized/-/is-capitalized-1.0.0.tgz", + "integrity": "sha1-TIRktNkdPk7rRIid0s2PGwrEwTY=" + }, + "is-class": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/is-class/-/is-class-0.0.4.tgz", + "integrity": "sha1-4FdFFwW7NOOePjNZjJOpg3KWtzY=" + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "optional": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "optional": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true, + "optional": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", + "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "optional": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "requires": { + "@types/estree": "*" + } + }, + "is-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", + "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", + "requires": { + "has-symbols": "^1.0.1" + }, + "dependencies": { + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + } + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-string": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", + "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", + "dev": true + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true, + "optional": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, + "optional": true + }, + "jayson": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/jayson/-/jayson-3.3.3.tgz", + "integrity": "sha512-0bQ/vNvWyi+fzNoMvRK63m7BGU+PWwaJRVaGgJWZeAL5euOSqJtqzqyCrfiS3Sdw3OrvgKWF5f5N8ut87gxrng==", + "requires": { + "@types/connect": "^3.4.32", + "@types/express-serve-static-core": "^4.16.9", + "@types/lodash": "^4.14.139", + "@types/node": "^12.7.7", + "JSONStream": "^1.3.1", + "commander": "^2.12.2", + "es6-promisify": "^5.0.0", + "eyes": "^0.1.8", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.15", + "uuid": "^3.2.1" + }, + "dependencies": { + "@types/node": { + "version": "12.12.53", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.53.tgz", + "integrity": "sha512-51MYTDTyCziHb70wtGNFRwB4l+5JNvdqzFSkbDvpbftEgVUBEE+T5f7pROhWMp/fxp07oNIEQZd5bbfAH22ohQ==" + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" + }, + "keypather": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/keypather/-/keypather-1.10.2.tgz", + "integrity": "sha1-4ESWMtSz5RbyHMAUznxWRP3c5hQ=", + "requires": { + "101": "^1.0.0" + } + }, + "keyv": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.1.tgz", + "integrity": "sha512-xz6Jv6oNkbhrFCvCP7HQa8AaII8y8LRpoSm661NOKLr4uHuBwhX4epXrPQgF3+xdJnN4Esm5X0xwY4bOlALOtw==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" + }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "requires": { + "graceful-fs": "^4.1.9" + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levenary": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", + "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", + "dev": true, + "requires": { + "leven": "^3.1.0" + } + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=", + "dev": true + }, + "load-json-file": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", + "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^2.2.0", + "pify": "^2.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "dev": true, + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.19", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", + "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" + }, + "lodash.assignin": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", + "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=" + }, + "lodash.bind": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", + "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=" + }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + }, + "lodash.filter": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", + "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=" + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + }, + "lodash.foreach": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", + "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", + "dev": true + }, + "lodash.map": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", + "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=" + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, + "lodash.pick": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", + "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=" + }, + "lodash.reduce": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", + "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=" + }, + "lodash.reject": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", + "integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU=" + }, + "lodash.set": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", + "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", + "dev": true + }, + "lodash.some": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", + "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=" + }, + "lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + }, + "macos-release": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.4.1.tgz", + "integrity": "sha512-H/QHeBIN1fIGJX517pvK8IEK53yQOW7YcEI55oYtgjDdoCQQz7eJS94qt5kNrscReEyuD/JcdFCm2XBEcGOITg==", + "dev": true + }, + "magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true, + "optional": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "optional": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "md5": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", + "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", + "dev": true, + "requires": { + "charenc": "~0.0.1", + "crypt": "~0.0.1", + "is-buffer": "~1.1.1" + } + }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=" + }, + "merge": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz", + "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "optional": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + }, + "dependencies": { + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true, + "optional": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true, + "optional": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "optional": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "optional": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "optional": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "optional": true + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "optional": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "optional": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "optional": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.9.0" + } + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "optional": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "optional": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "mkdirp-promise": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", + "integrity": "sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE=", + "requires": { + "mkdirp": "*" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "requires": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "optional": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true, + "optional": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true, + "optional": true + } + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" + }, + "node-environment-flags": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", + "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", + "dev": true, + "requires": { + "object.getownpropertydescriptors": "^2.0.3", + "semver": "^5.7.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + } + } + }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + }, + "node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "dev": true + }, + "node-releases": { + "version": "1.1.60", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz", + "integrity": "sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", + "dev": true + }, + "npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "requires": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "dependencies": { + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "requires": { + "pify": "^3.0.0" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + } + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "nth-check": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", + "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", + "requires": { + "boolbase": "~1.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "optional": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "optional": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" + }, + "object-keys": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", + "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==" + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "optional": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.getownpropertydescriptors": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", + "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "optional": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "object.values": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", + "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", + "dev": true, + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1", + "has": "^1.0.3" + } + }, + "octokit-pagination-methods": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz", + "integrity": "sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "os-name": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", + "integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==", + "dev": true, + "requires": { + "macos-release": "^2.2.0", + "windows-release": "^3.1.0" + } + }, + "p-cancelable": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", + "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==", + "dev": true + }, + "p-event": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", + "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", + "dev": true, + "requires": { + "p-timeout": "^3.1.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "dev": true, + "requires": { + "p-limit": "^1.1.0" + } + }, + "p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dev": true, + "requires": { + "p-finally": "^1.0.0" + } + }, + "p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", + "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", + "dev": true, + "requires": { + "error-ex": "^1.2.0" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true, + "optional": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true, + "optional": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", + "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", + "dev": true, + "requires": { + "pify": "^2.0.0" + } + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", + "dev": true + }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, + "pkg-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", + "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "dev": true, + "requires": { + "find-up": "^2.1.0" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true, + "optional": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "prettier": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", + "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "read-pkg": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", + "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", + "dev": true, + "requires": { + "load-json-file": "^2.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^2.0.0" + } + }, + "read-pkg-up": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", + "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", + "dev": true, + "requires": { + "find-up": "^2.0.0", + "read-pkg": "^2.0.0" + } + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "dev": true, + "optional": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + }, + "dependencies": { + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true, + "optional": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true, + "optional": true + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "optional": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "optional": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "optional": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "optional": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + } + }, + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true, + "optional": true + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "optional": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "optional": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "optional": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "optional": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "optional": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + } + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "regenerate": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", + "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==", + "dev": true + }, + "regenerate-unicode-properties": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", + "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", + "dev": true, + "requires": { + "regenerate": "^1.4.0" + } + }, + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + }, + "regenerator-transform": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", + "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "optional": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexpp": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", + "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", + "dev": true + }, + "regexpu-core": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", + "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==", + "dev": true, + "requires": { + "regenerate": "^1.4.0", + "regenerate-unicode-properties": "^8.2.0", + "regjsgen": "^0.5.1", + "regjsparser": "^0.6.4", + "unicode-match-property-ecmascript": "^1.0.4", + "unicode-match-property-value-ecmascript": "^1.2.0" + } + }, + "regjsgen": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", + "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", + "dev": true + }, + "regjsparser": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", + "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", + "dev": true, + "requires": { + "jsesc": "~0.5.0" + }, + "dependencies": { + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", + "dev": true + } + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true, + "optional": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true, + "optional": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true, + "optional": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "resolve": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", + "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==", + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true, + "optional": true + }, + "responselike": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", + "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", + "dev": true, + "requires": { + "lowercase-keys": "^2.0.0" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true, + "optional": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "rollup": { + "version": "2.23.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.23.1.tgz", + "integrity": "sha512-Heyl885+lyN/giQwxA8AYT2GY3U+gOlTqVLrMQYno8Z1X9lAOpfXPiKiZCyPc25e9BLJM3Zlh957dpTlO4pa8A==", + "dev": true, + "requires": { + "fsevents": "~2.1.2" + }, + "dependencies": { + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + } + } + }, + "rollup-plugin-copy": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-copy/-/rollup-plugin-copy-3.3.0.tgz", + "integrity": "sha512-euDjCUSBXZa06nqnwCNADbkAcYDfzwowfZQkto9K/TFhiH+QG7I4PUsEMwM9tDgomGWJc//z7KLW8t+tZwxADA==", + "dev": true, + "requires": { + "@types/fs-extra": "^8.0.1", + "colorette": "^1.1.0", + "fs-extra": "^8.1.0", + "globby": "10.0.1", + "is-plain-object": "^3.0.0" + }, + "dependencies": { + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + }, + "is-plain-object": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz", + "integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==", + "dev": true + } + } + }, + "rpc-websockets": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-5.2.4.tgz", + "integrity": "sha512-6jqeJK/18hPTsmeiN+K9O4miZiAmrIncgxfPXHwuGWs9BClA2zC3fOnTThRWo4blkrjH59oKKi0KMxSK+wdtNw==", + "requires": { + "@babel/runtime": "^7.8.7", + "assert-args": "^1.2.1", + "babel-runtime": "^6.26.0", + "circular-json": "^0.5.9", + "eventemitter3": "^3.1.2", + "uuid": "^3.4.0", + "ws": "^5.2.2" + }, + "dependencies": { + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "run-parallel": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", + "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", + "dev": true + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "optional": true, + "requires": { + "ret": "~0.1.10" + } + }, + "semver": { + "version": "7.3.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", + "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "optional": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", + "dev": true + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" + }, + "shell-quote": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", + "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==" + }, + "shelljs": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", + "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + } + }, + "signal-exit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", + "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", + "dev": true + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + } + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "optional": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "optional": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "optional": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "optional": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "optional": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "optional": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "optional": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true, + "optional": true + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", + "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==" + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "optional": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "optional": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "optional": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "string.prototype.padend": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.0.tgz", + "integrity": "sha512-3aIv8Ffdp8EZj8iLwREGpQaUZiPyrWrpzMBHvkiSW/bK/EGve9np07Vwy7IJ5waydpGXzQZu/F8Oze2/IWkBaA==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" + }, + "is-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", + "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + } + } + }, + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + }, + "dependencies": { + "es-abstract": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" + }, + "is-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", + "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", + "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "superstruct": { + "version": "0.8.4", + "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.8.4.tgz", + "integrity": "sha512-48Ors8IVWZm/tMr8r0Si6+mJiB7mkD7jqvIzktjJ4+EnP5tBp0qOpiM1J8sCUorKx+TXWrfb3i1UcjdD1YK/wA==", + "requires": { + "kind-of": "^6.0.2", + "tiny-invariant": "^1.0.6" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "table": { + "version": "5.4.6", + "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", + "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", + "dev": true, + "requires": { + "ajv": "^6.10.2", + "lodash": "^4.17.14", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ajv": { + "version": "6.12.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", + "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + } + } + }, + "tar": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "thenify": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", + "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", + "requires": { + "any-promise": "^1.0.0" + } + }, + "thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", + "requires": { + "thenify": ">= 3.1.0 < 4" + } + }, + "through": { + "version": "2.3.8", + "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "tiny-invariant": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", + "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==" + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-readable-stream": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-2.1.0.tgz", + "integrity": "sha512-o3Qa6DGg1CEXshSdvWNX2sN4QHqg03SPq7U6jPXRahlQdl5dK8oXjkU/2/sGrnOZKeGV1zLSO8qPwyKklPPE7w==", + "dev": true + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "optional": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "optional": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + }, + "dependencies": { + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "optional": true, + "requires": { + "kind-of": "^3.0.2" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "optional": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=", + "dev": true + }, + "tsconfig-paths": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", + "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", + "dev": true, + "requires": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.0", + "strip-bom": "^3.0.0" + } + }, + "tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", + "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=" + }, + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true + }, + "typescript": { + "version": "3.9.7", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", + "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", + "dev": true + }, + "typescript-compiler": { + "version": "1.4.1-2", + "resolved": "https://registry.npmjs.org/typescript-compiler/-/typescript-compiler-1.4.1-2.tgz", + "integrity": "sha1-uk99si2RU0oZKdkACdzhYety/T8=", + "dev": true + }, + "unicode-canonical-property-names-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", + "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", + "dev": true + }, + "unicode-match-property-ecmascript": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", + "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", + "dev": true, + "requires": { + "unicode-canonical-property-names-ecmascript": "^1.0.4", + "unicode-property-aliases-ecmascript": "^1.0.4" + } + }, + "unicode-match-property-value-ecmascript": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", + "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", + "dev": true + }, + "unicode-property-aliases-ecmascript": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", + "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", + "dev": true + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "optional": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "universal-user-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.1.tgz", + "integrity": "sha512-LnST3ebHwVL2aNe4mejI9IQh2HfZ1RLo8Io2HugSif8ekzD1TlWpHpColOB/eh8JHMLkGH3Akqf040I+4ylNxg==", + "dev": true, + "requires": { + "os-name": "^3.1.0" + } + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "optional": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "optional": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "optional": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true, + "optional": true + } + } + }, + "unzipper": { + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz", + "integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==", + "dev": true, + "requires": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + }, + "dependencies": { + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "dev": true + } + } + }, + "upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true, + "optional": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true, + "optional": true + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true, + "optional": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + }, + "v8-compile-cache": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", + "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", + "dev": true + }, + "v8flags": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", + "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "watch": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/watch/-/watch-1.0.2.tgz", + "integrity": "sha1-NApxe952Vyb6CqB9ch4BR6VR3ww=", + "dev": true, + "requires": { + "exec-sh": "^0.2.0", + "minimist": "^1.2.0" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "windows-release": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.3.1.tgz", + "integrity": "sha512-Pngk/RDCaI/DkuHPlGTdIkDiTAnAkyMjoQMZqRsxydNl1qGXNIoZrB7RK8g53F2tEgQBMqQJHQdYZuQEEAu54A==", + "dev": true, + "requires": { + "execa": "^1.0.0" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", + "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "dev": true, + "requires": { + "@types/color-name": "^1.1.1", + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "ws": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", + "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "optional": true + }, + "yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "requires": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "dependencies": { + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "string-width": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", + "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.0" + } + } + } + }, + "yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + } + } +} diff --git a/program/js/package.json b/program/js/package.json new file mode 100644 index 0000000..4016239 --- /dev/null +++ b/program/js/package.json @@ -0,0 +1,78 @@ +{ + "name": "@solana/spl-token", + "version": "0.0.5", + "description": "SPL Token Javascript API", + "license": "MIT", + "author": "Solana Maintainers ", + "homepage": "https://solana.com/", + "repository": { + "type": "git", + "url": "https://github.com/solana-labs/solana-program-library" + }, + "bugs": { + "url": "https://github.com/solana-labs/solana-program-library/issues" + }, + "publishConfig": { + "access": "public" + }, + "main": "lib/index.cjs.js", + "module": "lib/index.esm.js", + "types": "lib/index.d.ts", + "files": [ + "/lib", + "/module.flow.js" + ], + "testnetDefaultChannel": "v1.3.1", + "scripts": { + "build": "rollup -c", + "start": "babel-node cli/main.js", + "lint": "npm run pretty && eslint .", + "lint:fix": "npm run lint -- --fix", + "flow": "flow", + "flow:watch": "watch 'flow' . --wait=1 --ignoreDirectoryPattern=/doc/", + "lint:watch": "watch 'npm run lint:fix' . --wait=1", + "build:program": "rm client/util/store/config.json; ../../do.sh build token", + "cluster:localnet": "rm -f .env", + "cluster:devnet": "cp cluster-devnet.env .env", + "cluster:testnet": "cp cluster-testnet.env .env", + "cluster:mainnet-beta": "cp cluster-mainnet-beta.env .env", + "localnet:update": "solana-localnet update", + "localnet:up": "rm client/util/store/config.json; set -x; solana-localnet down; set -e; solana-localnet up", + "localnet:down": "solana-localnet down", + "localnet:logs": "solana-localnet logs -f", + "pretty": "prettier --write '{,cli*/**/}*.js'" + }, + "dependencies": { + "@babel/runtime": "^7.10.5", + "@solana/web3.js": "^0.64.0", + "bn.js": "^5.0.0", + "buffer-layout": "^1.2.0", + "dotenv": "8.2.0", + "mkdirp-promise": "^5.0.1" + }, + "devDependencies": { + "@babel/cli": "^7.10.5", + "@babel/core": "^7.10.5", + "@babel/node": "^7.10.5", + "@babel/plugin-proposal-class-properties": "^7.10.4", + "@babel/plugin-transform-runtime": "^7.10.5", + "@babel/preset-env": "^7.10.4", + "@babel/preset-flow": "^7.10.4", + "@rollup/plugin-babel": "^5.1.0", + "@rollup/plugin-commonjs": "^14.0.0", + "babel-eslint": "^10.1.0", + "eslint": "^7.4.0", + "eslint-plugin-import": "^2.22.0", + "flow-bin": "0.131.0", + "flow-typed": "^3.2.0", + "mz": "^2.7.0", + "prettier": "^2.0.5", + "rollup": "^2.23.0", + "rollup-plugin-copy": "^3.3.0", + "semver": "^7.0.0", + "watch": "^1.0.2" + }, + "engines": { + "node": ">= 10" + } +} diff --git a/program/js/rollup.config.js b/program/js/rollup.config.js new file mode 100644 index 0000000..211e4b7 --- /dev/null +++ b/program/js/rollup.config.js @@ -0,0 +1,74 @@ +import babel from '@rollup/plugin-babel'; +import commonjs from '@rollup/plugin-commonjs'; +import copy from 'rollup-plugin-copy'; + +function generateConfig(configType) { + const config = { + input: 'client/token.js', + plugins: [ + babel({ + configFile: './babel.rollup.config.json', + exclude: 'node_modules/**', + babelHelpers: 'runtime', + }), + commonjs(), + copy({ + targets: [{src: 'module.d.ts', dest: 'lib', rename: 'index.d.ts'}], + }), + ], + }; + + switch (configType) { + case 'browser': + // TODO: Add support + break; + case 'node': + config.output = [ + { + file: 'lib/index.cjs.js', + format: 'cjs', + sourcemap: true, + }, + { + file: 'lib/index.esm.js', + format: 'es', + sourcemap: true, + }, + ]; + + // Quash 'Unresolved dependencies' complaints for modules listed in the + // package.json "dependencies" section. Unfortunately this list is manually + // maintained. + config.external = [ + 'assert', + '@babel/runtime/core-js/get-iterator', + '@babel/runtime/core-js/json/stringify', + '@babel/runtime/core-js/object/assign', + '@babel/runtime/core-js/object/get-prototype-of', + '@babel/runtime/core-js/object/keys', + '@babel/runtime/core-js/promise', + '@babel/runtime/helpers/asyncToGenerator', + '@babel/runtime/helpers/classCallCheck', + '@babel/runtime/helpers/createClass', + '@babel/runtime/helpers/defineProperty', + '@babel/runtime/helpers/get', + '@babel/runtime/helpers/getPrototypeOf', + '@babel/runtime/helpers/inherits', + '@babel/runtime/helpers/possibleConstructorReturn', + '@babel/runtime/helpers/slicedToArray', + '@babel/runtime/helpers/toConsumableArray', + '@babel/runtime/helpers/typeof', + '@babel/runtime/regenerator', + 'bn.js', + 'buffer-layout', + '@solana/web3.js', + ]; + break; + default: + throw new Error(`Unknown configType: ${configType}`); + } + + return config; +} + +export default [generateConfig('node')]; diff --git a/program/js/url.js b/program/js/url.js new file mode 100644 index 0000000..3138a7c --- /dev/null +++ b/program/js/url.js @@ -0,0 +1,31 @@ +// To connect to a public cluster, set `export LIVE=1` in your +// environment. By default, `LIVE=1` will connect to the devnet cluster. + +import {clusterApiUrl, Cluster} from '@solana/web3.js'; +import dotenv from 'dotenv'; + +function chooseCluster(): Cluster | undefined { + dotenv.config(); + if (!process.env.LIVE) return; + switch (process.env.CLUSTER) { + case 'devnet': + case 'testnet': + case 'mainnet-beta': { + return process.env.CLUSTER; + } + } + throw 'Unknown cluster "' + process.env.CLUSTER + '", check the .env file'; +} + +export const cluster = chooseCluster(); + +export const url = + process.env.RPC_URL || + (process.env.LIVE ? clusterApiUrl(cluster, false) : 'http://localhost:8899'); + +export const urlTls = + process.env.RPC_URL || + (process.env.LIVE ? clusterApiUrl(cluster, true) : 'http://localhost:8899'); + +export let walletUrl = + process.env.WALLET_URL || 'https://solana-example-webwallet.herokuapp.com/'; diff --git a/program/program-id.md b/program/program-id.md new file mode 100644 index 0000000..77df2a0 --- /dev/null +++ b/program/program-id.md @@ -0,0 +1 @@ +TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o diff --git a/program/src/entrypoint.rs b/program/src/entrypoint.rs new file mode 100644 index 0000000..b5594ca --- /dev/null +++ b/program/src/entrypoint.rs @@ -0,0 +1,24 @@ +//! Program entrypoint + +#![cfg(feature = "program")] +#![cfg(not(feature = "no-entrypoint"))] + +use crate::{error::TokenError, processor::Processor}; +use solana_sdk::{ + account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, + program_error::PrintProgramError, pubkey::Pubkey, +}; + +entrypoint!(process_instruction); +fn process_instruction<'a>( + program_id: &Pubkey, + accounts: &'a [AccountInfo<'a>], + instruction_data: &[u8], +) -> ProgramResult { + if let Err(error) = Processor::process(program_id, accounts, instruction_data) { + // catch the error so we can print it + error.print::(); + return Err(error); + } + Ok(()) +} diff --git a/program/src/error.rs b/program/src/error.rs new file mode 100644 index 0000000..32f9822 --- /dev/null +++ b/program/src/error.rs @@ -0,0 +1,56 @@ +//! Error types + +use num_derive::FromPrimitive; +use solana_sdk::{decode_error::DecodeError, program_error::ProgramError}; +use thiserror::Error; + +/// Errors that may be returned by the Token program. +#[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)] +pub enum TokenError { + /// Insufficient funds for the operation requested. + #[error("Insufficient funds")] + InsufficientFunds, + /// Account not associated with this Mint. + #[error("Account not associated with this Mint")] + MintMismatch, + /// Owner does not match. + #[error("Owner does not match")] + OwnerMismatch, + /// This token's supply is fixed and new tokens cannot be minted. + #[error("Fixed supply")] + FixedSupply, + /// The account cannot be initialized because it is already being used. + #[error("AlreadyInUse")] + AlreadyInUse, + /// An owner is required if initial supply is zero. + #[error("An owner is required if supply is zero")] + OwnerRequiredIfNoInitialSupply, + /// Invalid number of provided signers. + #[error("Invalid number of provided signers")] + InvalidNumberOfProvidedSigners, + /// Invalid number of required signers. + #[error("Invalid number of required signers")] + InvalidNumberOfRequiredSigners, + /// State is uninitialized. + #[error("State is unititialized")] + UninitializedState, + /// Instruction does not support native tokens + #[error("Instruction does not support native tokens")] + NativeNotSupported, + /// Instruction does not support non-native tokens + #[error("Instruction does not support non-native tokens")] + NonNativeNotSupported, + /// Invalid instruction + #[error("Invalid instruction")] + InvalidInstruction, +} +impl From for ProgramError { + fn from(e: TokenError) -> Self { + ProgramError::Custom(e as u32) + } +} +impl DecodeError for TokenError { + fn type_of() -> &'static str { + "TokenError" + } +} diff --git a/program/src/instruction.rs b/program/src/instruction.rs new file mode 100644 index 0000000..e3e78e7 --- /dev/null +++ b/program/src/instruction.rs @@ -0,0 +1,593 @@ +//! Instruction types + +use crate::error::TokenError; +use solana_sdk::{ + instruction::{AccountMeta, Instruction}, + program_error::ProgramError, + pubkey::Pubkey, +}; +use std::mem::size_of; + +/// Minimum number of multisignature signers (min N) +pub const MIN_SIGNERS: usize = 1; +/// Maximum number of multisignature signers (max N) +pub const MAX_SIGNERS: usize = 11; + +/// Instructions supported by the token program. +#[repr(C)] +#[derive(Clone, Debug, PartialEq)] +pub enum TokenInstruction { + /// Initializes a new mint and optionally deposits all the newly minted tokens in an account. + /// + /// The `InitializeMint` instruction requires no signers and MUST be included within + /// the same Transaction as the system program's `CreateInstruction` that creates the account + /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. + /// + /// Accounts expected by this instruction: + /// + /// 0. `[writable]` The mint to initialize. + /// 1. + /// * If supply is non-zero: `[writable]` The account to hold all the newly minted tokens. + /// * If supply is zero: `[]` The owner/multisignature of the mint. + /// 2. `[]` (optional) The owner/multisignature of the mint if supply is non-zero, if + /// present then further minting is supported. + /// + InitializeMint { + /// Initial amount of tokens to mint. + amount: u64, + /// Number of base 10 digits to the right of the decimal place. + decimals: u8, + }, + /// Initializes a new account to hold tokens. If this account is associated with the native mint + /// then the token balance of the initialized account will be equal to the amount of SOL in the account. + /// + /// The `InitializeAccount` instruction requires no signers and MUST be included within + /// the same Transaction as the system program's `CreateInstruction` that creates the account + /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. + /// + /// Accounts expected by this instruction: + /// + /// 0. `[writable]` The account to initialize. + /// 1. `[]` The mint this account will be associated with. + /// 2. `[]` The new account's owner/multisignature. + InitializeAccount, + /// Initializes a multisignature account with N provided signers. + /// + /// Multisignature accounts can used in place of any single owner/delegate accounts in any + /// token instruction that require an owner/delegate to be present. The variant field represents the + /// number of signers (M) required to validate this multisignature account. + /// + /// The `InitializeMultisig` instruction requires no signers and MUST be included within + /// the same Transaction as the system program's `CreateInstruction` that creates the account + /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. + /// + /// Accounts expected by this instruction: + /// + /// 0. `[writable]` The multisignature account to initialize. + /// 1. ..1+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. + InitializeMultisig { + /// The number of signers (M) required to validate this multisignature account. + m: u8, + }, + /// Transfers tokens from one account to another either directly or via a delegate. If this + /// account is associated with the native mint then equal amounts of SOL and Tokens will be + /// transferred to the destination account. + /// + /// Accounts expected by this instruction: + /// + /// * Single owner/delegate + /// 0. `[writable]` The source account. + /// 1. `[writable]` The destination account. + /// 2. '[signer]' The source account's owner/delegate. + /// + /// * Multisignature owner/delegate + /// 0. `[writable]` The source account. + /// 1. `[writable]` The destination account. + /// 2. '[]' The source account's multisignature owner/delegate. + /// 3. ..3+M '[signer]' M signer accounts. + Transfer { + /// The amount of tokens to transfer. + amount: u64, + }, + /// Approves a delegate. A delegate is given the authority over + /// tokens on behalf of the source account's owner. + + /// Accounts expected by this instruction: + /// + /// * Single owner + /// 0. `[writable]` The source account. + /// 1. `[]` The delegate. + /// 2. `[signer]` The source account owner. + /// + /// * Multisignature owner + /// 0. `[writable]` The source account. + /// 1. `[]` The delegate. + /// 2. '[]' The source account's multisignature owner. + /// 3. ..3+M '[signer]' M signer accounts + Approve { + /// The amount of tokens the delegate is approved for. + amount: u64, + }, + /// Revokes the delegate's authority. + /// + /// Accounts expected by this instruction: + /// + /// * Single owner + /// 0. `[writable]` The source account. + /// 1. `[signer]` The source account owner. + /// + /// * Multisignature owner + /// 0. `[writable]` The source account. + /// 1. '[]' The source account's multisignature owner. + /// 2. ..2+M '[signer]' M signer accounts + Revoke, + /// Sets a new owner of a mint or account. + /// + /// Accounts expected by this instruction: + /// + /// * Single owner + /// 0. `[writable]` The mint or account to change the owner of. + /// 1. `[]` The new owner/delegate/multisignature. + /// 2. `[signer]` The owner of the mint or account. + /// + /// * Multisignature owner + /// 0. `[writable]` The mint or account to change the owner of. + /// 1. `[]` The new owner/delegate/multisignature. + /// 2. `[]` The mint's or account's multisignature owner. + /// 3. ..3+M '[signer]' M signer accounts + SetOwner, + /// Mints new tokens to an account. The native mint does not support minting. + /// + /// Accounts expected by this instruction: + /// + /// * Single owner + /// 0. `[writable]` The mint. + /// 1. `[writable]` The account to mint tokens to. + /// 2. `[signer]` The mint's owner. + /// + /// * Multisignature owner + /// 0. `[writable]` The mint. + /// 1. `[writable]` The account to mint tokens to. + /// 2. `[]` The mint's multisignature owner. + /// 3. ..3+M '[signer]' M signer accounts. + MintTo { + /// The amount of new tokens to mint. + amount: u64, + }, + /// Burns tokens by removing them from an account. `Burn` does not support accounts + /// associated with the native mint, use `CloseAccount` instead. + /// + /// Accounts expected by this instruction: + /// + /// * Single owner/delegate + /// 0. `[writable]` The account to burn from. + /// 1. `[signer]` The account's owner/delegate. + /// + /// * Multisignature owner/delegate + /// 0. `[writable]` The account to burn from. + /// 1. `[]` The account's multisignature owner/delegate. + /// 2. ..2+M '[signer]' M signer accounts. + Burn { + /// The amount of tokens to burn. + amount: u64, + }, + /// Close an account by transferring all its SOL to the destination account. + /// Non-native accounts may only be closed if its token amount is zero. + /// + /// Accounts expected by this instruction: + /// + /// * Single owner + /// 0. `[writable]` The account to close. + /// 1. '[writable]' The destination account. + /// 2. `[signer]` The account's owner. + /// + /// * Multisignature owner + /// 0. `[writable]` The account to close. + /// 1. '[writable]' The destination account. + /// 2. `[]` The account's multisignature owner. + /// 3. ..3+M '[signer]' M signer accounts. + CloseAccount, +} +impl TokenInstruction { + /// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html). + pub fn unpack(input: &[u8]) -> Result { + if input.len() < size_of::() { + return Err(TokenError::InvalidInstruction.into()); + } + Ok(match input[0] { + 0 => { + if input.len() < size_of::() + size_of::() + size_of::() { + return Err(TokenError::InvalidInstruction.into()); + } + #[allow(clippy::cast_ptr_alignment)] + let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; + let decimals = + unsafe { *(&input[size_of::() + size_of::()] as *const u8) }; + Self::InitializeMint { amount, decimals } + } + 1 => Self::InitializeAccount, + 2 => { + if input.len() < size_of::() + size_of::() { + return Err(TokenError::InvalidInstruction.into()); + } + #[allow(clippy::cast_ptr_alignment)] + let m = unsafe { *(&input[1] as *const u8) }; + Self::InitializeMultisig { m } + } + 3 => { + if input.len() < size_of::() + size_of::() { + return Err(TokenError::InvalidInstruction.into()); + } + #[allow(clippy::cast_ptr_alignment)] + let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; + Self::Transfer { amount } + } + 4 => { + if input.len() < size_of::() + size_of::() { + return Err(TokenError::InvalidInstruction.into()); + } + #[allow(clippy::cast_ptr_alignment)] + let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; + Self::Approve { amount } + } + 5 => Self::Revoke, + 6 => Self::SetOwner, + 7 => { + if input.len() < size_of::() + size_of::() { + return Err(TokenError::InvalidInstruction.into()); + } + #[allow(clippy::cast_ptr_alignment)] + let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; + Self::MintTo { amount } + } + 8 => { + if input.len() < size_of::() + size_of::() { + return Err(TokenError::InvalidInstruction.into()); + } + #[allow(clippy::cast_ptr_alignment)] + let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; + Self::Burn { amount } + } + 9 => Self::CloseAccount, + _ => return Err(TokenError::InvalidInstruction.into()), + }) + } + + /// Packs a [TokenInstruction](enum.TokenInstruction.html) into a byte buffer. + pub fn pack(self: &Self) -> Result, ProgramError> { + let mut output = vec![0u8; size_of::()]; + match self { + Self::InitializeMint { amount, decimals } => { + output[0] = 0; + #[allow(clippy::cast_ptr_alignment)] + let value = unsafe { &mut *(&mut output[size_of::()] as *mut u8 as *mut u64) }; + *value = *amount; + let value = + unsafe { &mut *(&mut output[size_of::() + size_of::()] as *mut u8) }; + *value = *decimals; + } + Self::InitializeAccount => output[0] = 1, + Self::InitializeMultisig { m } => { + output[0] = 2; + #[allow(clippy::cast_ptr_alignment)] + let value = unsafe { &mut *(&mut output[size_of::()] as *mut u8 as *mut u8) }; + *value = *m; + } + Self::Transfer { amount } => { + output[0] = 3; + #[allow(clippy::cast_ptr_alignment)] + let value = unsafe { &mut *(&mut output[size_of::()] as *mut u8 as *mut u64) }; + *value = *amount; + } + Self::Approve { amount } => { + output[0] = 4; + #[allow(clippy::cast_ptr_alignment)] + let value = unsafe { &mut *(&mut output[size_of::()] as *mut u8 as *mut u64) }; + *value = *amount; + } + Self::Revoke => output[0] = 5, + Self::SetOwner => output[0] = 6, + Self::MintTo { amount } => { + output[0] = 7; + #[allow(clippy::cast_ptr_alignment)] + let value = unsafe { &mut *(&mut output[size_of::()] as *mut u8 as *mut u64) }; + *value = *amount; + } + Self::Burn { amount } => { + output[0] = 8; + #[allow(clippy::cast_ptr_alignment)] + let value = unsafe { &mut *(&mut output[size_of::()] as *mut u8 as *mut u64) }; + *value = *amount; + } + Self::CloseAccount => output[0] = 9, + } + Ok(output) + } +} + +/// Creates a 'InitializeMint' instruction. +pub fn initialize_mint( + token_program_id: &Pubkey, + mint_pubkey: &Pubkey, + account_pubkey: Option<&Pubkey>, + owner_pubkey: Option<&Pubkey>, + amount: u64, + decimals: u8, +) -> Result { + let data = TokenInstruction::InitializeMint { amount, decimals }.pack()?; + + let mut accounts = vec![AccountMeta::new(*mint_pubkey, false)]; + if amount != 0 { + match account_pubkey { + Some(pubkey) => accounts.push(AccountMeta::new(*pubkey, false)), + None => { + return Err(ProgramError::NotEnoughAccountKeys); + } + } + } + match owner_pubkey { + Some(pubkey) => accounts.push(AccountMeta::new_readonly(*pubkey, false)), + None => { + if amount == 0 { + return Err(TokenError::OwnerRequiredIfNoInitialSupply.into()); + } + } + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates a `InitializeAccount` instruction. +pub fn initialize_account( + token_program_id: &Pubkey, + account_pubkey: &Pubkey, + mint_pubkey: &Pubkey, + owner_pubkey: &Pubkey, +) -> Result { + let data = TokenInstruction::InitializeAccount.pack()?; + + let accounts = vec![ + AccountMeta::new(*account_pubkey, false), + AccountMeta::new_readonly(*mint_pubkey, false), + AccountMeta::new_readonly(*owner_pubkey, false), + ]; + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates a `InitializeMultisig` instruction. +pub fn initialize_multisig( + token_program_id: &Pubkey, + multisig_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], + m: u8, +) -> Result { + if !is_valid_signer_index(m as usize) + || !is_valid_signer_index(signer_pubkeys.len()) + || m as usize > signer_pubkeys.len() + { + return Err(ProgramError::MissingRequiredSignature); + } + let data = TokenInstruction::InitializeMultisig { m }.pack()?; + + let mut accounts = Vec::with_capacity(1 + signer_pubkeys.len()); + accounts.push(AccountMeta::new(*multisig_pubkey, false)); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new_readonly(**signer_pubkey, false)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates a `Transfer` instruction. +pub fn transfer( + token_program_id: &Pubkey, + source_pubkey: &Pubkey, + destination_pubkey: &Pubkey, + authority_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], + amount: u64, +) -> Result { + let data = TokenInstruction::Transfer { amount }.pack()?; + + let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); + accounts.push(AccountMeta::new(*source_pubkey, false)); + accounts.push(AccountMeta::new(*destination_pubkey, false)); + accounts.push(AccountMeta::new_readonly( + *authority_pubkey, + signer_pubkeys.is_empty(), + )); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new(**signer_pubkey, true)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates an `Approve` instruction. +pub fn approve( + token_program_id: &Pubkey, + source_pubkey: &Pubkey, + delegate_pubkey: &Pubkey, + owner_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], + amount: u64, +) -> Result { + let data = TokenInstruction::Approve { amount }.pack()?; + + let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); + accounts.push(AccountMeta::new(*source_pubkey, false)); + accounts.push(AccountMeta::new_readonly(*delegate_pubkey, false)); + accounts.push(AccountMeta::new_readonly( + *owner_pubkey, + signer_pubkeys.is_empty(), + )); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new(**signer_pubkey, true)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates a `Revoke` instruction. +pub fn revoke( + token_program_id: &Pubkey, + source_pubkey: &Pubkey, + owner_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], +) -> Result { + let data = TokenInstruction::Revoke.pack()?; + + let mut accounts = Vec::with_capacity(2 + signer_pubkeys.len()); + accounts.push(AccountMeta::new_readonly(*source_pubkey, false)); + accounts.push(AccountMeta::new_readonly( + *owner_pubkey, + signer_pubkeys.is_empty(), + )); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new(**signer_pubkey, true)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates a `SetOwner` instruction. +pub fn set_owner( + token_program_id: &Pubkey, + owned_pubkey: &Pubkey, + new_owner_pubkey: &Pubkey, + owner_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], +) -> Result { + let data = TokenInstruction::SetOwner.pack()?; + + let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); + accounts.push(AccountMeta::new(*owned_pubkey, false)); + accounts.push(AccountMeta::new_readonly(*new_owner_pubkey, false)); + accounts.push(AccountMeta::new_readonly( + *owner_pubkey, + signer_pubkeys.is_empty(), + )); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new(**signer_pubkey, true)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates a `MintTo` instruction. +pub fn mint_to( + token_program_id: &Pubkey, + mint_pubkey: &Pubkey, + account_pubkey: &Pubkey, + owner_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], + amount: u64, +) -> Result { + let data = TokenInstruction::MintTo { amount }.pack()?; + + let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); + accounts.push(AccountMeta::new(*mint_pubkey, false)); + accounts.push(AccountMeta::new(*account_pubkey, false)); + accounts.push(AccountMeta::new_readonly( + *owner_pubkey, + signer_pubkeys.is_empty(), + )); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new(**signer_pubkey, true)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates a `Burn` instruction. +pub fn burn( + token_program_id: &Pubkey, + account_pubkey: &Pubkey, + authority_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], + amount: u64, +) -> Result { + let data = TokenInstruction::Burn { amount }.pack()?; + + let mut accounts = Vec::with_capacity(2 + signer_pubkeys.len()); + accounts.push(AccountMeta::new(*account_pubkey, false)); + accounts.push(AccountMeta::new_readonly( + *authority_pubkey, + signer_pubkeys.is_empty(), + )); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new(**signer_pubkey, true)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates a `CloseAccount` instruction. +pub fn close_account( + token_program_id: &Pubkey, + account_pubkey: &Pubkey, + destination_pubkey: &Pubkey, + owner_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], +) -> Result { + let data = TokenInstruction::CloseAccount.pack()?; + + let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); + accounts.push(AccountMeta::new(*account_pubkey, false)); + accounts.push(AccountMeta::new(*destination_pubkey, false)); + accounts.push(AccountMeta::new_readonly( + *owner_pubkey, + signer_pubkeys.is_empty(), + )); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new(**signer_pubkey, true)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Utility function that checks index is between MIN_SIGNERS and MAX_SIGNERS +pub fn is_valid_signer_index(index: usize) -> bool { + !(index < MIN_SIGNERS || index > MAX_SIGNERS) +} diff --git a/program/src/lib.rs b/program/src/lib.rs new file mode 100644 index 0000000..bc39c05 --- /dev/null +++ b/program/src/lib.rs @@ -0,0 +1,28 @@ +#![deny(missing_docs)] + +//! An ERC20-like Token program for the Solana blockchain + +pub mod entrypoint; +pub mod error; +pub mod instruction; +pub mod native_mint; +pub mod option; +pub mod processor; +pub mod state; + +// Export current solana-sdk types for downstream users who may also be building with a different +// solana-sdk version +pub use solana_sdk; + +/// Convert the UI representation of a token amount (using the decimals field defined in its mint) +/// to the raw amount +pub fn ui_amount_to_amount(ui_amount: f64, decimals: u8) -> u64 { + (ui_amount * 10_usize.pow(decimals as u32) as f64) as u64 +} + +/// Convert a raw amount to its UI representation (using the decimals field defined in its mint) +pub fn amount_to_ui_amount(amount: u64, decimals: u8) -> f64 { + amount as f64 / 10_usize.pow(decimals as u32) as f64 +} + +solana_sdk::declare_id!("TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o"); diff --git a/program/src/native_mint.rs b/program/src/native_mint.rs new file mode 100644 index 0000000..6e80b28 --- /dev/null +++ b/program/src/native_mint.rs @@ -0,0 +1,25 @@ +//! The Mint that represents the native token + +/// There are 10^9 lamports in one SOL +pub const DECIMALS: u8 = 9; + +// The Mint for native SOL Token accounts +solana_sdk::declare_id!("So11111111111111111111111111111111111111111"); + +#[cfg(test)] +mod tests { + use super::*; + use solana_sdk::native_token::*; + + #[test] + fn test_decimals() { + assert_eq!( + lamports_to_sol(42), + crate::amount_to_ui_amount(42, DECIMALS) + ); + assert_eq!( + sol_to_lamports(42.), + crate::ui_amount_to_amount(42., DECIMALS) + ); + } +} diff --git a/program/src/option.rs b/program/src/option.rs new file mode 100644 index 0000000..914bb51 --- /dev/null +++ b/program/src/option.rs @@ -0,0 +1,965 @@ +//! A C representation of Rust's `std::option::Option` used accross the FFI +//! boundary for Solana program interfaces +//! +//! This implementation mostly matches `std::option` except iterators since the iteration +//! trait requires returning `std::option::Option` + +use std::pin::Pin; +use std::{ + convert, hint, mem, + ops::{Deref, DerefMut}, +}; + +/// A C representation of Rust's `std::option::Option` +#[repr(C)] +#[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] +pub enum COption { + /// No value + None, + /// Some value `T` + Some(T), +} + +///////////////////////////////////////////////////////////////////////////// +// Type implementation +///////////////////////////////////////////////////////////////////////////// + +impl COption { + ///////////////////////////////////////////////////////////////////////// + // Querying the contained values + ///////////////////////////////////////////////////////////////////////// + + /// Returns `true` if the option is a [`COption::Some`] value. + /// + /// # Examples + /// + /// ```ignore + /// let x: COption = COption::Some(2); + /// assert_eq!(x.is_some(), true); + /// + /// let x: COption = COption::None; + /// assert_eq!(x.is_some(), false); + /// ``` + /// + /// [`COption::Some`]: #variant.COption::Some + #[must_use = "if you intended to assert that this has a value, consider `.unwrap()` instead"] + #[inline] + pub fn is_some(&self) -> bool { + match *self { + COption::Some(_) => true, + COption::None => false, + } + } + + /// Returns `true` if the option is a [`COption::None`] value. + /// + /// # Examples + /// + /// ```ignore + /// let x: COption = COption::Some(2); + /// assert_eq!(x.is_none(), false); + /// + /// let x: COption = COption::None; + /// assert_eq!(x.is_none(), true); + /// ``` + /// + /// [`COption::None`]: #variant.COption::None + #[must_use = "if you intended to assert that this doesn't have a value, consider \ + `.and_then(|| panic!(\"`COption` had a value when expected `COption::None`\"))` instead"] + #[inline] + pub fn is_none(&self) -> bool { + !self.is_some() + } + + /// Returns `true` if the option is a [`COption::Some`] value containing the given value. + /// + /// # Examples + /// + /// ```ignore + /// #![feature(option_result_contains)] + /// + /// let x: COption = COption::Some(2); + /// assert_eq!(x.contains(&2), true); + /// + /// let x: COption = COption::Some(3); + /// assert_eq!(x.contains(&2), false); + /// + /// let x: COption = COption::None; + /// assert_eq!(x.contains(&2), false); + /// ``` + #[must_use] + #[inline] + pub fn contains(&self, x: &U) -> bool + where + U: PartialEq, + { + match self { + COption::Some(y) => x == y, + COption::None => false, + } + } + + ///////////////////////////////////////////////////////////////////////// + // Adapter for working with references + ///////////////////////////////////////////////////////////////////////// + + /// Converts from `&COption` to `COption<&T>`. + /// + /// # Examples + /// + /// Converts an `COption<`[`String`]`>` into an `COption<`[`usize`]`>`, preserving the original. + /// The [`map`] method takes the `self` argument by value, consuming the original, + /// so this technique uses `as_ref` to first take an `COption` to a reference + /// to the value inside the original. + /// + /// [`map`]: enum.COption.html#method.map + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ```ignore + /// let text: COption = COption::Some("Hello, world!".to_string()); + /// // First, cast `COption` to `COption<&String>` with `as_ref`, + /// // then consume *that* with `map`, leaving `text` on the stack. + /// let text_length: COption = text.as_ref().map(|s| s.len()); + /// println!("still can print text: {:?}", text); + /// ``` + #[inline] + pub fn as_ref(&self) -> COption<&T> { + match *self { + COption::Some(ref x) => COption::Some(x), + COption::None => COption::None, + } + } + + /// Converts from `&mut COption` to `COption<&mut T>`. + /// + /// # Examples + /// + /// ```ignore + /// let mut x = COption::Some(2); + /// match x.as_mut() { + /// COption::Some(v) => *v = 42, + /// COption::None => {}, + /// } + /// assert_eq!(x, COption::Some(42)); + /// ``` + #[inline] + pub fn as_mut(&mut self) -> COption<&mut T> { + match *self { + COption::Some(ref mut x) => COption::Some(x), + COption::None => COption::None, + } + } + + /// Converts from [`Pin`]`<&COption>` to `COption<`[`Pin`]`<&T>>`. + /// + /// [`Pin`]: ../pin/struct.Pin.html + #[inline] + #[allow(clippy::wrong_self_convention)] + pub fn as_pin_ref(self: Pin<&Self>) -> COption> { + unsafe { Pin::get_ref(self).as_ref().map(|x| Pin::new_unchecked(x)) } + } + + /// Converts from [`Pin`]`<&mut COption>` to `COption<`[`Pin`]`<&mut T>>`. + /// + /// [`Pin`]: ../pin/struct.Pin.html + #[inline] + #[allow(clippy::wrong_self_convention)] + pub fn as_pin_mut(self: Pin<&mut Self>) -> COption> { + unsafe { + Pin::get_unchecked_mut(self) + .as_mut() + .map(|x| Pin::new_unchecked(x)) + } + } + + ///////////////////////////////////////////////////////////////////////// + // Getting to contained values + ///////////////////////////////////////////////////////////////////////// + + /// Unwraps an option, yielding the content of a [`COption::Some`]. + /// + /// # Panics + /// + /// Panics if the value is a [`COption::None`] with a custom panic message provided by + /// `msg`. + /// + /// [`COption::Some`]: #variant.COption::Some + /// [`COption::None`]: #variant.COption::None + /// + /// # Examples + /// + /// ```ignore + /// let x = COption::Some("value"); + /// assert_eq!(x.expect("the world is ending"), "value"); + /// ``` + /// + /// ```ignore{.should_panic} + /// let x: COption<&str> = COption::None; + /// x.expect("the world is ending"); // panics with `the world is ending` + /// ``` + #[inline] + pub fn expect(self, msg: &str) -> T { + match self { + COption::Some(val) => val, + COption::None => expect_failed(msg), + } + } + + /// Moves the value `v` out of the `COption` if it is [`COption::Some(v)`]. + /// + /// In general, because this function may panic, its use is discouraged. + /// Instead, prefer to use pattern matching and handle the [`COption::None`] + /// case explicitly. + /// + /// # Panics + /// + /// Panics if the self value equals [`COption::None`]. + /// + /// [`COption::Some(v)`]: #variant.COption::Some + /// [`COption::None`]: #variant.COption::None + /// + /// # Examples + /// + /// ```ignore + /// let x = COption::Some("air"); + /// assert_eq!(x.unwrap(), "air"); + /// ``` + /// + /// ```ignore{.should_panic} + /// let x: COption<&str> = COption::None; + /// assert_eq!(x.unwrap(), "air"); // fails + /// ``` + #[inline] + pub fn unwrap(self) -> T { + match self { + COption::Some(val) => val, + COption::None => panic!("called `COption::unwrap()` on a `COption::None` value"), + } + } + + /// Returns the contained value or a default. + /// + /// Arguments passed to `unwrap_or` are eagerly evaluated; if you are passing + /// the result of a function call, it is recommended to use [`unwrap_or_else`], + /// which is lazily evaluated. + /// + /// [`unwrap_or_else`]: #method.unwrap_or_else + /// + /// # Examples + /// + /// ```ignore + /// assert_eq!(COption::Some("car").unwrap_or("bike"), "car"); + /// assert_eq!(COption::None.unwrap_or("bike"), "bike"); + /// ``` + #[inline] + pub fn unwrap_or(self, def: T) -> T { + match self { + COption::Some(x) => x, + COption::None => def, + } + } + + /// Returns the contained value or computes it from a closure. + /// + /// # Examples + /// + /// ```ignore + /// let k = 10; + /// assert_eq!(COption::Some(4).unwrap_or_else(|| 2 * k), 4); + /// assert_eq!(COption::None.unwrap_or_else(|| 2 * k), 20); + /// ``` + #[inline] + pub fn unwrap_or_else T>(self, f: F) -> T { + match self { + COption::Some(x) => x, + COption::None => f(), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Transforming contained values + ///////////////////////////////////////////////////////////////////////// + + /// Maps an `COption` to `COption` by applying a function to a contained value. + /// + /// # Examples + /// + /// Converts an `COption<`[`String`]`>` into an `COption<`[`usize`]`>`, consuming the original: + /// + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ```ignore + /// let maybe_some_string = COption::Some(String::from("Hello, World!")); + /// // `COption::map` takes self *by value*, consuming `maybe_some_string` + /// let maybe_some_len = maybe_some_string.map(|s| s.len()); + /// + /// assert_eq!(maybe_some_len, COption::Some(13)); + /// ``` + #[inline] + pub fn map U>(self, f: F) -> COption { + match self { + COption::Some(x) => COption::Some(f(x)), + COption::None => COption::None, + } + } + + /// Applies a function to the contained value (if any), + /// or returns the provided default (if not). + /// + /// # Examples + /// + /// ```ignore + /// let x = COption::Some("foo"); + /// assert_eq!(x.map_or(42, |v| v.len()), 3); + /// + /// let x: COption<&str> = COption::None; + /// assert_eq!(x.map_or(42, |v| v.len()), 42); + /// ``` + #[inline] + pub fn map_or U>(self, default: U, f: F) -> U { + match self { + COption::Some(t) => f(t), + COption::None => default, + } + } + + /// Applies a function to the contained value (if any), + /// or computes a default (if not). + /// + /// # Examples + /// + /// ```ignore + /// let k = 21; + /// + /// let x = COption::Some("foo"); + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3); + /// + /// let x: COption<&str> = COption::None; + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42); + /// ``` + #[inline] + pub fn map_or_else U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U { + match self { + COption::Some(t) => f(t), + COption::None => default(), + } + } + + /// Transforms the `COption` into a [`Result`], mapping [`COption::Some(v)`] to + /// [`Ok(v)`] and [`COption::None`] to [`Err(err)`]. + /// + /// Arguments passed to `ok_or` are eagerly evaluated; if you are passing the + /// result of a function call, it is recommended to use [`ok_or_else`], which is + /// lazily evaluated. + /// + /// [`Result`]: ../../std/result/enum.Result.html + /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok + /// [`Err(err)`]: ../../std/result/enum.Result.html#variant.Err + /// [`COption::None`]: #variant.COption::None + /// [`COption::Some(v)`]: #variant.COption::Some + /// [`ok_or_else`]: #method.ok_or_else + /// + /// # Examples + /// + /// ```ignore + /// let x = COption::Some("foo"); + /// assert_eq!(x.ok_or(0), Ok("foo")); + /// + /// let x: COption<&str> = COption::None; + /// assert_eq!(x.ok_or(0), Err(0)); + /// ``` + #[inline] + pub fn ok_or(self, err: E) -> Result { + match self { + COption::Some(v) => Ok(v), + COption::None => Err(err), + } + } + + /// Transforms the `COption` into a [`Result`], mapping [`COption::Some(v)`] to + /// [`Ok(v)`] and [`COption::None`] to [`Err(err())`]. + /// + /// [`Result`]: ../../std/result/enum.Result.html + /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok + /// [`Err(err())`]: ../../std/result/enum.Result.html#variant.Err + /// [`COption::None`]: #variant.COption::None + /// [`COption::Some(v)`]: #variant.COption::Some + /// + /// # Examples + /// + /// ```ignore + /// let x = COption::Some("foo"); + /// assert_eq!(x.ok_or_else(|| 0), Ok("foo")); + /// + /// let x: COption<&str> = COption::None; + /// assert_eq!(x.ok_or_else(|| 0), Err(0)); + /// ``` + #[inline] + pub fn ok_or_else E>(self, err: F) -> Result { + match self { + COption::Some(v) => Ok(v), + COption::None => Err(err()), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Boolean operations on the values, eager and lazy + ///////////////////////////////////////////////////////////////////////// + + /// Returns [`COption::None`] if the option is [`COption::None`], otherwise returns `optb`. + /// + /// [`COption::None`]: #variant.COption::None + /// + /// # Examples + /// + /// ```ignore + /// let x = COption::Some(2); + /// let y: COption<&str> = COption::None; + /// assert_eq!(x.and(y), COption::None); + /// + /// let x: COption = COption::None; + /// let y = COption::Some("foo"); + /// assert_eq!(x.and(y), COption::None); + /// + /// let x = COption::Some(2); + /// let y = COption::Some("foo"); + /// assert_eq!(x.and(y), COption::Some("foo")); + /// + /// let x: COption = COption::None; + /// let y: COption<&str> = COption::None; + /// assert_eq!(x.and(y), COption::None); + /// ``` + #[inline] + pub fn and(self, optb: COption) -> COption { + match self { + COption::Some(_) => optb, + COption::None => COption::None, + } + } + + /// Returns [`COption::None`] if the option is [`COption::None`], otherwise calls `f` with the + /// wrapped value and returns the result. + /// + /// COption::Some languages call this operation flatmap. + /// + /// [`COption::None`]: #variant.COption::None + /// + /// # Examples + /// + /// ```ignore + /// fn sq(x: u32) -> COption { COption::Some(x * x) } + /// fn nope(_: u32) -> COption { COption::None } + /// + /// assert_eq!(COption::Some(2).and_then(sq).and_then(sq), COption::Some(16)); + /// assert_eq!(COption::Some(2).and_then(sq).and_then(nope), COption::None); + /// assert_eq!(COption::Some(2).and_then(nope).and_then(sq), COption::None); + /// assert_eq!(COption::None.and_then(sq).and_then(sq), COption::None); + /// ``` + #[inline] + pub fn and_then COption>(self, f: F) -> COption { + match self { + COption::Some(x) => f(x), + COption::None => COption::None, + } + } + + /// Returns [`COption::None`] if the option is [`COption::None`], otherwise calls `predicate` + /// with the wrapped value and returns: + /// + /// - [`COption::Some(t)`] if `predicate` returns `true` (where `t` is the wrapped + /// value), and + /// - [`COption::None`] if `predicate` returns `false`. + /// + /// This function works similar to [`Iterator::filter()`]. You can imagine + /// the `COption` being an iterator over one or zero elements. `filter()` + /// lets you decide which elements to keep. + /// + /// # Examples + /// + /// ```ignore + /// fn is_even(n: &i32) -> bool { + /// n % 2 == 0 + /// } + /// + /// assert_eq!(COption::None.filter(is_even), COption::None); + /// assert_eq!(COption::Some(3).filter(is_even), COption::None); + /// assert_eq!(COption::Some(4).filter(is_even), COption::Some(4)); + /// ``` + /// + /// [`COption::None`]: #variant.COption::None + /// [`COption::Some(t)`]: #variant.COption::Some + /// [`Iterator::filter()`]: ../../std/iter/trait.Iterator.html#method.filter + #[inline] + pub fn filter bool>(self, predicate: P) -> Self { + if let COption::Some(x) = self { + if predicate(&x) { + return COption::Some(x); + } + } + COption::None + } + + /// Returns the option if it contains a value, otherwise returns `optb`. + /// + /// Arguments passed to `or` are eagerly evaluated; if you are passing the + /// result of a function call, it is recommended to use [`or_else`], which is + /// lazily evaluated. + /// + /// [`or_else`]: #method.or_else + /// + /// # Examples + /// + /// ```ignore + /// let x = COption::Some(2); + /// let y = COption::None; + /// assert_eq!(x.or(y), COption::Some(2)); + /// + /// let x = COption::None; + /// let y = COption::Some(100); + /// assert_eq!(x.or(y), COption::Some(100)); + /// + /// let x = COption::Some(2); + /// let y = COption::Some(100); + /// assert_eq!(x.or(y), COption::Some(2)); + /// + /// let x: COption = COption::None; + /// let y = COption::None; + /// assert_eq!(x.or(y), COption::None); + /// ``` + #[inline] + pub fn or(self, optb: COption) -> COption { + match self { + COption::Some(_) => self, + COption::None => optb, + } + } + + /// Returns the option if it contains a value, otherwise calls `f` and + /// returns the result. + /// + /// # Examples + /// + /// ```ignore + /// fn nobody() -> COption<&'static str> { COption::None } + /// fn vikings() -> COption<&'static str> { COption::Some("vikings") } + /// + /// assert_eq!(COption::Some("barbarians").or_else(vikings), COption::Some("barbarians")); + /// assert_eq!(COption::None.or_else(vikings), COption::Some("vikings")); + /// assert_eq!(COption::None.or_else(nobody), COption::None); + /// ``` + #[inline] + pub fn or_else COption>(self, f: F) -> COption { + match self { + COption::Some(_) => self, + COption::None => f(), + } + } + + /// Returns [`COption::Some`] if exactly one of `self`, `optb` is [`COption::Some`], otherwise returns [`COption::None`]. + /// + /// [`COption::Some`]: #variant.COption::Some + /// [`COption::None`]: #variant.COption::None + /// + /// # Examples + /// + /// ```ignore + /// let x = COption::Some(2); + /// let y: COption = COption::None; + /// assert_eq!(x.xor(y), COption::Some(2)); + /// + /// let x: COption = COption::None; + /// let y = COption::Some(2); + /// assert_eq!(x.xor(y), COption::Some(2)); + /// + /// let x = COption::Some(2); + /// let y = COption::Some(2); + /// assert_eq!(x.xor(y), COption::None); + /// + /// let x: COption = COption::None; + /// let y: COption = COption::None; + /// assert_eq!(x.xor(y), COption::None); + /// ``` + #[inline] + pub fn xor(self, optb: COption) -> COption { + match (self, optb) { + (COption::Some(a), COption::None) => COption::Some(a), + (COption::None, COption::Some(b)) => COption::Some(b), + _ => COption::None, + } + } + + ///////////////////////////////////////////////////////////////////////// + // Entry-like operations to insert if COption::None and return a reference + ///////////////////////////////////////////////////////////////////////// + + /// Inserts `v` into the option if it is [`COption::None`], then + /// returns a mutable reference to the contained value. + /// + /// [`COption::None`]: #variant.COption::None + /// + /// # Examples + /// + /// ```ignore + /// let mut x = COption::None; + /// + /// { + /// let y: &mut u32 = x.get_or_insert(5); + /// assert_eq!(y, &5); + /// + /// *y = 7; + /// } + /// + /// assert_eq!(x, COption::Some(7)); + /// ``` + #[inline] + pub fn get_or_insert(&mut self, v: T) -> &mut T { + self.get_or_insert_with(|| v) + } + + /// Inserts a value computed from `f` into the option if it is [`COption::None`], then + /// returns a mutable reference to the contained value. + /// + /// [`COption::None`]: #variant.COption::None + /// + /// # Examples + /// + /// ```ignore + /// let mut x = COption::None; + /// + /// { + /// let y: &mut u32 = x.get_or_insert_with(|| 5); + /// assert_eq!(y, &5); + /// + /// *y = 7; + /// } + /// + /// assert_eq!(x, COption::Some(7)); + /// ``` + #[inline] + pub fn get_or_insert_with T>(&mut self, f: F) -> &mut T { + if let COption::None = *self { + *self = COption::Some(f()) + } + + match *self { + COption::Some(ref mut v) => v, + COption::None => unsafe { hint::unreachable_unchecked() }, + } + } + + ///////////////////////////////////////////////////////////////////////// + // Misc + ///////////////////////////////////////////////////////////////////////// + + /// Replaces the actual value in the option by the value given in parameter, + /// returning the old value if present, + /// leaving a [`COption::Some`] in its place without deinitializing either one. + /// + /// [`COption::Some`]: #variant.COption::Some + /// + /// # Examples + /// + /// ```ignore + /// let mut x = COption::Some(2); + /// let old = x.replace(5); + /// assert_eq!(x, COption::Some(5)); + /// assert_eq!(old, COption::Some(2)); + /// + /// let mut x = COption::None; + /// let old = x.replace(3); + /// assert_eq!(x, COption::Some(3)); + /// assert_eq!(old, COption::None); + /// ``` + #[inline] + pub fn replace(&mut self, value: T) -> COption { + mem::replace(self, COption::Some(value)) + } +} + +impl COption<&T> { + /// Maps an `COption<&T>` to an `COption` by copying the contents of the + /// option. + /// + /// # Examples + /// + /// ```ignore + /// let x = 12; + /// let opt_x = COption::Some(&x); + /// assert_eq!(opt_x, COption::Some(&12)); + /// let copied = opt_x.copied(); + /// assert_eq!(copied, COption::Some(12)); + /// ``` + pub fn copied(self) -> COption { + self.map(|&t| t) + } +} + +impl COption<&mut T> { + /// Maps an `COption<&mut T>` to an `COption` by copying the contents of the + /// option. + /// + /// # Examples + /// + /// ```ignore + /// let mut x = 12; + /// let opt_x = COption::Some(&mut x); + /// assert_eq!(opt_x, COption::Some(&mut 12)); + /// let copied = opt_x.copied(); + /// assert_eq!(copied, COption::Some(12)); + /// ``` + pub fn copied(self) -> COption { + self.map(|&mut t| t) + } +} + +impl COption<&T> { + /// Maps an `COption<&T>` to an `COption` by cloning the contents of the + /// option. + /// + /// # Examples + /// + /// ```ignore + /// let x = 12; + /// let opt_x = COption::Some(&x); + /// assert_eq!(opt_x, COption::Some(&12)); + /// let cloned = opt_x.cloned(); + /// assert_eq!(cloned, COption::Some(12)); + /// ``` + pub fn cloned(self) -> COption { + self.map(|t| t.clone()) + } +} + +impl COption<&mut T> { + /// Maps an `COption<&mut T>` to an `COption` by cloning the contents of the + /// option. + /// + /// # Examples + /// + /// ```ignore + /// let mut x = 12; + /// let opt_x = COption::Some(&mut x); + /// assert_eq!(opt_x, COption::Some(&mut 12)); + /// let cloned = opt_x.cloned(); + /// assert_eq!(cloned, COption::Some(12)); + /// ``` + pub fn cloned(self) -> COption { + self.map(|t| t.clone()) + } +} + +impl COption { + /// Returns the contained value or a default + /// + /// Consumes the `self` argument then, if [`COption::Some`], returns the contained + /// value, otherwise if [`COption::None`], returns the [default value] for that + /// type. + /// + /// # Examples + /// + /// Converts a string to an integer, turning poorly-formed strings + /// into 0 (the default value for integers). [`parse`] converts + /// a string to any other type that implements [`FromStr`], returning + /// [`COption::None`] on error. + /// + /// ```ignore + /// let good_year_from_input = "1909"; + /// let bad_year_from_input = "190blarg"; + /// let good_year = good_year_from_input.parse().ok().unwrap_or_default(); + /// let bad_year = bad_year_from_input.parse().ok().unwrap_or_default(); + /// + /// assert_eq!(1909, good_year); + /// assert_eq!(0, bad_year); + /// ``` + /// + /// [`COption::Some`]: #variant.COption::Some + /// [`COption::None`]: #variant.COption::None + /// [default value]: ../default/trait.Default.html#tymethod.default + /// [`parse`]: ../../std/primitive.str.html#method.parse + /// [`FromStr`]: ../../std/str/trait.FromStr.html + #[inline] + pub fn unwrap_or_default(self) -> T { + match self { + COption::Some(x) => x, + COption::None => Default::default(), + } + } +} + +impl COption { + /// Converts from `COption` (or `&COption`) to `COption<&T::Target>`. + /// + /// Leaves the original COption in-place, creating a new one with a reference + /// to the original one, additionally coercing the contents via [`Deref`]. + /// + /// [`Deref`]: ../../std/ops/trait.Deref.html + /// + /// # Examples + /// + /// ```ignore + /// #![feature(inner_deref)] + /// + /// let x: COption = COption::Some("hey".to_owned()); + /// assert_eq!(x.as_deref(), COption::Some("hey")); + /// + /// let x: COption = COption::None; + /// assert_eq!(x.as_deref(), COption::None); + /// ``` + pub fn as_deref(&self) -> COption<&T::Target> { + self.as_ref().map(|t| t.deref()) + } +} + +impl COption { + /// Converts from `COption` (or `&mut COption`) to `COption<&mut T::Target>`. + /// + /// Leaves the original `COption` in-place, creating a new one containing a mutable reference to + /// the inner type's `Deref::Target` type. + /// + /// # Examples + /// + /// ```ignore + /// #![feature(inner_deref)] + /// + /// let mut x: COption = COption::Some("hey".to_owned()); + /// assert_eq!(x.as_deref_mut().map(|x| { + /// x.make_ascii_uppercase(); + /// x + /// }), COption::Some("HEY".to_owned().as_mut_str())); + /// ``` + pub fn as_deref_mut(&mut self) -> COption<&mut T::Target> { + self.as_mut().map(|t| t.deref_mut()) + } +} + +impl COption> { + /// Transposes an `COption` of a [`Result`] into a [`Result`] of an `COption`. + /// + /// [`COption::None`] will be mapped to [`Ok`]`(`[`COption::None`]`)`. + /// [`COption::Some`]`(`[`Ok`]`(_))` and [`COption::Some`]`(`[`Err`]`(_))` will be mapped to + /// [`Ok`]`(`[`COption::Some`]`(_))` and [`Err`]`(_)`. + /// + /// [`COption::None`]: #variant.COption::None + /// [`Ok`]: ../../std/result/enum.Result.html#variant.Ok + /// [`COption::Some`]: #variant.COption::Some + /// [`Err`]: ../../std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// ```ignore + /// #[derive(Debug, Eq, PartialEq)] + /// struct COption::SomeErr; + /// + /// let x: Result, COption::SomeErr> = Ok(COption::Some(5)); + /// let y: COption> = COption::Some(Ok(5)); + /// assert_eq!(x, y.transpose()); + /// ``` + #[inline] + pub fn transpose(self) -> Result, E> { + match self { + COption::Some(Ok(x)) => Ok(COption::Some(x)), + COption::Some(Err(e)) => Err(e), + COption::None => Ok(COption::None), + } + } +} + +// This is a separate function to reduce the code size of .expect() itself. +#[inline(never)] +#[cold] +fn expect_failed(msg: &str) -> ! { + panic!("{}", msg) +} + +// // This is a separate function to reduce the code size of .expect_none() itself. +// #[inline(never)] +// #[cold] +// fn expect_none_failed(msg: &str, value: &dyn fmt::Debug) -> ! { +// panic!("{}: {:?}", msg, value) +// } + +///////////////////////////////////////////////////////////////////////////// +// Trait implementations +///////////////////////////////////////////////////////////////////////////// + +impl Clone for COption { + #[inline] + fn clone(&self) -> Self { + match self { + COption::Some(x) => COption::Some(x.clone()), + COption::None => COption::None, + } + } + + #[inline] + fn clone_from(&mut self, source: &Self) { + match (self, source) { + (COption::Some(to), COption::Some(from)) => to.clone_from(from), + (to, from) => *to = from.clone(), + } + } +} + +impl Default for COption { + /// Returns [`COption::None`][COption::COption::None]. + /// + /// # Examples + /// + /// ```ignore + /// let opt: COption = COption::default(); + /// assert!(opt.is_none()); + /// ``` + #[inline] + fn default() -> COption { + COption::None + } +} + +impl From for COption { + fn from(val: T) -> COption { + COption::Some(val) + } +} + +impl<'a, T> From<&'a COption> for COption<&'a T> { + fn from(o: &'a COption) -> COption<&'a T> { + o.as_ref() + } +} + +impl<'a, T> From<&'a mut COption> for COption<&'a mut T> { + fn from(o: &'a mut COption) -> COption<&'a mut T> { + o.as_mut() + } +} + +impl COption> { + /// Converts from `COption>` to `COption` + /// + /// # Examples + /// Basic usage: + /// ```ignore + /// #![feature(option_flattening)] + /// let x: COption> = COption::Some(COption::Some(6)); + /// assert_eq!(COption::Some(6), x.flatten()); + /// + /// let x: COption> = COption::Some(COption::None); + /// assert_eq!(COption::None, x.flatten()); + /// + /// let x: COption> = COption::None; + /// assert_eq!(COption::None, x.flatten()); + /// ``` + /// Flattening once only removes one level of nesting: + /// ```ignore + /// #![feature(option_flattening)] + /// let x: COption>> = COption::Some(COption::Some(COption::Some(6))); + /// assert_eq!(COption::Some(COption::Some(6)), x.flatten()); + /// assert_eq!(COption::Some(6), x.flatten().flatten()); + /// ``` + #[inline] + pub fn flatten(self) -> COption { + self.and_then(convert::identity) + } +} diff --git a/program/src/processor.rs b/program/src/processor.rs new file mode 100644 index 0000000..c50d474 --- /dev/null +++ b/program/src/processor.rs @@ -0,0 +1,2232 @@ +//! Program state processor + +#![cfg(feature = "program")] + +use crate::{ + error::TokenError, + instruction::{is_valid_signer_index, TokenInstruction}, + option::COption, + state::{self, Account, Mint, Multisig}, +}; +use num_traits::FromPrimitive; +use solana_sdk::{ + account_info::{next_account_info, AccountInfo}, + decode_error::DecodeError, + entrypoint::ProgramResult, + info, + program_error::{PrintProgramError, ProgramError}, + pubkey::Pubkey, +}; +use std::mem::size_of; + +/// Program state handler. +pub struct Processor {} +impl Processor { + /// Processes an [InitializeMint](enum.TokenInstruction.html) instruction. + pub fn process_initialize_mint( + accounts: &[AccountInfo], + amount: u64, + decimals: u8, + ) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let mint_info = next_account_info(account_info_iter)?; + + let mut mint_info_data = mint_info.data.borrow_mut(); + let mut mint: &mut Mint = state::unpack_unchecked(&mut mint_info_data)?; + if mint.is_initialized { + return Err(TokenError::AlreadyInUse.into()); + } + + let owner = if amount != 0 { + let dest_account_info = next_account_info(account_info_iter)?; + let mut dest_account_data = dest_account_info.data.borrow_mut(); + let mut dest_account: &mut Account = state::unpack(&mut dest_account_data)?; + + if mint_info.key != &dest_account.mint { + return Err(TokenError::MintMismatch.into()); + } + + dest_account.amount = amount; + + if let Ok(owner_info) = next_account_info(account_info_iter) { + COption::Some(*owner_info.key) + } else { + COption::None + } + } else if let Ok(owner_info) = next_account_info(account_info_iter) { + COption::Some(*owner_info.key) + } else { + return Err(TokenError::OwnerRequiredIfNoInitialSupply.into()); + }; + + mint.owner = owner; + mint.decimals = decimals; + mint.is_initialized = true; + + Ok(()) + } + + /// Processes an [InitializeAccount](enum.TokenInstruction.html) instruction. + pub fn process_initialize_account(accounts: &[AccountInfo]) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let new_account_info = next_account_info(account_info_iter)?; + let mint_info = next_account_info(account_info_iter)?; + let owner_info = next_account_info(account_info_iter)?; + + let mut new_account_data = new_account_info.data.borrow_mut(); + let mut account: &mut Account = state::unpack_unchecked(&mut new_account_data)?; + if account.is_initialized { + return Err(TokenError::AlreadyInUse.into()); + } + + account.mint = *mint_info.key; + account.owner = *owner_info.key; + account.delegate = COption::None; + account.delegated_amount = 0; + account.is_initialized = true; + if *mint_info.key == crate::native_mint::id() { + account.is_native = true; + account.amount = new_account_info.lamports(); + } else { + account.is_native = false; + account.amount = 0; + }; + + Ok(()) + } + + /// Processes a [InitializeMultisig](enum.TokenInstruction.html) instruction. + pub fn process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let multisig_info = next_account_info(account_info_iter)?; + let mut multisig_account_data = multisig_info.data.borrow_mut(); + let mut multisig: &mut Multisig = state::unpack_unchecked(&mut multisig_account_data)?; + if multisig.is_initialized { + return Err(TokenError::AlreadyInUse.into()); + } + + let signer_infos = account_info_iter.as_slice(); + multisig.m = m; + multisig.n = signer_infos.len() as u8; + if !is_valid_signer_index(multisig.n as usize) { + return Err(TokenError::InvalidNumberOfProvidedSigners.into()); + } + if !is_valid_signer_index(multisig.m as usize) { + return Err(TokenError::InvalidNumberOfRequiredSigners.into()); + } + for (i, signer_info) in signer_infos.iter().enumerate() { + multisig.signers[i] = *signer_info.key; + } + multisig.is_initialized = true; + + Ok(()) + } + + /// Processes a [Transfer](enum.TokenInstruction.html) instruction. + pub fn process_transfer( + program_id: &Pubkey, + accounts: &[AccountInfo], + amount: u64, + ) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let source_account_info = next_account_info(account_info_iter)?; + let dest_account_info = next_account_info(account_info_iter)?; + let authority_info = next_account_info(account_info_iter)?; + + if source_account_info.key == dest_account_info.key { + return Ok(()); + } + + let mut source_data = source_account_info.data.borrow_mut(); + let mut source_account: &mut Account = state::unpack(&mut source_data)?; + let mut dest_data = dest_account_info.data.borrow_mut(); + let mut dest_account: &mut Account = state::unpack(&mut dest_data)?; + + if source_account.amount < amount { + return Err(TokenError::InsufficientFunds.into()); + } + if source_account.mint != dest_account.mint { + return Err(TokenError::MintMismatch.into()); + } + + match source_account.delegate { + COption::Some(ref delegate) if authority_info.key == delegate => { + Self::validate_owner( + program_id, + delegate, + authority_info, + account_info_iter.as_slice(), + )?; + if source_account.delegated_amount < amount { + return Err(TokenError::InsufficientFunds.into()); + } + source_account.delegated_amount -= amount; + if source_account.delegated_amount == 0 { + source_account.delegate = COption::None; + } + } + _ => Self::validate_owner( + program_id, + &source_account.owner, + authority_info, + account_info_iter.as_slice(), + )?, + }; + + source_account.amount -= amount; + dest_account.amount += amount; + + if source_account.is_native { + **source_account_info.lamports.borrow_mut() -= amount; + **dest_account_info.lamports.borrow_mut() += amount; + } + + Ok(()) + } + + /// Processes an [Approve](enum.TokenInstruction.html) instruction. + pub fn process_approve( + program_id: &Pubkey, + accounts: &[AccountInfo], + amount: u64, + ) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let source_account_info = next_account_info(account_info_iter)?; + + let mut source_data = source_account_info.data.borrow_mut(); + let mut source_account: &mut Account = state::unpack(&mut source_data)?; + let delegate_info = next_account_info(account_info_iter)?; + let owner_info = next_account_info(account_info_iter)?; + + Self::validate_owner( + program_id, + &source_account.owner, + owner_info, + account_info_iter.as_slice(), + )?; + + source_account.delegate = COption::Some(*delegate_info.key); + source_account.delegated_amount = amount; + + Ok(()) + } + + /// Processes an [Revoke](enum.TokenInstruction.html) instruction. + pub fn process_revoke(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let source_account_info = next_account_info(account_info_iter)?; + + let mut source_data = source_account_info.data.borrow_mut(); + let mut source_account: &mut Account = state::unpack(&mut source_data)?; + let owner_info = next_account_info(account_info_iter)?; + + Self::validate_owner( + program_id, + &source_account.owner, + owner_info, + account_info_iter.as_slice(), + )?; + + source_account.delegate = COption::None; + source_account.delegated_amount = 0; + + Ok(()) + } + + /// Processes a [SetOwner](enum.TokenInstruction.html) instruction. + pub fn process_set_owner(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let account_info = next_account_info(account_info_iter)?; + let new_owner_info = next_account_info(account_info_iter)?; + let authority_info = next_account_info(account_info_iter)?; + + if account_info.data_len() == size_of::() { + let mut account_data = account_info.data.borrow_mut(); + let mut account: &mut Account = state::unpack(&mut account_data)?; + + Self::validate_owner( + program_id, + &account.owner, + authority_info, + account_info_iter.as_slice(), + )?; + + account.owner = *new_owner_info.key; + } else if account_info.data_len() == size_of::() { + let mut account_data = account_info.data.borrow_mut(); + let mut mint: &mut Mint = state::unpack(&mut account_data)?; + + match mint.owner { + COption::Some(ref owner) => { + Self::validate_owner( + program_id, + owner, + authority_info, + account_info_iter.as_slice(), + )?; + } + COption::None => return Err(TokenError::FixedSupply.into()), + } + mint.owner = COption::Some(*new_owner_info.key); + } else { + return Err(ProgramError::InvalidArgument); + } + + Ok(()) + } + + /// Processes a [MintTo](enum.TokenInstruction.html) instruction. + pub fn process_mint_to( + program_id: &Pubkey, + accounts: &[AccountInfo], + amount: u64, + ) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let mint_info = next_account_info(account_info_iter)?; + let dest_account_info = next_account_info(account_info_iter)?; + let owner_info = next_account_info(account_info_iter)?; + + let mut dest_account_data = dest_account_info.data.borrow_mut(); + let mut dest_account: &mut Account = state::unpack(&mut dest_account_data)?; + + if dest_account.is_native { + return Err(TokenError::NativeNotSupported.into()); + } + if mint_info.key != &dest_account.mint { + return Err(TokenError::MintMismatch.into()); + } + + let mut mint_info_data = mint_info.data.borrow_mut(); + let mint: &mut Mint = state::unpack(&mut mint_info_data)?; + + match mint.owner { + COption::Some(owner) => { + Self::validate_owner(program_id, &owner, owner_info, account_info_iter.as_slice())?; + } + COption::None => { + return Err(TokenError::FixedSupply.into()); + } + } + + dest_account.amount += amount; + + Ok(()) + } + + /// Processes a [Burn](enum.TokenInstruction.html) instruction. + pub fn process_burn( + program_id: &Pubkey, + accounts: &[AccountInfo], + amount: u64, + ) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let source_account_info = next_account_info(account_info_iter)?; + let authority_info = next_account_info(account_info_iter)?; + + let mut source_data = source_account_info.data.borrow_mut(); + let source_account: &mut Account = state::unpack(&mut source_data)?; + + if source_account.is_native { + return Err(TokenError::NativeNotSupported.into()); + } + if source_account.amount < amount { + return Err(TokenError::InsufficientFunds.into()); + } + + match source_account.delegate { + COption::Some(ref delegate) if authority_info.key == delegate => { + Self::validate_owner( + program_id, + delegate, + authority_info, + account_info_iter.as_slice(), + )?; + + if source_account.delegated_amount < amount { + return Err(TokenError::InsufficientFunds.into()); + } + source_account.delegated_amount -= amount; + if source_account.delegated_amount == 0 { + source_account.delegate = COption::None; + } + } + _ => Self::validate_owner( + program_id, + &source_account.owner, + authority_info, + account_info_iter.as_slice(), + )?, + } + + source_account.amount -= amount; + + Ok(()) + } + + /// Processes a [CloseAccount](enum.TokenInstruction.html) instruction. + pub fn process_close_account(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let source_account_info = next_account_info(account_info_iter)?; + let dest_account_info = next_account_info(account_info_iter)?; + let authority_info = next_account_info(account_info_iter)?; + + let mut source_data = source_account_info.data.borrow_mut(); + let source_account: &mut Account = state::unpack(&mut source_data)?; + + if !source_account.is_native { + return Err(TokenError::NonNativeNotSupported.into()); + } + + Self::validate_owner( + program_id, + &source_account.owner, + authority_info, + account_info_iter.as_slice(), + )?; + + **dest_account_info.lamports.borrow_mut() += source_account_info.lamports(); + **source_account_info.lamports.borrow_mut() = 0; + source_account.amount = 0; + + Ok(()) + } + + /// Processes an [Instruction](enum.Instruction.html). + pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult { + let instruction = TokenInstruction::unpack(input)?; + + match instruction { + TokenInstruction::InitializeMint { amount, decimals } => { + info!("Instruction: InitializeMint"); + Self::process_initialize_mint(accounts, amount, decimals) + } + TokenInstruction::InitializeAccount => { + info!("Instruction: InitializeAccount"); + Self::process_initialize_account(accounts) + } + TokenInstruction::InitializeMultisig { m } => { + info!("Instruction: InitializeMultisig"); + Self::process_initialize_multisig(accounts, m) + } + TokenInstruction::Transfer { amount } => { + info!("Instruction: Transfer"); + Self::process_transfer(program_id, accounts, amount) + } + TokenInstruction::Approve { amount } => { + info!("Instruction: Approve"); + Self::process_approve(program_id, accounts, amount) + } + TokenInstruction::Revoke => { + info!("Instruction: Revoke"); + Self::process_revoke(program_id, accounts) + } + TokenInstruction::SetOwner => { + info!("Instruction: SetOwner"); + Self::process_set_owner(program_id, accounts) + } + TokenInstruction::MintTo { amount } => { + info!("Instruction: MintTo"); + Self::process_mint_to(program_id, accounts, amount) + } + TokenInstruction::Burn { amount } => { + info!("Instruction: Burn"); + Self::process_burn(program_id, accounts, amount) + } + TokenInstruction::CloseAccount => { + info!("Instruction: CloseAccount"); + Self::process_close_account(program_id, accounts) + } + } + } + + /// Validates owner(s) are present + pub fn validate_owner( + program_id: &Pubkey, + expected_owner: &Pubkey, + owner_account_info: &AccountInfo, + signers: &[AccountInfo], + ) -> ProgramResult { + if expected_owner != owner_account_info.key { + return Err(TokenError::OwnerMismatch.into()); + } + if program_id == owner_account_info.owner + && owner_account_info.data_len() == std::mem::size_of::() + { + let mut owner_data = owner_account_info.data.borrow_mut(); + let multisig: &mut Multisig = state::unpack(&mut owner_data)?; + let mut num_signers = 0; + for signer in signers.iter() { + if multisig.signers[0..multisig.n as usize].contains(signer.key) { + if !signer.is_signer { + return Err(ProgramError::MissingRequiredSignature); + } + num_signers += 1; + } + } + if num_signers < multisig.m { + return Err(ProgramError::MissingRequiredSignature); + } + } else if !owner_account_info.is_signer { + return Err(ProgramError::MissingRequiredSignature); + } + Ok(()) + } +} + +impl PrintProgramError for TokenError { + fn print(&self) + where + E: 'static + std::error::Error + DecodeError + PrintProgramError + FromPrimitive, + { + match self { + TokenError::InsufficientFunds => info!("Error: insufficient funds"), + TokenError::MintMismatch => info!("Error: Account not associated with this Mint"), + TokenError::OwnerMismatch => info!("Error: owner does not match"), + TokenError::FixedSupply => info!("Error: the total supply of this token is fixed"), + TokenError::AlreadyInUse => info!("Error: account or token already in use"), + TokenError::OwnerRequiredIfNoInitialSupply => { + info!("Error: An owner is required if supply is zero") + } + TokenError::InvalidNumberOfProvidedSigners => { + info!("Error: Invalid number of provided signers") + } + TokenError::InvalidNumberOfRequiredSigners => { + info!("Error: Invalid number of required signers") + } + TokenError::UninitializedState => info!("Error: State is uninitialized"), + TokenError::NativeNotSupported => { + info!("Error: Instruction does not support native tokens") + } + TokenError::NonNativeNotSupported => { + info!("Error: Instruction does not support non-native tokens") + } + TokenError::InvalidInstruction => info!("Error: Invalid instruction"), + } + } +} + +// Pull in syscall stubs when building for non-BPF targets +#[cfg(not(target_arch = "bpf"))] +solana_sdk::program_stubs!(); + +#[cfg(test)] +mod tests { + use super::*; + use crate::instruction::{ + approve, burn, close_account, initialize_account, initialize_mint, initialize_multisig, + mint_to, revoke, set_owner, transfer, MAX_SIGNERS, + }; + use solana_sdk::{ + account::Account as SolanaAccount, account_info::create_is_signer_account_infos, + clock::Epoch, instruction::Instruction, + }; + + fn pubkey_rand() -> Pubkey { + Pubkey::new(&rand::random::<[u8; 32]>()) + } + + fn do_process_instruction( + instruction: Instruction, + accounts: Vec<&mut SolanaAccount>, + ) -> ProgramResult { + let mut meta = instruction + .accounts + .iter() + .zip(accounts) + .map(|(account_meta, account)| (&account_meta.pubkey, account_meta.is_signer, account)) + .collect::>(); + + let account_infos = create_is_signer_account_infos(&mut meta); + Processor::process(&instruction.program_id, &account_infos, &instruction.data) + } + + fn return_token_error_as_program_error() -> ProgramError { + TokenError::MintMismatch.into() + } + + #[test] + fn test_print_error() { + let error = return_token_error_as_program_error(); + error.print::(); + } + + #[test] + #[should_panic(expected = "Custom(1)")] + fn test_error_unwrap() { + Err::<(), ProgramError>(return_token_error_as_program_error()).unwrap(); + } + + #[test] + fn test_unique_account_sizes() { + assert_ne!(size_of::(), 0); + assert_ne!(size_of::(), size_of::()); + assert_ne!(size_of::(), size_of::()); + assert_ne!(size_of::(), 0); + assert_ne!(size_of::(), size_of::()); + assert_ne!(size_of::(), 0); + } + + #[test] + fn test_initialize_mint() { + let program_id = pubkey_rand(); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mint2_key = pubkey_rand(); + let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); + + // account not created + assert_eq!( + Err(TokenError::UninitializedState.into()), + do_process_instruction( + initialize_mint(&program_id, &mint_key, Some(&account_key), None, 1000, 2).unwrap(), + vec![&mut mint_account, &mut account_account] + ) + ); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut owner_account, &mut mint_account], + ) + .unwrap(); + + // create new mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, Some(&account_key), None, 1000, 2).unwrap(), + vec![&mut mint_account, &mut account_account], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), + vec![&mut account2_account, &mut owner_account, &mut mint_account], + ) + .unwrap(); + + // mismatch account + assert_eq!( + Err(TokenError::MintMismatch.into()), + do_process_instruction( + initialize_mint(&program_id, &mint2_key, Some(&account2_key), None, 1000, 2) + .unwrap(), + vec![&mut mint2_account, &mut account2_account] + ) + ); + + // create twice + assert_eq!( + Err(TokenError::AlreadyInUse.into()), + do_process_instruction( + initialize_mint(&program_id, &mint_key, Some(&account_key), None, 1000, 2).unwrap(), + vec![&mut mint_account, &mut account_account] + ) + ); + } + + #[test] + fn test_initialize_mint_account() { + let program_id = pubkey_rand(); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create twice + assert_eq!( + Err(TokenError::AlreadyInUse.into()), + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + ); + } + + #[test] + fn test_transfer() { + let program_id = pubkey_rand(); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let account3_key = pubkey_rand(); + let mut account3_account = SolanaAccount::new(0, size_of::(), &program_id); + let delegate_key = pubkey_rand(); + let mut delegate_account = SolanaAccount::default(); + let mismatch_key = pubkey_rand(); + let mut mismatch_account = SolanaAccount::new(0, size_of::(), &program_id); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = pubkey_rand(); + let mut owner2_account = SolanaAccount::default(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mint2_key = pubkey_rand(); + let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), + vec![&mut account2_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), + vec![&mut account3_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create mismatch account + do_process_instruction( + initialize_account(&program_id, &mismatch_key, &mint2_key, &owner_key).unwrap(), + vec![ + &mut mismatch_account, + &mut mint2_account, + &mut owner_account, + ], + ) + .unwrap(); + + // create new mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, Some(&account_key), None, 1000, 2).unwrap(), + vec![&mut mint_account, &mut account_account], + ) + .unwrap(); + + // missing signer + let mut instruction = transfer( + &program_id, + &account_key, + &account2_key, + &owner_key, + &[], + 1000, + ) + .unwrap(); + instruction.accounts[2].is_signer = false; + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + do_process_instruction( + instruction, + vec![ + &mut account_account, + &mut account2_account, + &mut owner_account, + ], + ) + ); + + // mismatch mint + assert_eq!( + Err(TokenError::MintMismatch.into()), + do_process_instruction( + transfer( + &program_id, + &account_key, + &mismatch_key, + &owner_key, + &[], + 1000 + ) + .unwrap(), + vec![ + &mut account_account, + &mut mismatch_account, + &mut owner_account, + ], + ) + ); + + // missing owner + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &owner2_key, + &[], + 1000 + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner2_account, + ], + ) + ); + + // transfer + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &owner_key, + &[], + 1000, + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner_account, + ], + ) + .unwrap(); + + // insufficient funds + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + do_process_instruction( + transfer(&program_id, &account_key, &account2_key, &owner_key, &[], 1).unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner_account, + ], + ) + ); + + // transfer half back + do_process_instruction( + transfer( + &program_id, + &account2_key, + &account_key, + &owner_key, + &[], + 500, + ) + .unwrap(), + vec![ + &mut account2_account, + &mut account_account, + &mut owner_account, + ], + ) + .unwrap(); + + // transfer rest + do_process_instruction( + transfer( + &program_id, + &account2_key, + &account_key, + &owner_key, + &[], + 500, + ) + .unwrap(), + vec![ + &mut account2_account, + &mut account_account, + &mut owner_account, + ], + ) + .unwrap(); + + // transfer to self + { + let instruction = transfer( + &program_id, + &account_key, + &account_key, + &owner_key, + &[], + 500, + ) + .unwrap(); + let account_account_info = AccountInfo::from(( + &instruction.accounts[0].pubkey, + instruction.accounts[0].is_signer, + &mut account_account, + )); + let owner_account_info = AccountInfo::from(( + &instruction.accounts[2].pubkey, + instruction.accounts[2].is_signer, + &mut owner_account, + )); + Processor::process( + &instruction.program_id, + &[ + account_account_info.clone(), + account_account_info, + owner_account_info, + ], + &instruction.data, + ) + .unwrap() + } + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + assert_eq!(account.amount, 1000); + + // insufficient funds + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + do_process_instruction( + transfer(&program_id, &account2_key, &account_key, &owner_key, &[], 1).unwrap(), + vec![ + &mut account2_account, + &mut account_account, + &mut owner_account, + ], + ) + ); + + // approve delegate + do_process_instruction( + approve( + &program_id, + &account_key, + &delegate_key, + &owner_key, + &[], + 100, + ) + .unwrap(), + vec![ + &mut account_account, + &mut delegate_account, + &mut owner_account, + ], + ) + .unwrap(); + + // transfer via delegate + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &delegate_key, + &[], + 100, + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut delegate_account, + ], + ) + .unwrap(); + + // insufficient funds approved via delegate + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &delegate_key, + &[], + 100 + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut delegate_account, + ], + ) + ); + + // transfer rest + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &owner_key, + &[], + 900, + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner_account, + ], + ) + .unwrap(); + + // approve delegate + do_process_instruction( + approve( + &program_id, + &account_key, + &delegate_key, + &owner_key, + &[], + 100, + ) + .unwrap(), + vec![ + &mut account_account, + &mut delegate_account, + &mut owner_account, + ], + ) + .unwrap(); + + // insufficient funds in source account via delegate + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &delegate_key, + &[], + 100 + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut delegate_account, + ], + ) + ); + } + + #[test] + fn test_mintable_token_with_zero_supply() { + let program_id = pubkey_rand(); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut owner_account, &mut mint_account], + ) + .unwrap(); + + // create mint-able token without owner + let mut instruction = + initialize_mint(&program_id, &mint_key, None, Some(&owner_key), 0, 2).unwrap(); + instruction.accounts.pop(); + assert_eq!( + Err(TokenError::OwnerRequiredIfNoInitialSupply.into()), + do_process_instruction(instruction, vec![&mut mint_account]) + ); + + // create mint-able token with zero supply + let amount = 0; + let decimals = 2; + do_process_instruction( + initialize_mint( + &program_id, + &mint_key, + None, + Some(&owner_key), + amount, + decimals, + ) + .unwrap(), + vec![&mut mint_account, &mut account_account], + ) + .unwrap(); + let mint: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); + assert_eq!( + *mint, + Mint { + owner: COption::Some(owner_key), + decimals, + is_initialized: true, + } + ); + + // mint to + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 42).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + + let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); + let dest_account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + assert_eq!(dest_account.amount, 42); + } + + #[test] + fn test_approve() { + let program_id = pubkey_rand(); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let delegate_key = pubkey_rand(); + let mut delegate_account = SolanaAccount::default(); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = pubkey_rand(); + let mut owner2_account = SolanaAccount::default(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut owner_account, &mut mint_account], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), + vec![&mut account2_account, &mut owner_account, &mut mint_account], + ) + .unwrap(); + + // create new mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, Some(&account_key), None, 1000, 2).unwrap(), + vec![&mut mint_account, &mut account_account], + ) + .unwrap(); + + // missing signer + let mut instruction = approve( + &program_id, + &account_key, + &delegate_key, + &owner_key, + &[], + 100, + ) + .unwrap(); + instruction.accounts[2].is_signer = false; + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + do_process_instruction( + instruction, + vec![ + &mut account_account, + &mut delegate_account, + &mut owner_account, + ], + ) + ); + + // no owner + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + approve( + &program_id, + &account_key, + &delegate_key, + &owner2_key, + &[], + 100 + ) + .unwrap(), + vec![ + &mut account_account, + &mut delegate_account, + &mut owner2_account, + ], + ) + ); + + // approve delegate + do_process_instruction( + approve( + &program_id, + &account_key, + &delegate_key, + &owner_key, + &[], + 100, + ) + .unwrap(), + vec![ + &mut account_account, + &mut delegate_account, + &mut owner_account, + ], + ) + .unwrap(); + + // revoke delegate + do_process_instruction( + revoke(&program_id, &account_key, &owner_key, &[]).unwrap(), + vec![&mut account_account, &mut owner_account], + ) + .unwrap(); + } + + #[test] + fn test_set_owner() { + let program_id = pubkey_rand(); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = pubkey_rand(); + let mut owner2_account = SolanaAccount::default(); + let owner3_key = pubkey_rand(); + let mut owner3_account = SolanaAccount::default(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mint2_key = pubkey_rand(); + let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); + + // invalid account + assert_eq!( + Err(TokenError::UninitializedState.into()), + do_process_instruction( + set_owner(&program_id, &account_key, &owner2_key, &owner_key, &[]).unwrap(), + vec![ + &mut account_account, + &mut owner2_account, + &mut owner_account, + ], + ) + ); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint2_key, &owner_key).unwrap(), + vec![ + &mut account2_account, + &mut mint2_account, + &mut owner_account, + ], + ) + .unwrap(); + + // missing owner + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + set_owner(&program_id, &account_key, &owner_key, &owner2_key, &[]).unwrap(), + vec![ + &mut account_account, + &mut owner_account, + &mut owner2_account, + ], + ) + ); + + // owner did not sign + let mut instruction = + set_owner(&program_id, &account_key, &owner2_key, &owner_key, &[]).unwrap(); + instruction.accounts[2].is_signer = false; + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + do_process_instruction( + instruction, + vec![ + &mut account_account, + &mut owner2_account, + &mut owner_account, + ], + ) + ); + + // set owner + do_process_instruction( + set_owner(&program_id, &account_key, &owner2_key, &owner_key, &[]).unwrap(), + vec![ + &mut account_account, + &mut owner2_account, + &mut owner_account, + ], + ) + .unwrap(); + + // create new mint with owner + do_process_instruction( + initialize_mint( + &program_id, + &mint_key, + Some(&account_key), + Some(&owner_key), + 1000, + 2, + ) + .unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + + // wrong account + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + set_owner(&program_id, &mint_key, &owner3_key, &owner2_key, &[]).unwrap(), + vec![&mut mint_account, &mut owner3_account, &mut owner2_account], + ) + ); + + // owner did not sign + let mut instruction = + set_owner(&program_id, &mint_key, &owner2_key, &owner_key, &[]).unwrap(); + instruction.accounts[2].is_signer = false; + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + do_process_instruction( + instruction, + vec![&mut mint_account, &mut owner2_account, &mut owner_account], + ) + ); + + // set owner + do_process_instruction( + set_owner(&program_id, &mint_key, &owner2_key, &owner_key, &[]).unwrap(), + vec![&mut mint_account, &mut owner2_account, &mut owner_account], + ) + .unwrap(); + + // create new mint without owner + do_process_instruction( + initialize_mint(&program_id, &mint2_key, Some(&account2_key), None, 1000, 2).unwrap(), + vec![&mut mint2_account, &mut account2_account], + ) + .unwrap(); + + // set owner for non-mint-able token + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + set_owner(&program_id, &mint2_key, &owner2_key, &owner_key, &[]).unwrap(), + vec![&mut mint_account, &mut owner2_account, &mut owner_account], + ) + ); + } + + #[test] + fn test_mint_to() { + let program_id = pubkey_rand(); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let account3_key = pubkey_rand(); + let mut account3_account = SolanaAccount::new(0, size_of::(), &program_id); + let mismatch_key = pubkey_rand(); + let mut mismatch_account = SolanaAccount::new(0, size_of::(), &program_id); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = pubkey_rand(); + let mut owner2_account = SolanaAccount::default(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mint2_key = pubkey_rand(); + let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); + let uninitialized_key = pubkey_rand(); + let mut uninitialized_account = SolanaAccount::new(0, size_of::(), &program_id); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), + vec![&mut account2_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), + vec![&mut account3_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create mismatch account + do_process_instruction( + initialize_account(&program_id, &mismatch_key, &mint2_key, &owner_key).unwrap(), + vec![ + &mut mismatch_account, + &mut mint2_account, + &mut owner_account, + ], + ) + .unwrap(); + + // create new mint with owner + do_process_instruction( + initialize_mint( + &program_id, + &mint_key, + Some(&account_key), + Some(&owner_key), + 1000, + 2, + ) + .unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + + // mint to + do_process_instruction( + mint_to(&program_id, &mint_key, &account2_key, &owner_key, &[], 42).unwrap(), + vec![&mut mint_account, &mut account2_account, &mut owner_account], + ) + .unwrap(); + + let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); + let dest_account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); + assert_eq!(dest_account.amount, 42); + + // missing signer + let mut instruction = + mint_to(&program_id, &mint_key, &account2_key, &owner_key, &[], 42).unwrap(); + instruction.accounts[2].is_signer = false; + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + do_process_instruction( + instruction, + vec![&mut mint_account, &mut account2_account, &mut owner_account], + ) + ); + + // mismatch account + assert_eq!( + Err(TokenError::MintMismatch.into()), + do_process_instruction( + mint_to(&program_id, &mint_key, &mismatch_key, &owner_key, &[], 42).unwrap(), + vec![&mut mint_account, &mut mismatch_account, &mut owner_account], + ) + ); + + // missing owner + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + mint_to(&program_id, &mint_key, &account2_key, &owner2_key, &[], 42).unwrap(), + vec![ + &mut mint_account, + &mut account2_account, + &mut owner2_account, + ], + ) + ); + + // uninitialized destination account + assert_eq!( + Err(TokenError::UninitializedState.into()), + do_process_instruction( + mint_to( + &program_id, + &mint_key, + &uninitialized_key, + &owner_key, + &[], + 42 + ) + .unwrap(), + vec![ + &mut mint_account, + &mut uninitialized_account, + &mut owner_account, + ], + ) + ); + } + + #[test] + fn test_burn() { + let program_id = pubkey_rand(); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let account3_key = pubkey_rand(); + let mut account3_account = SolanaAccount::new(0, size_of::(), &program_id); + let delegate_key = pubkey_rand(); + let mut delegate_account = SolanaAccount::default(); + let mismatch_key = pubkey_rand(); + let mut mismatch_account = SolanaAccount::new(0, size_of::(), &program_id); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = pubkey_rand(); + let mut owner2_account = SolanaAccount::default(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mint2_key = pubkey_rand(); + let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), + vec![&mut account2_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), + vec![&mut account3_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create mismatch account + do_process_instruction( + initialize_account(&program_id, &mismatch_key, &mint2_key, &owner_key).unwrap(), + vec![ + &mut mismatch_account, + &mut mint2_account, + &mut owner_account, + ], + ) + .unwrap(); + + // create new mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, Some(&account_key), None, 1000, 2).unwrap(), + vec![&mut mint_account, &mut account_account], + ) + .unwrap(); + + // missing signer + let mut instruction = burn(&program_id, &account_key, &delegate_key, &[], 42).unwrap(); + instruction.accounts[1].is_signer = false; + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + instruction, + vec![&mut account_account, &mut delegate_account], + ) + ); + + // missing owner + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + burn(&program_id, &account_key, &owner2_key, &[], 42).unwrap(), + vec![&mut account_account, &mut owner2_account], + ) + ); + + // burn + do_process_instruction( + burn(&program_id, &account_key, &owner_key, &[], 42).unwrap(), + vec![&mut account_account, &mut owner_account], + ) + .unwrap(); + + let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + assert_eq!(account.amount, 1000 - 42); + + // insufficient funds + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + do_process_instruction( + burn(&program_id, &account_key, &owner_key, &[], 100_000_000).unwrap(), + vec![&mut account_account, &mut owner_account], + ) + ); + + // approve delegate + do_process_instruction( + approve( + &program_id, + &account_key, + &delegate_key, + &owner_key, + &[], + 84, + ) + .unwrap(), + vec![ + &mut account_account, + &mut delegate_account, + &mut owner_account, + ], + ) + .unwrap(); + + // not a delegate of source account + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + do_process_instruction( + burn(&program_id, &account_key, &owner_key, &[], 100_000_000).unwrap(), + vec![&mut account_account, &mut owner_account], + ) + ); + + // burn via delegate + do_process_instruction( + burn(&program_id, &account_key, &delegate_key, &[], 84).unwrap(), + vec![&mut account_account, &mut delegate_account], + ) + .unwrap(); + + // match + let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + assert_eq!(account.amount, 1000 - 42 - 84); + + // insufficient funds approved via delegate + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + burn(&program_id, &account_key, &delegate_key, &[], 100).unwrap(), + vec![&mut account_account, &mut delegate_account], + ) + ); + } + + #[test] + fn test_multisig() { + let program_id = pubkey_rand(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let account_key = pubkey_rand(); + let mut account = SolanaAccount::new(0, size_of::(), &program_id); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let multisig_key = pubkey_rand(); + let mut multisig_account = SolanaAccount::new(0, size_of::(), &program_id); + let multisig_delegate_key = pubkey_rand(); + let mut multisig_delegate_account = + SolanaAccount::new(0, size_of::(), &program_id); + let signer_keys = vec![pubkey_rand(); MAX_SIGNERS]; + let signer_key_refs: Vec<&Pubkey> = signer_keys.iter().map(|key| key).collect(); + let mut signer_accounts = vec![SolanaAccount::new(0, 0, &program_id); MAX_SIGNERS]; + + // single signer + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(), + vec![ + &mut multisig_account, + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // multiple signer + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + initialize_multisig( + &program_id, + &multisig_delegate_key, + &signer_key_refs, + MAX_SIGNERS as u8, + ) + .unwrap(), + vec![ + &mut multisig_delegate_account, + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // create account with multisig owner + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &multisig_key).unwrap(), + vec![&mut account, &mut mint_account, &mut multisig_account], + ) + .unwrap(); + + // create another account with multisig owner + do_process_instruction( + initialize_account( + &program_id, + &account2_key, + &mint_key, + &multisig_delegate_key, + ) + .unwrap(), + vec![ + &mut account2_account, + &mut mint_account, + &mut multisig_account, + ], + ) + .unwrap(); + + // create new m int with multisig owner + do_process_instruction( + initialize_mint( + &program_id, + &mint_key, + Some(&account_key), + Some(&multisig_key), + 1000, + 2, + ) + .unwrap(), + vec![&mut mint_account, &mut account, &mut multisig_account], + ) + .unwrap(); + + // approve + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + approve( + &program_id, + &account_key, + &multisig_delegate_key, + &multisig_key, + &[&signer_keys[0]], + 100, + ) + .unwrap(), + vec![ + &mut account, + &mut multisig_delegate_account, + &mut multisig_account, + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // transfer + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &multisig_key, + &[&signer_keys[0]], + 42, + ) + .unwrap(), + vec![ + &mut account, + &mut account2_account, + &mut multisig_account, + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // transfer via delegate + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &multisig_delegate_key, + &signer_key_refs, + 42, + ) + .unwrap(), + vec![ + &mut account, + &mut account2_account, + &mut multisig_delegate_account, + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // mint to + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + mint_to( + &program_id, + &mint_key, + &account2_key, + &multisig_key, + &[&signer_keys[0]], + 42, + ) + .unwrap(), + vec![ + &mut mint_account, + &mut account2_account, + &mut multisig_account, + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // burn + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + burn( + &program_id, + &account_key, + &multisig_key, + &[&signer_keys[0]], + 42, + ) + .unwrap(), + vec![ + &mut account, + &mut multisig_account, + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // burn via delegate + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + burn( + &program_id, + &account_key, + &multisig_delegate_key, + &signer_key_refs, + 42, + ) + .unwrap(), + vec![ + &mut account, + &mut multisig_delegate_account, + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // do SetOwner on mint + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + set_owner( + &program_id, + &mint_key, + &owner_key, + &multisig_key, + &[&signer_keys[0]], + ) + .unwrap(), + vec![ + &mut mint_account, + &mut owner_account, + &mut multisig_account, + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // do SetOwner on account + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + set_owner( + &program_id, + &account_key, + &owner_key, + &multisig_key, + &[&signer_keys[0]], + ) + .unwrap(), + vec![ + &mut account, + &mut owner_account, + &mut multisig_account, + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + } + + #[test] + fn test_validate_owner() { + let program_id = pubkey_rand(); + let owner_key = pubkey_rand(); + let mut signer_keys = [Pubkey::default(); MAX_SIGNERS]; + for signer_key in signer_keys.iter_mut().take(MAX_SIGNERS) { + *signer_key = pubkey_rand(); + } + let mut signer_lamports = 0; + let mut signer_data = vec![]; + let mut signers = vec![ + AccountInfo::new( + &owner_key, + true, + false, + &mut signer_lamports, + &mut signer_data, + &program_id, + false, + Epoch::default(), + ); + MAX_SIGNERS + 1 + ]; + for (signer, key) in signers.iter_mut().zip(&signer_keys) { + signer.key = key; + } + let mut lamports = 0; + let mut data = vec![0; size_of::()]; + let mut multisig: &mut Multisig = state::unpack_unchecked(&mut data).unwrap(); + multisig.m = MAX_SIGNERS as u8; + multisig.n = MAX_SIGNERS as u8; + multisig.signers = signer_keys; + multisig.is_initialized = true; + let owner_account_info = AccountInfo::new( + &owner_key, + false, + false, + &mut lamports, + &mut data, + &program_id, + false, + Epoch::default(), + ); + + // full 11 of 11 + Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap(); + + // 1 of 11 + { + let mut data_ref_mut = owner_account_info.data.borrow_mut(); + let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); + multisig.m = 1; + } + Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap(); + + // 2:1 + { + let mut data_ref_mut = owner_account_info.data.borrow_mut(); + let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); + multisig.m = 2; + multisig.n = 1; + } + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers) + ); + + // 0:11 + { + let mut data_ref_mut = owner_account_info.data.borrow_mut(); + let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); + multisig.m = 0; + multisig.n = 11; + } + Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap(); + + // 2:11 but 0 provided + { + let mut data_ref_mut = owner_account_info.data.borrow_mut(); + let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); + multisig.m = 2; + multisig.n = 11; + } + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &[]) + ); + // 2:11 but 1 provided + { + let mut data_ref_mut = owner_account_info.data.borrow_mut(); + let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); + multisig.m = 2; + multisig.n = 11; + } + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers[0..1]) + ); + + // 2:11, 2 from middle provided + { + let mut data_ref_mut = owner_account_info.data.borrow_mut(); + let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); + multisig.m = 2; + multisig.n = 11; + } + Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers[5..7]) + .unwrap(); + + // 11:11, one is not a signer + { + let mut data_ref_mut = owner_account_info.data.borrow_mut(); + let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); + multisig.m = 2; + multisig.n = 11; + } + signers[5].is_signer = false; + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers) + ); + signers[5].is_signer = true; + } + + #[test] + fn test_close_account() { + let program_id = pubkey_rand(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(42, size_of::(), &program_id); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new(2, size_of::(), &program_id); + let account3_key = pubkey_rand(); + let mut account3_account = SolanaAccount::new(2, size_of::(), &program_id); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = pubkey_rand(); + let mut owner2_account = SolanaAccount::default(); + + // uninitialized + assert_eq!( + Err(TokenError::UninitializedState.into()), + do_process_instruction( + close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(), + vec![ + &mut account_account, + &mut account3_account, + &mut owner2_account, + ], + ) + ); + + // initialize non-native account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // initialize native account + do_process_instruction( + initialize_account( + &program_id, + &account2_key, + &crate::native_mint::id(), + &owner_key, + ) + .unwrap(), + vec![&mut account2_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); + assert!(account.is_native); + assert_eq!(account.amount, 2); + + // close non-native account + assert_eq!( + Err(TokenError::NonNativeNotSupported.into()), + do_process_instruction( + close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(), + vec![ + &mut account_account, + &mut account3_account, + &mut owner_account, + ], + ) + ); + assert_eq!(account_account.lamports, 42); + + // close native account + do_process_instruction( + close_account(&program_id, &account2_key, &account3_key, &owner_key, &[]).unwrap(), + vec![ + &mut account2_account, + &mut account3_account, + &mut owner_account, + ], + ) + .unwrap(); + let account: &mut Account = state::unpack_unchecked(&mut account2_account.data).unwrap(); + assert!(account.is_native); + assert_eq!(account.amount, 0); + assert_eq!(account3_account.lamports, 4); + } + + #[test] + fn test_native_token() { + let program_id = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(42, size_of::(), &program_id); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new(2, size_of::(), &program_id); + let account3_key = pubkey_rand(); + let mut account3_account = SolanaAccount::new(2, 0, &program_id); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + + // initialize native account + do_process_instruction( + initialize_account( + &program_id, + &account_key, + &crate::native_mint::id(), + &owner_key, + ) + .unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + assert!(account.is_native); + assert_eq!(account.amount, 42); + + // initialize native account + do_process_instruction( + initialize_account( + &program_id, + &account2_key, + &crate::native_mint::id(), + &owner_key, + ) + .unwrap(), + vec![&mut account2_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); + assert!(account.is_native); + assert_eq!(account.amount, 2); + + // mint_to unsupported + assert_eq!( + Err(TokenError::NativeNotSupported.into()), + do_process_instruction( + mint_to( + &program_id, + &crate::native_mint::id(), + &account_key, + &owner_key, + &[], + 42 + ) + .unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + ); + + // burn unsupported + assert_eq!( + Err(TokenError::NativeNotSupported.into()), + do_process_instruction( + burn(&program_id, &account_key, &owner_key, &[], 42).unwrap(), + vec![&mut account_account, &mut owner_account], + ) + ); + + // initialize native account + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &owner_key, + &[], + 40, + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner_account, + ], + ) + .unwrap(); + + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + assert!(account.is_native); + assert_eq!(account_account.lamports, 2); + assert_eq!(account.amount, 2); + let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); + assert!(account.is_native); + assert_eq!(account2_account.lamports, 42); + assert_eq!(account.amount, 42); + + // close native account + do_process_instruction( + close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(), + vec![ + &mut account_account, + &mut account3_account, + &mut owner_account, + ], + ) + .unwrap(); + let account: &mut Account = state::unpack_unchecked(&mut account_account.data).unwrap(); + assert!(account.is_native); + assert_eq!(account_account.lamports, 0); + assert_eq!(account.amount, 0); + assert_eq!(account3_account.lamports, 4); + } +} diff --git a/program/src/state.rs b/program/src/state.rs new file mode 100644 index 0000000..3b63f4f --- /dev/null +++ b/program/src/state.rs @@ -0,0 +1,92 @@ +//! State transition types + +use crate::{error::TokenError, instruction::MAX_SIGNERS, option::COption}; +use solana_sdk::{program_error::ProgramError, pubkey::Pubkey}; +use std::mem::size_of; + +/// Mint data. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct Mint { + /// Optional owner, used to mint new tokens. The owner may only + /// be provided during mint creation. If no owner is present then the mint + /// has a fixed supply and no further tokens may be minted. + pub owner: COption, + /// Number of base 10 digits to the right of the decimal place. + pub decimals: u8, + /// Is `true` if this structure has been initialized + pub is_initialized: bool, +} +impl IsInitialized for Mint { + fn is_initialized(&self) -> bool { + self.is_initialized + } +} + +/// Account data. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct Account { + /// The mint associated with this account + pub mint: Pubkey, + /// The owner of this account. + pub owner: Pubkey, + /// The amount of tokens this account holds. + pub amount: u64, + /// If `delegate` is `Some` then `delegated_amount` represents + /// the amount authorized by the delegate + pub delegate: COption, + /// Is `true` if this structure has been initialized + pub is_initialized: bool, + /// Is this a native token + pub is_native: bool, + /// The amount delegated + pub delegated_amount: u64, +} +impl IsInitialized for Account { + fn is_initialized(&self) -> bool { + self.is_initialized + } +} + +/// Multisignature data. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct Multisig { + /// Number of signers required + pub m: u8, + /// Number of valid signers + pub n: u8, + /// Is `true` if this structure has been initialized + pub is_initialized: bool, + /// Signer public keys + pub signers: [Pubkey; MAX_SIGNERS], +} +impl IsInitialized for Multisig { + fn is_initialized(&self) -> bool { + self.is_initialized + } +} + +/// Check is a token state is initialized +pub trait IsInitialized { + /// Is initialized + fn is_initialized(&self) -> bool; +} + +/// Unpacks a token state from a bytes buffer while assuring that the state is initialized. +pub fn unpack(input: &mut [u8]) -> Result<&mut T, ProgramError> { + let mut_ref: &mut T = unpack_unchecked(input)?; + if !mut_ref.is_initialized() { + return Err(TokenError::UninitializedState.into()); + } + Ok(mut_ref) +} +/// Unpacks a token state from a bytes buffer without checking that the state is initialized. +pub fn unpack_unchecked(input: &mut [u8]) -> Result<&mut T, ProgramError> { + if input.len() != size_of::() { + return Err(ProgramError::InvalidAccountData); + } + #[allow(clippy::cast_ptr_alignment)] + Ok(unsafe { &mut *(&mut input[0] as *mut u8 as *mut T) }) +} From afadf968e036385e5c08c5030cd4d823ad725051 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Sat, 8 Aug 2020 11:24:21 -0700 Subject: [PATCH 002/248] Relocate js --- program/js/.eslintignore | 1 - program/js/.eslintrc.js | 56 - program/js/.flowconfig | 20 - program/js/.gitignore | 1 - program/js/.prettierrc.yaml | 7 - program/js/README.md | 69 - program/js/babel.config.json | 17 - program/js/babel.rollup.config.json | 18 - program/js/cli/main.js | 58 - program/js/cli/token-test.js | 500 - program/js/client/layout.js | 47 - program/js/client/token.js | 1242 --- .../client/util/new-account-with-lamports.js | 25 - .../util/new-system-account-with-airdrop.js | 17 - .../util/send-and-confirm-transaction.js | 21 - program/js/client/util/sleep.js | 6 - program/js/client/util/store.js | 26 - program/js/cluster-devnet.env | 2 - program/js/cluster-mainnet-beta.env | 2 - program/js/cluster-testnet.env | 2 - program/js/flow-typed/bn.js.js | 4 - program/js/flow-typed/bs58.js | 6 - program/js/flow-typed/buffer-layout.js | 4 - .../js/flow-typed/mkdirp-promise_vx.x.x.js | 32 - program/js/flow-typed/npm/mz_vx.x.x.js | 73 - program/js/flow-typed/semver.js | 3 - program/js/module.d.ts | 160 - program/js/module.flow.js | 163 - program/js/package-lock.json | 8383 ----------------- program/js/package.json | 78 - program/js/rollup.config.js | 74 - program/js/url.js | 31 - 32 files changed, 11148 deletions(-) delete mode 100644 program/js/.eslintignore delete mode 100644 program/js/.eslintrc.js delete mode 100644 program/js/.flowconfig delete mode 100644 program/js/.gitignore delete mode 100644 program/js/.prettierrc.yaml delete mode 100644 program/js/README.md delete mode 100644 program/js/babel.config.json delete mode 100644 program/js/babel.rollup.config.json delete mode 100644 program/js/cli/main.js delete mode 100644 program/js/cli/token-test.js delete mode 100644 program/js/client/layout.js delete mode 100644 program/js/client/token.js delete mode 100644 program/js/client/util/new-account-with-lamports.js delete mode 100644 program/js/client/util/new-system-account-with-airdrop.js delete mode 100644 program/js/client/util/send-and-confirm-transaction.js delete mode 100644 program/js/client/util/sleep.js delete mode 100644 program/js/client/util/store.js delete mode 100644 program/js/cluster-devnet.env delete mode 100644 program/js/cluster-mainnet-beta.env delete mode 100644 program/js/cluster-testnet.env delete mode 100644 program/js/flow-typed/bn.js.js delete mode 100644 program/js/flow-typed/bs58.js delete mode 100644 program/js/flow-typed/buffer-layout.js delete mode 100644 program/js/flow-typed/mkdirp-promise_vx.x.x.js delete mode 100644 program/js/flow-typed/npm/mz_vx.x.x.js delete mode 100644 program/js/flow-typed/semver.js delete mode 100644 program/js/module.d.ts delete mode 100644 program/js/module.flow.js delete mode 100644 program/js/package-lock.json delete mode 100644 program/js/package.json delete mode 100644 program/js/rollup.config.js delete mode 100644 program/js/url.js diff --git a/program/js/.eslintignore b/program/js/.eslintignore deleted file mode 100644 index 502167f..0000000 --- a/program/js/.eslintignore +++ /dev/null @@ -1 +0,0 @@ -/lib diff --git a/program/js/.eslintrc.js b/program/js/.eslintrc.js deleted file mode 100644 index 96d3aae..0000000 --- a/program/js/.eslintrc.js +++ /dev/null @@ -1,56 +0,0 @@ -// eslint-disable-next-line -module.exports = { - // eslint-disable-line import/no-commonjs - env: { - browser: true, - es6: true, - node: true, - }, - extends: [ - 'eslint:recommended', - 'plugin:import/errors', - 'plugin:import/warnings', - ], - parser: 'babel-eslint', - parserOptions: { - sourceType: 'module', - ecmaVersion: 8, - }, - rules: { - 'no-trailing-spaces': ['error'], - 'import/first': ['error'], - 'import/no-commonjs': ['error'], - 'import/order': [ - 'error', - { - groups: [ - ['internal', 'external', 'builtin'], - ['index', 'sibling', 'parent'], - ], - 'newlines-between': 'always', - }, - ], - indent: [ - 'error', - 2, - { - MemberExpression: 1, - SwitchCase: 1, - }, - ], - 'linebreak-style': ['error', 'unix'], - 'no-console': [0], - quotes: [ - 'error', - 'single', - {avoidEscape: true, allowTemplateLiterals: true}, - ], - 'require-await': ['error'], - semi: ['error', 'always'], - }, - settings: { - react: { - version: 'detect', - }, - }, -}; diff --git a/program/js/.flowconfig b/program/js/.flowconfig deleted file mode 100644 index 486f7e7..0000000 --- a/program/js/.flowconfig +++ /dev/null @@ -1,20 +0,0 @@ -[ignore] -/node_modules/* - -[include] - -[libs] -node_modules/@solana/web3.js/module.flow.js -flow-typed/ - -[options] - -emoji=true -esproposal.class_instance_fields=enable -esproposal.class_static_fields=enable -esproposal.decorators=ignore -esproposal.export_star_as=enable -module.system.node.resolve_dirname=./src -module.use_strict=true -experimental.const_params=true -include_warnings=true diff --git a/program/js/.gitignore b/program/js/.gitignore deleted file mode 100644 index a65b417..0000000 --- a/program/js/.gitignore +++ /dev/null @@ -1 +0,0 @@ -lib diff --git a/program/js/.prettierrc.yaml b/program/js/.prettierrc.yaml deleted file mode 100644 index 8deef5d..0000000 --- a/program/js/.prettierrc.yaml +++ /dev/null @@ -1,7 +0,0 @@ -arrowParens: "avoid" -bracketSpacing: false -jsxBracketSameLine: false -semi: true -singleQuote: true -tabWidth: 2 -trailingComma: "all" diff --git a/program/js/README.md b/program/js/README.md deleted file mode 100644 index 5deb943..0000000 --- a/program/js/README.md +++ /dev/null @@ -1,69 +0,0 @@ -# Token Javascript API - -The Token JavaScript library comprises: - -* A library to interact with the on-chain program -* A test client that exercises the program -* Scripts to facilitate building the program - -## Getting Started - -First fetch the npm dependencies, including `@solana/web3.js`, by running: -```bash -$ npm install -``` - -### Select a Network - -The client connects to a local Solana cluster by default. - -To enable on-chain program logs, set the `RUST_LOG` environment variable: - -```bash -$ export RUST_LOG=solana_runtime::native_loader=trace,solana_runtime::system_instruction_processor=trace,solana_runtime::bank=debug,solana_bpf_loader=debug,solana_rbpf=debug -``` - -To start a local Solana cluster run: -```bash -$ npm run localnet:update -$ npm run localnet:up -``` - -Solana cluster logs are available with: -```bash -$ npm run localnet:logs -``` - -For more details on working with a local cluster, see the [full -instructions](https://github.com/solana-labs/solana-web3.js#local-network). - -### Build the on-chain program - -```bash -$ npm run build:program -``` - -### Run the test client - -```bash -$ npm run start -``` - -## Pointing to a public Solana cluster - -Solana maintains three public clusters: -- `devnet` - Development cluster with airdrops enabled -- `testnet` - Tour De Sol test cluster without airdrops enabled -- `mainnet-beta` - Main cluster - -Use npm scripts to configure which cluster. - -To point to `devnet`: -```bash -$ npm run cluster:devnet -``` - -To point back to the local cluster: -```bash -$ npm run cluster:localnet -``` diff --git a/program/js/babel.config.json b/program/js/babel.config.json deleted file mode 100644 index 9c47e88..0000000 --- a/program/js/babel.config.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env", - { - "targets": { - "node": "10" - } - } - ], - "@babel/preset-flow" - ], - "plugins": [ - "@babel/plugin-transform-runtime", - "@babel/plugin-proposal-class-properties" - ] -} diff --git a/program/js/babel.rollup.config.json b/program/js/babel.rollup.config.json deleted file mode 100644 index ee7e695..0000000 --- a/program/js/babel.rollup.config.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env", - { - "modules": false, - "targets": { - "node": "10" - } - } - ], - "@babel/preset-flow" - ], - "plugins": [ - "@babel/plugin-transform-runtime", - "@babel/plugin-proposal-class-properties" - ] -} diff --git a/program/js/cli/main.js b/program/js/cli/main.js deleted file mode 100644 index ee36e4f..0000000 --- a/program/js/cli/main.js +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Exercises the token program - * - * @flow - */ - -import { - loadTokenProgram, - createMint, - createAccount, - transfer, - approveRevoke, - invalidApprove, - failOnApproveOverspend, - setOwner, - mintTo, - multisig, - burn, - failOnCloseAccount, - nativeToken, -} from './token-test'; - -async function main() { - console.log('Run test: loadTokenProgram'); - await loadTokenProgram(); - console.log('Run test: createMint'); - await createMint(); - console.log('Run test: createAccount'); - await createAccount(); - console.log('Run test: transfer'); - await transfer(); - console.log('Run test: approveRevoke'); - await approveRevoke(); - console.log('Run test: invalidApprove'); - await invalidApprove(); - console.log('Run test: failOnApproveOverspend'); - await failOnApproveOverspend(); - console.log('Run test: setOwner'); - await setOwner(); - console.log('Run test: mintTo'); - await mintTo(); - console.log('Run test: multisig'); - await multisig(); - console.log('Run test: burn'); - await burn(); - console.log('Run test: failOnCloseAccount'); - await failOnCloseAccount(); - console.log('Run test: nativeToken'); - await nativeToken(); - console.log('Success\n'); -} - -main() - .catch(err => { - console.error(err); - process.exit(-1); - }) - .then(() => process.exit()); diff --git a/program/js/cli/token-test.js b/program/js/cli/token-test.js deleted file mode 100644 index c0523d9..0000000 --- a/program/js/cli/token-test.js +++ /dev/null @@ -1,500 +0,0 @@ -// @flow - -import fs from 'mz/fs'; -import {Account, Connection, BpfLoader, PublicKey} from '@solana/web3.js'; -import semver from 'semver'; - -import {Token, u64} from '../client/token'; -import {url} from '../url'; -import {newAccountWithLamports} from '../client/util/new-account-with-lamports'; -import {sleep} from '../client/util/sleep'; -import {Store} from '../client/util/store'; - -// Loaded token program's program id -let programId: PublicKey; - -// A token created by the next test and used by all subsequent tests -let mintOwner: Account; -let testToken: Token; -// Initial token account -let testAccountOwner: Account; -let testAccount: PublicKey; - -// A mintable token used by multiple tests -let mintableOwner: Account; -let testMintableToken: Token; -// Initial token account -let testMintableAccountOwner: Account; -let testMintableAccount: PublicKey; - -function assert(condition, message) { - if (!condition) { - console.log(Error().stack + ':token-test.js'); - throw message || 'Assertion failed'; - } -} - -async function didThrow(func, args): Promise { - try { - await func.apply(args); - } catch (e) { - return true; - } - return false; -} - -let connection; -async function getConnection(): Promise { - if (connection) return connection; - - let newConnection = new Connection(url, 'recent'); - const version = await newConnection.getVersion(); - - // commitment params are only supported >= 0.21.0 - const solanaCoreVersion = version['solana-core'].split(' ')[0]; - if (semver.gte(solanaCoreVersion, '0.21.0')) { - newConnection = new Connection(url, 'recent'); - } - - // eslint-disable-next-line require-atomic-updates - connection = newConnection; - console.log('Connection to cluster established:', url, version); - return connection; -} - -async function loadProgram( - connection: Connection, - path: string, -): Promise { - const NUM_RETRIES = 500; /* allow some number of retries */ - const data = await fs.readFile(path); - const {feeCalculator} = await connection.getRecentBlockhash(); - const balanceNeeded = - feeCalculator.lamportsPerSignature * - (BpfLoader.getMinNumSignatures(data.length) + NUM_RETRIES) + - (await connection.getMinimumBalanceForRentExemption(data.length)); - - const from = await newAccountWithLamports(connection, balanceNeeded); - const program_account = new Account(); - console.log('Loading program:', path); - await BpfLoader.load(connection, from, program_account, data); - return program_account.publicKey; -} - -async function GetPrograms(connection: Connection): Promise { - const store = new Store(); - let tokenProgramId = null; - try { - const config = await store.load('config.json'); - console.log('Using pre-loaded Token program'); - console.log( - ' Note: To reload program remove client/util/store/config.json', - ); - tokenProgramId = new PublicKey(config.tokenProgramId); - } catch (err) { - tokenProgramId = await loadProgram( - connection, - '../../target/bpfel-unknown-unknown/release/spl_token.so', - ); - await store.save('config.json', { - tokenProgramId: tokenProgramId.toString(), - }); - } - return tokenProgramId; -} - -export async function loadTokenProgram(): Promise { - const connection = await getConnection(); - programId = await GetPrograms(connection); - - console.log('Token Program ID', programId.toString()); -} - -export async function createMint(): Promise { - const connection = await getConnection(); - const payer = await newAccountWithLamports( - connection, - 100000000000 /* wag */, - ); - mintOwner = new Account(); - testAccountOwner = new Account(); - [testToken, testAccount] = await Token.createMint( - connection, - payer, - mintOwner.publicKey, - testAccountOwner.publicKey, - new u64(10000), - 2, - programId, - false, - ); - - const mintInfo = await testToken.getMintInfo(); - assert(mintInfo.decimals == 2); - assert(mintInfo.owner == null); - - const accountInfo = await testToken.getAccountInfo(testAccount); - assert(accountInfo.mint.equals(testToken.publicKey)); - assert(accountInfo.owner.equals(testAccountOwner.publicKey)); - assert(accountInfo.amount.toNumber() == 10000); - assert(accountInfo.delegate == null); - assert(accountInfo.delegatedAmount.toNumber() == 0); -} - -export async function createAccount(): Promise { - const destOwner = new Account(); - const account = await testToken.createAccount(destOwner.publicKey); - const accountInfo = await testToken.getAccountInfo(account); - assert(accountInfo.mint.equals(testToken.publicKey)); - assert(accountInfo.owner.equals(destOwner.publicKey)); - assert(accountInfo.amount.toNumber() == 0); - assert(accountInfo.delegate == null); -} - -export async function transfer(): Promise { - const destOwner = new Account(); - const dest = await testToken.createAccount(destOwner.publicKey); - - await testToken.transfer(testAccount, dest, testAccountOwner, [], 123); - await sleep(500); - - let destAccountInfo = await testToken.getAccountInfo(dest); - assert(destAccountInfo.amount.toNumber() == 123); -} - -export async function approveRevoke(): Promise { - if (programId == null) { - console.log('test skipped, requires "load token program" to succeed'); - return; - } - - const delegate = new PublicKey(); - await testToken.approve(testAccount, delegate, testAccountOwner, [], 456); - let testAccountInfo = await testToken.getAccountInfo(testAccount); - assert(testAccountInfo.delegatedAmount.toNumber() == 456); - if (testAccountInfo.delegate === null) { - throw new Error('deleage should not be null'); - } else { - assert(testAccountInfo.delegate.equals(delegate)); - } - - await testToken.revoke(testAccount, testAccountOwner, []); - testAccountInfo = await testToken.getAccountInfo(testAccount); - assert(testAccountInfo.delegatedAmount.toNumber() == 0); - if (testAccountInfo.delegate != null) { - throw new Error('delegate should be null'); - } -} - -export async function invalidApprove(): Promise { - const owner = new Account(); - const account1 = await testToken.createAccount(owner.publicKey); - const account2 = await testToken.createAccount(owner.publicKey); - const delegate = new Account(); - - // account2 is not a delegate account of account1 - assert(didThrow(testToken.approve, [account1, account2, owner, [], 123])); - // account1Delegate is not a delegate account of account2 - assert(didThrow(testToken.approve, [account2, delegate, owner, [], 123])); -} - -export async function failOnApproveOverspend(): Promise { - const owner = new Account(); - const account1 = await testToken.createAccount(owner.publicKey); - const account2 = await testToken.createAccount(owner.publicKey); - const delegate = new Account(); - - await testToken.transfer(testAccount, account1, testAccountOwner, [], 10); - - await testToken.approve(account1, delegate.publicKey, owner, [], 2); - - let account1Info = await testToken.getAccountInfo(account1); - assert(account1Info.amount.toNumber() == 10); - assert(account1Info.delegatedAmount.toNumber() == 2); - if (account1Info.delegate === null) { - throw new Error('deleage should not be null'); - } else { - assert(account1Info.delegate.equals(delegate.publicKey)); - } - - await testToken.transfer(account1, account2, delegate, [], 1); - - account1Info = await testToken.getAccountInfo(account1); - assert(account1Info.amount.toNumber() == 9); - assert(account1Info.delegatedAmount.toNumber() == 1); - - await testToken.transfer(account1, account2, delegate, [], 1); - - account1Info = await testToken.getAccountInfo(account1); - assert(account1Info.amount.toNumber() == 8); - assert(account1Info.delegate === null); - assert(account1Info.delegatedAmount.toNumber() == 0); - - assert(didThrow(testToken.transfer, [account1, account2, delegate, [], 1])); -} - -export async function setOwner(): Promise { - const owner = new Account(); - const newOwner = new Account(); - const owned = await testToken.createAccount(owner.publicKey); - - await testToken.setOwner(owned, newOwner.publicKey, owner, []); - assert(didThrow(testToken.setOwner, [owned, newOwner.publicKey, owner, []])); - await testToken.setOwner(owned, owner.publicKey, newOwner, []); -} - -export async function mintTo(): Promise { - const connection = await getConnection(); - const payer = await newAccountWithLamports( - connection, - 100000000000 /* wag */, - ); - mintableOwner = new Account(); - testMintableAccountOwner = new Account(); - [testMintableToken, testMintableAccount] = await Token.createMint( - connection, - payer, - mintableOwner.publicKey, - testMintableAccountOwner.publicKey, - new u64(10000), - 2, - programId, - true, - ); - - { - const mintInfo = await testMintableToken.getMintInfo(); - assert(mintInfo.decimals == 2); - if (mintInfo.owner === null) { - throw new Error('owner should not be null'); - } else { - assert(mintInfo.owner.equals(mintableOwner.publicKey)); - } - - const accountInfo = await testMintableToken.getAccountInfo( - testMintableAccount, - ); - assert(accountInfo.mint.equals(testMintableToken.publicKey)); - assert(accountInfo.owner.equals(testMintableAccountOwner.publicKey)); - assert(accountInfo.amount.toNumber() == 10000); - assert(accountInfo.delegate == null); - assert(accountInfo.delegatedAmount.toNumber() == 0); - } - - const dest = await testMintableToken.createAccount( - testMintableAccountOwner.publicKey, - ); - await testMintableToken.mintTo(dest, mintableOwner, [], 42); - - { - const mintInfo = await testMintableToken.getMintInfo(); - assert(mintInfo.decimals == 2); - if (mintInfo.owner === null) { - throw new Error('owner should not be null'); - } else { - assert(mintInfo.owner.equals(mintableOwner.publicKey)); - } - - const accountInfo = await testMintableToken.getAccountInfo(dest); - assert(accountInfo.mint.equals(testMintableToken.publicKey)); - assert(accountInfo.owner.equals(testMintableAccountOwner.publicKey)); - assert(accountInfo.amount.toNumber() == 42); - assert(accountInfo.delegate == null); - assert(accountInfo.delegatedAmount.toNumber() == 0); - } -} - -export async function burn(): Promise { - let accountInfo = await testToken.getAccountInfo(testAccount); - const amount = accountInfo.amount.toNumber(); - - await testToken.burn(testAccount, testAccountOwner, [], 1); - await sleep(500); - - accountInfo = await testToken.getAccountInfo(testAccount); - assert(accountInfo.amount.toNumber() == amount - 1); -} - -export async function multisig(): Promise { - const m = 2; - const n = 5; - - let signerAccounts = []; - for (var i = 0; i < n; i++) { - signerAccounts.push(new Account()); - } - let signerPublicKeys = []; - signerAccounts.forEach(account => signerPublicKeys.push(account.publicKey)); - const multisig = await testToken.createMultisig(m, signerPublicKeys); - - const multisigInfo = await testToken.getMultisigInfo(multisig); - assert(multisigInfo.m === m); - assert(multisigInfo.n === n); - assert(multisigInfo.signer1.equals(signerPublicKeys[0])); - assert(multisigInfo.signer2.equals(signerPublicKeys[1])); - assert(multisigInfo.signer3.equals(signerPublicKeys[2])); - assert(multisigInfo.signer4.equals(signerPublicKeys[3])); - assert(multisigInfo.signer5.equals(signerPublicKeys[4])); - - const multisigOwnedAccount = await testToken.createAccount(multisig); - const finalDest = await testToken.createAccount(multisig); - await testToken.transfer( - testAccount, - multisigOwnedAccount, - testAccountOwner, - [], - 2, - ); - - // Transfer via multisig - await testToken.transfer( - multisigOwnedAccount, - finalDest, - multisig, - signerAccounts, - 1, - ); - await sleep(500); - let accountInfo = await testToken.getAccountInfo(finalDest); - assert(accountInfo.amount.toNumber() == 1); - - // Approve via multisig - { - const delegate = new PublicKey(); - await testToken.approve( - multisigOwnedAccount, - delegate, - multisig, - signerAccounts, - 1, - ); - const accountInfo = await testToken.getAccountInfo(multisigOwnedAccount); - assert(accountInfo.delegate != null); - if (accountInfo.delegate != null) { - assert(accountInfo.delegate.equals(delegate)); - assert(accountInfo.delegatedAmount.toNumber() == 1); - } - } - - // MintTo via multisig - { - let accountInfo = await testMintableToken.getAccountInfo( - testMintableAccount, - ); - const initialAmount = accountInfo.amount.toNumber(); - await testMintableToken.setOwner( - testMintableToken.publicKey, - multisig, - mintableOwner, - [], - ); - await testMintableToken.mintTo( - testMintableAccount, - multisig, - signerAccounts, - 42, - ); - accountInfo = await testMintableToken.getAccountInfo(testMintableAccount); - assert(accountInfo.amount.toNumber() == initialAmount + 42); - } - - // SetOwner of mint via multisig - { - await testMintableToken.setOwner( - testMintableToken.publicKey, - mintableOwner.publicKey, - multisig, - signerAccounts, - ); - const mintInfo = await testMintableToken.getMintInfo(); - assert(mintInfo.owner != null); - if (mintInfo.owner != null) { - assert(mintInfo.owner.equals(mintableOwner.publicKey)); - } - } - - // SetOwner of account via multisig - { - const newOwner = new PublicKey(); - await testToken.setOwner( - multisigOwnedAccount, - newOwner, - multisig, - signerAccounts, - ); - const accountInfo = await testToken.getAccountInfo(multisigOwnedAccount); - assert(accountInfo.owner.equals(newOwner)); - } -} - -export async function failOnCloseAccount(): Promise { - const connection = await getConnection(); - const owner = new Account(); - const close = await testToken.createAccount(owner.publicKey); - - let close_balance; - let info = await connection.getAccountInfo(close); - if (info != null) { - close_balance = info.lamports; - } else { - throw new Error('Account not found'); - } - - // Initialize destination account to isolate source of failure - const balanceNeeded = await connection.getMinimumBalanceForRentExemption(0); - const dest = await newAccountWithLamports(connection, balanceNeeded); - - info = await connection.getAccountInfo(dest.publicKey); - if (info != null) { - assert(info.lamports == balanceNeeded); - } else { - throw new Error('Account not found'); - } - - assert(didThrow(testToken.closeAccount, [close, dest.publicKey, owner, []])); - - info = await connection.getAccountInfo(close); - if (info != null) { - assert(info.lamports == close_balance); - } else { - throw new Error('Account not found'); - } -} - -export async function nativeToken(): Promise { - const connection = await getConnection(); - - const mintPublicKey = new PublicKey( - 'So11111111111111111111111111111111111111111', - ); - const payer = await newAccountWithLamports( - connection, - 100000000000 /* wag */, - ); - const token = new Token(connection, mintPublicKey, programId, payer); - const owner = new Account(); - const native = await token.createAccount(owner.publicKey); - let accountInfo = await token.getAccountInfo(native); - assert(accountInfo.isNative); - let balance; - let info = await connection.getAccountInfo(native); - if (info != null) { - balance = info.lamports; - } else { - throw new Error('Account not found'); - } - - const balanceNeeded = await connection.getMinimumBalanceForRentExemption(0); - const dest = await newAccountWithLamports(connection, balanceNeeded); - await token.closeAccount(native, dest.publicKey, owner, []); - info = await connection.getAccountInfo(native); - if (info != null) { - throw new Error('Account not burned'); - } - info = await connection.getAccountInfo(dest.publicKey); - if (info != null) { - assert(info.lamports == balanceNeeded + balance); - } else { - throw new Error('Account not found'); - } -} diff --git a/program/js/client/layout.js b/program/js/client/layout.js deleted file mode 100644 index 6c3e06b..0000000 --- a/program/js/client/layout.js +++ /dev/null @@ -1,47 +0,0 @@ -// @flow - -import * as BufferLayout from 'buffer-layout'; - -/** - * Layout for a public key - */ -export const publicKey = (property: string = 'publicKey'): Object => { - return BufferLayout.blob(32, property); -}; - -/** - * Layout for a 64bit unsigned value - */ -export const uint64 = (property: string = 'uint64'): Object => { - return BufferLayout.blob(8, property); -}; - -/** - * Layout for a Rust String type - */ -export const rustString = (property: string = 'string') => { - const rsl = BufferLayout.struct( - [ - BufferLayout.u32('length'), - BufferLayout.u32('lengthPadding'), - BufferLayout.blob(BufferLayout.offset(BufferLayout.u32(), -8), 'chars'), - ], - property, - ); - const _decode = rsl.decode.bind(rsl); - const _encode = rsl.encode.bind(rsl); - - rsl.decode = (buffer, offset) => { - const data = _decode(buffer, offset); - return data.chars.toString('utf8'); - }; - - rsl.encode = (str, buffer, offset) => { - const data = { - chars: Buffer.from(str, 'utf8'), - }; - return _encode(data, buffer, offset); - }; - - return rsl; -}; diff --git a/program/js/client/token.js b/program/js/client/token.js deleted file mode 100644 index e59a2ab..0000000 --- a/program/js/client/token.js +++ /dev/null @@ -1,1242 +0,0 @@ -/** - * @flow - */ - -import assert from 'assert'; -import BN from 'bn.js'; -import * as BufferLayout from 'buffer-layout'; -import { - Account, - PublicKey, - SystemProgram, - Transaction, - TransactionInstruction, -} from '@solana/web3.js'; -import type {Connection, TransactionSignature} from '@solana/web3.js'; - -import * as Layout from './layout'; -import {sendAndConfirmTransaction} from './util/send-and-confirm-transaction'; - -/** - * 64-bit value - */ -export class u64 extends BN { - /** - * Convert to Buffer representation - */ - toBuffer(): Buffer { - const a = super.toArray().reverse(); - const b = Buffer.from(a); - if (b.length === 8) { - return b; - } - assert(b.length < 8, 'u64 too large'); - - const zeroPad = Buffer.alloc(8); - b.copy(zeroPad); - return zeroPad; - } - - /** - * Construct a u64 from Buffer representation - */ - static fromBuffer(buffer: Buffer): u64 { - assert(buffer.length === 8, `Invalid buffer length: ${buffer.length}`); - return new BN( - [...buffer] - .reverse() - .map(i => `00${i.toString(16)}`.slice(-2)) - .join(''), - 16, - ); - } -} - -function isAccount(accountOrPublicKey: any): boolean { - return 'publicKey' in accountOrPublicKey; -} - -/** - * Information about the mint - */ -type MintInfo = {| - /** - * Owner of the mint, given authority to mint new tokens - */ - owner: null | PublicKey, - - /** - * Number of base 10 digits to the right of the decimal place - */ - decimals: number, - - /** - * Is this mint initialized - */ - initialized: boolean, -|}; - -const MintLayout = BufferLayout.struct([ - BufferLayout.u32('option'), - Layout.publicKey('owner'), - BufferLayout.u8('decimals'), - BufferLayout.u8('is_initialized'), - BufferLayout.u16('padding'), -]); - -/** - * Information about an account - */ -type AccountInfo = {| - /** - * The mint associated with this account - */ - mint: PublicKey, - - /** - * Owner of this account - */ - owner: PublicKey, - - /** - * Amount of tokens this account holds - */ - amount: u64, - - /** - * The delegate for this account - */ - delegate: null | PublicKey, - - /** - * The amount of tokens the delegate authorized to the delegate - */ - delegatedAmount: u64, - - /** - * Is this account initialized - */ - isInitialized: boolean, - - /** - * Is this a native token account - */ - isNative: boolean, -|}; - -/** - * @private - */ -const AccountLayout = BufferLayout.struct([ - Layout.publicKey('mint'), - Layout.publicKey('owner'), - Layout.uint64('amount'), - BufferLayout.u32('option'), - Layout.publicKey('delegate'), - BufferLayout.u8('is_initialized'), - BufferLayout.u8('is_native'), - BufferLayout.u16('padding'), - Layout.uint64('delegatedAmount'), -]); - -/** - * Information about an multisig - */ -type MultisigInfo = {| - /** - * The number of signers required - */ - m: number, - - /** - * Number of possible signers, corresponds to the - * number of `signers` that are valid. - */ - n: number, - - /** - * Is this mint initialized - */ - initialized: boolean, - - /** - * The signers - */ - signer1: PublicKey, - signer2: PublicKey, - signer3: PublicKey, - signer4: PublicKey, - signer5: PublicKey, - signer6: PublicKey, - signer7: PublicKey, - signer8: PublicKey, - signer9: PublicKey, - signer10: PublicKey, - signer11: PublicKey, -|}; - -/** - * @private - */ -const MultisigLayout = BufferLayout.struct([ - BufferLayout.u8('m'), - BufferLayout.u8('n'), - BufferLayout.u8('is_initialized'), - Layout.publicKey('signer1'), - Layout.publicKey('signer2'), - Layout.publicKey('signer3'), - Layout.publicKey('signer4'), - Layout.publicKey('signer5'), - Layout.publicKey('signer6'), - Layout.publicKey('signer7'), - Layout.publicKey('signer8'), - Layout.publicKey('signer9'), - Layout.publicKey('signer10'), - Layout.publicKey('signer11'), -]); - -type TokenAndPublicKey = [Token, PublicKey]; // This type exists to workaround an esdoc parse error - -/** - * An ERC20-like Token - */ -export class Token { - /** - * @private - */ - connection: Connection; - - /** - * The public key identifying this mint - */ - publicKey: PublicKey; - - /** - * Program Identifier for the Token program - */ - programId: PublicKey; - - /** - * Fee payer - */ - payer: Account; - - /** - * Create a Token object attached to the specific mint - * - * @param connection The connection to use - * @param token Public key of the mint - * @param programId token programId - * @param payer Payer of fees - */ - constructor( - connection: Connection, - publicKey: PublicKey, - programId: PublicKey, - payer: Account, - ) { - Object.assign(this, {connection, publicKey, programId, payer}); - } - - /** - * Get the minimum balance for the mint to be rent exempt - * - * @return Number of lamports required - */ - static async getMinBalanceRentForExemptMint( - connection: Connection, - ): Promise { - return await connection.getMinimumBalanceForRentExemption(MintLayout.span); - } - - /** - * Get the minimum balance for the account to be rent exempt - * - * @return Number of lamports required - */ - static async getMinBalanceRentForExemptAccount( - connection: Connection, - ): Promise { - return await connection.getMinimumBalanceForRentExemption( - AccountLayout.span, - ); - } - - /** - * Get the minimum balance for the multsig to be rent exempt - * - * @return Number of lamports required - */ - static async getMinBalanceRentForExemptMultisig( - connection: Connection, - ): Promise { - return await connection.getMinimumBalanceForRentExemption( - MultisigLayout.span, - ); - } - - /** - * Creates and initializes a token. - * - * @param connection The connection to use - * @param owner User account that will own the returned account - * @param supply Initial supply to mint - * @param decimals Location of the decimal place - * @param programId Optional token programId, uses the system programId by default - * @return Token object for the newly minted token, Public key of the account - * holding the total amount of new tokens - */ - static async createMint( - connection: Connection, - payer: Account, - mintOwner: PublicKey, - accountOwner: PublicKey, - supply: u64, - decimals: number, - programId: PublicKey, - is_owned: boolean = false, - ): Promise { - let transaction; - const mintAccount = new Account(); - const token = new Token( - connection, - mintAccount.publicKey, - programId, - payer, - ); - const initialAccountPublicKey = await token.createAccount(accountOwner); - - // Allocate memory for the account - const balanceNeeded = await Token.getMinBalanceRentForExemptMint( - connection, - ); - transaction = SystemProgram.createAccount({ - fromPubkey: payer.publicKey, - newAccountPubkey: mintAccount.publicKey, - lamports: balanceNeeded, - space: MintLayout.span, - programId, - }); - - // Create the mint - let keys = [ - {pubkey: mintAccount.publicKey, isSigner: false, isWritable: true}, - ]; - if (supply.toNumber() != 0) { - keys.push({ - pubkey: initialAccountPublicKey, - isSigner: false, - isWritable: true, - }); - } - if (is_owned) { - keys.push({pubkey: mintOwner, isSigner: false, isWritable: false}); - } - const commandDataLayout = BufferLayout.struct([ - BufferLayout.u8('instruction'), - Layout.uint64('supply'), - BufferLayout.u8('decimals'), - ]); - let data = Buffer.alloc(1024); - { - const encodeLength = commandDataLayout.encode( - { - instruction: 0, // InitializeMint instruction - supply: supply.toBuffer(), - decimals, - }, - data, - ); - data = data.slice(0, encodeLength); - } - transaction.add({ - keys, - programId, - data, - }); - - // Send the two instructions - await sendAndConfirmTransaction( - 'createAccount and InitializeMint', - connection, - transaction, - payer, - mintAccount, - ); - - return [token, initialAccountPublicKey]; - } - - /** - * Create and initializes a new account. - * - * This account may then be used as a `transfer()` or `approve()` destination - * - * @param owner User account that will own the new account - * @return Public key of the new empty account - */ - async createAccount(owner: PublicKey): Promise { - const mintAccount = new Account(); - let transaction; - - // Allocate memory for the account - const balanceNeeded = await Token.getMinBalanceRentForExemptAccount( - this.connection, - ); - transaction = SystemProgram.createAccount({ - fromPubkey: this.payer.publicKey, - newAccountPubkey: mintAccount.publicKey, - lamports: balanceNeeded, - space: AccountLayout.span, - programId: this.programId, - }); - - // create the new account - const keys = [ - {pubkey: mintAccount.publicKey, isSigner: false, isWritable: true}, - {pubkey: this.publicKey, isSigner: false, isWritable: false}, - {pubkey: owner, isSigner: false, isWritable: false}, - ]; - const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]); - const data = Buffer.alloc(dataLayout.span); - dataLayout.encode( - { - instruction: 1, // InitializeAccount instruction - }, - data, - ); - transaction.add({ - keys, - programId: this.programId, - data, - }); - - // Send the two instructions - await sendAndConfirmTransaction( - 'createAccount and InitializeAccount', - this.connection, - transaction, - this.payer, - mintAccount, - ); - - return mintAccount.publicKey; - } - - /** - * Create and initializes a new multisig. - * - * This account may then be used for multisignature verification - * - * @param owner User account that will own the multsig account - * @return Public key of the new multisig account - */ - async createMultisig( - m: number, - signers: Array, - ): Promise { - const multisigAccount = new Account(); - let transaction; - - // Allocate memory for the account - const balanceNeeded = await Token.getMinBalanceRentForExemptMultisig( - this.connection, - ); - transaction = SystemProgram.createAccount({ - fromPubkey: this.payer.publicKey, - newAccountPubkey: multisigAccount.publicKey, - lamports: balanceNeeded, - space: MultisigLayout.span, - programId: this.programId, - }); - - // create the new account - let keys = [ - {pubkey: multisigAccount.publicKey, isSigner: false, isWritable: true}, - ]; - signers.forEach(signer => - keys.push({pubkey: signer, isSigner: false, isWritable: false}), - ); - const dataLayout = BufferLayout.struct([ - BufferLayout.u8('instruction'), - BufferLayout.u8('m'), - ]); - const data = Buffer.alloc(dataLayout.span); - dataLayout.encode( - { - instruction: 2, // InitializeM { - const info = await this.connection.getAccountInfo(this.publicKey); - if (info === null) { - throw new Error('Failed to find mint account'); - } - if (!info.owner.equals(this.programId)) { - throw new Error(`Invalid mint owner: ${JSON.stringify(info.owner)}`); - } - if (info.data.length != MintLayout.span) { - throw new Error(`Invalid mint size`); - } - - const data = Buffer.from(info.data); - const mintInfo = MintLayout.decode(data); - if (mintInfo.option === 0) { - mintInfo.owner = null; - } else { - mintInfo.owner = new PublicKey(mintInfo.owner); - } - return mintInfo; - } - - /** - * Retrieve account information - * - * @param account Public key of the account - */ - async getAccountInfo(account: PublicKey): Promise { - const info = await this.connection.getAccountInfo(account); - if (info === null) { - throw new Error('Failed to find account'); - } - if (!info.owner.equals(this.programId)) { - throw new Error(`Invalid account owner`); - } - if (info.data.length != AccountLayout.span) { - throw new Error(`Invalid account size`); - } - - const data = Buffer.from(info.data); - const accountInfo = AccountLayout.decode(data); - accountInfo.mint = new PublicKey(accountInfo.mint); - accountInfo.owner = new PublicKey(accountInfo.owner); - accountInfo.amount = u64.fromBuffer(accountInfo.amount); - accountInfo.isInitialized = accountInfo.isInitialized != 0; - accountInfo.isNative = accountInfo.isNative != 0; - if (accountInfo.option === 0) { - accountInfo.delegate = null; - accountInfo.delegatedAmount = new u64(); - } else { - accountInfo.delegate = new PublicKey(accountInfo.delegate); - accountInfo.delegatedAmount = u64.fromBuffer(accountInfo.delegatedAmount); - } - - if (!accountInfo.mint.equals(this.publicKey)) { - throw new Error( - `Invalid account mint: ${JSON.stringify( - accountInfo.mint, - )} !== ${JSON.stringify(this.publicKey)}`, - ); - } - return accountInfo; - } - - /** - * Retrieve Multisig information - * - * @param multisig Public key of the account - */ - async getMultisigInfo(multisig: PublicKey): Promise { - const info = await this.connection.getAccountInfo(multisig); - if (info === null) { - throw new Error('Failed to find multisig'); - } - if (!info.owner.equals(this.programId)) { - throw new Error(`Invalid multisig owner`); - } - if (info.data.length != MultisigLayout.span) { - throw new Error(`Invalid multisig size`); - } - - const data = Buffer.from(info.data); - const multisigInfo = MultisigLayout.decode(data); - multisigInfo.signer1 = new PublicKey(multisigInfo.signer1); - multisigInfo.signer2 = new PublicKey(multisigInfo.signer2); - multisigInfo.signer3 = new PublicKey(multisigInfo.signer3); - multisigInfo.signer4 = new PublicKey(multisigInfo.signer4); - multisigInfo.signer5 = new PublicKey(multisigInfo.signer5); - multisigInfo.signer6 = new PublicKey(multisigInfo.signer6); - multisigInfo.signer7 = new PublicKey(multisigInfo.signer7); - multisigInfo.signer8 = new PublicKey(multisigInfo.signer8); - multisigInfo.signer9 = new PublicKey(multisigInfo.signer9); - multisigInfo.signer10 = new PublicKey(multisigInfo.signer10); - multisigInfo.signer11 = new PublicKey(multisigInfo.signer11); - - return multisigInfo; - } - - /** - * Transfer tokens to another account - * - * @param source Source account - * @param destination Destination account - * @param authority Owner of the source account - * @param multiSigners Signing accounts if `authority` is a multiSig - * @param amount Number of tokens to transfer - */ - async transfer( - source: PublicKey, - destination: PublicKey, - authority: any, - multiSigners: Array, - amount: number | u64, - ): Promise { - let ownerPublicKey; - let signers; - if (isAccount(authority)) { - ownerPublicKey = authority.publicKey; - signers = [authority]; - } else { - ownerPublicKey = authority; - signers = multiSigners; - } - return await sendAndConfirmTransaction( - 'Transfer', - this.connection, - new Transaction().add( - Token.createTransferInstruction( - this.programId, - source, - destination, - ownerPublicKey, - multiSigners, - amount, - ), - ), - this.payer, - ...signers, - ); - } - - /** - * Grant a third-party permission to transfer up the specified number of tokens from an account - * - * @param account Public key of the account - * @param delegate Account authorized to perform a transfer tokens from the source account - * @param owner Owner of the source account - * @param multiSigners Signing accounts if `owner` is a multiSig - * @param amount Maximum number of tokens the delegate may transfer - */ - async approve( - account: PublicKey, - delegate: PublicKey, - owner: any, - multiSigners: Array, - amount: number | u64, - ): Promise { - let ownerPublicKey; - let signers; - if (isAccount(owner)) { - ownerPublicKey = owner.publicKey; - signers = [owner]; - } else { - ownerPublicKey = owner; - signers = multiSigners; - } - await sendAndConfirmTransaction( - 'Approve', - this.connection, - new Transaction().add( - Token.createApproveInstruction( - this.programId, - account, - delegate, - ownerPublicKey, - multiSigners, - amount, - ), - ), - this.payer, - ...signers, - ); - } - - /** - * Remove approval for the transfer of any remaining tokens - * - * @param account Public key of the account - * @param owner Owner of the source account - * @param multiSigners Signing accounts if `owner` is a multiSig - */ - async revoke( - account: PublicKey, - owner: any, - multiSigners: Array, - ): Promise { - let ownerPublicKey; - let signers; - if (isAccount(owner)) { - ownerPublicKey = owner.publicKey; - signers = [owner]; - } else { - ownerPublicKey = owner; - signers = multiSigners; - } - await sendAndConfirmTransaction( - 'Revoke', - this.connection, - new Transaction().add( - Token.createRevokeInstruction( - this.programId, - account, - ownerPublicKey, - multiSigners, - ), - ), - this.payer, - ...signers, - ); - } - - /** - * Assign a new owner to the account - * - * @param account Public key of the account - * @param newOwner New owner of the account - * @param owner Owner of the account - * @param multiSigners Signing accounts if `owner` is a multiSig - */ - async setOwner( - owned: PublicKey, - newOwner: PublicKey, - owner: any, - multiSigners: Array, - ): Promise { - let ownerPublicKey; - let signers; - if (isAccount(owner)) { - ownerPublicKey = owner.publicKey; - signers = [owner]; - } else { - ownerPublicKey = owner; - signers = multiSigners; - } - await sendAndConfirmTransaction( - 'SetOwner', - this.connection, - new Transaction().add( - Token.createSetOwnerInstruction( - this.programId, - owned, - newOwner, - ownerPublicKey, - multiSigners, - ), - ), - this.payer, - ...signers, - ); - } - - /** - * Mint new tokens - * - * @param mint Public key of the mint - * @param dest Public key of the account to mint to - * @param authority Owner of the mint - * @param multiSigners Signing accounts if `authority` is a multiSig - * @param amount ammount to mint - */ - async mintTo( - dest: PublicKey, - authority: any, - multiSigners: Array, - amount: number, - ): Promise { - let ownerPublicKey; - let signers; - if (isAccount(authority)) { - ownerPublicKey = authority.publicKey; - signers = [authority]; - } else { - ownerPublicKey = authority; - signers = multiSigners; - } - await sendAndConfirmTransaction( - 'MintTo', - this.connection, - new Transaction().add( - Token.createMintToInstruction( - this.programId, - this.publicKey, - dest, - ownerPublicKey, - multiSigners, - amount, - ), - ), - this.payer, - ...signers, - ); - } - - /** - * Burn tokens - * - * @param account Account to burn tokens from - * @param authority Public key account owner - * @param multiSigners Signing accounts if `authority` is a multiSig - * @param amount ammount to burn - */ - async burn( - account: PublicKey, - authority: any, - multiSigners: Array, - amount: number, - ): Promise { - let ownerPublicKey; - let signers; - if (isAccount(authority)) { - ownerPublicKey = authority.publicKey; - signers = [authority]; - } else { - ownerPublicKey = authority; - signers = multiSigners; - } - await sendAndConfirmTransaction( - 'Burn', - this.connection, - new Transaction().add( - Token.createBurnInstruction( - this.programId, - account, - ownerPublicKey, - multiSigners, - amount, - ), - ), - this.payer, - ...signers, - ); - } - - /** - * Burn account - * - * @param account Account to burn - * @param authority account owner - * @param multiSigners Signing accounts if `owner` is a multiSig - */ - async closeAccount( - account: PublicKey, - dest: PublicKey, - owner: any, - multiSigners: Array, - ): Promise { - let ownerPublicKey; - let signers; - if (isAccount(owner)) { - ownerPublicKey = owner.publicKey; - signers = [owner]; - } else { - ownerPublicKey = owner; - signers = multiSigners; - } - await sendAndConfirmTransaction( - 'CloseAccount', - this.connection, - new Transaction().add( - Token.createCloseAccountInstruction( - this.programId, - account, - dest, - ownerPublicKey, - multiSigners, - ), - ), - this.payer, - ...signers, - ); - } - - /** - * Construct a Transfer instruction - * - * @param source Source account - * @param destination Destination account - * @param authority Owner of the source account - * @param multiSigners Signing accounts if `authority` is a multiSig - * @param amount Number of tokens to transfer - */ - static createTransferInstruction( - programId: PublicKey, - source: PublicKey, - destination: PublicKey, - authority: any, - multiSigners: Array, - amount: number | u64, - ): TransactionInstruction { - const dataLayout = BufferLayout.struct([ - BufferLayout.u8('instruction'), - Layout.uint64('amount'), - ]); - - const data = Buffer.alloc(dataLayout.span); - dataLayout.encode( - { - instruction: 3, // Transfer instruction - amount: new u64(amount).toBuffer(), - }, - data, - ); - - let keys = [ - {pubkey: source, isSigner: false, isWritable: true}, - {pubkey: destination, isSigner: false, isWritable: true}, - ]; - if (isAccount(authority)) { - keys.push({ - pubkey: authority.publicKey, - isSigner: true, - isWritable: false, - }); - } else { - keys.push({pubkey: authority, isSigner: false, isWritable: false}); - multiSigners.forEach(signer => - keys.push({ - pubkey: signer.publicKey, - isSigner: true, - isWritable: false, - }), - ); - } - return new TransactionInstruction({ - keys, - programId: programId, - data, - }); - } - - /** - * Construct an Approve instruction - * - * @param account Public key of the account - * @param delegate Account authorized to perform a transfer of tokens from the source account - * @param owner Owner of the source account - * @param multiSigners Signing accounts if `owner` is a multiSig - * @param amount Maximum number of tokens the delegate may transfer - */ - static createApproveInstruction( - programId: PublicKey, - account: PublicKey, - delegate: PublicKey, - owner: any, - multiSigners: Array, - amount: number | u64, - ): TransactionInstruction { - const dataLayout = BufferLayout.struct([ - BufferLayout.u8('instruction'), - Layout.uint64('amount'), - ]); - - const data = Buffer.alloc(dataLayout.span); - dataLayout.encode( - { - instruction: 4, // Approve instruction - amount: new u64(amount).toBuffer(), - }, - data, - ); - - let keys = [ - {pubkey: account, isSigner: false, isWritable: true}, - {pubkey: delegate, isSigner: false, isWritable: false}, - ]; - if (isAccount(owner)) { - keys.push({pubkey: owner.publicKey, isSigner: true, isWritable: false}); - } else { - keys.push({pubkey: owner, isSigner: false, isWritable: false}); - multiSigners.forEach(signer => - keys.push({ - pubkey: signer.publicKey, - isSigner: true, - isWritable: false, - }), - ); - } - - return new TransactionInstruction({ - keys, - programId: programId, - data, - }); - } - - /** - * Construct an Approve instruction - * - * @param account Public key of the account - * @param delegate Account authorized to perform a transfer of tokens from the source account - * @param owner Owner of the source account - * @param multiSigners Signing accounts if `owner` is a multiSig - * @param amount Maximum number of tokens the delegate may transfer - */ - static createRevokeInstruction( - programId: PublicKey, - account: PublicKey, - owner: any, - multiSigners: Array, - ): TransactionInstruction { - const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]); - - const data = Buffer.alloc(dataLayout.span); - dataLayout.encode( - { - instruction: 5, // Approve instruction - }, - data, - ); - - let keys = [{pubkey: account, isSigner: false, isWritable: true}]; - if (isAccount(owner)) { - keys.push({pubkey: owner.publicKey, isSigner: true, isWritable: false}); - } else { - keys.push({pubkey: owner, isSigner: false, isWritable: false}); - multiSigners.forEach(signer => - keys.push({ - pubkey: signer.publicKey, - isSigner: true, - isWritable: false, - }), - ); - } - - return new TransactionInstruction({ - keys, - programId: programId, - data, - }); - } - - /** - * Construct a SetOwner instruction - * - * @param account Public key of the account - * @param newOwner New owner of the account - * @param owner Owner of the account - * @param multiSigners Signing accounts if `owner` is a multiSig - */ - static createSetOwnerInstruction( - programId: PublicKey, - owned: PublicKey, - newOwner: PublicKey, - owner: any, - multiSigners: Array, - ): TransactionInstruction { - const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]); - - const data = Buffer.alloc(dataLayout.span); - dataLayout.encode( - { - instruction: 6, // SetOwner instruction - }, - data, - ); - - let keys = [ - {pubkey: owned, isSigner: false, isWritable: true}, - {pubkey: newOwner, isSigner: false, isWritable: false}, - ]; - if (isAccount(owner)) { - keys.push({pubkey: owner.publicKey, isSigner: true, isWritable: false}); - } else { - keys.push({pubkey: owner, isSigner: false, isWritable: false}); - multiSigners.forEach(signer => - keys.push({ - pubkey: signer.publicKey, - isSigner: true, - isWritable: false, - }), - ); - } - - return new TransactionInstruction({ - keys, - programId: programId, - data, - }); - } - - /** - * Construct a MintTo instruction - * - * @param dest Public key of the account to mint to - * @param authority Owner of the mint - * @param multiSigners Signing accounts if `authority` is a multiSig - - * @param amount amount to mint - */ - static createMintToInstruction( - programId: PublicKey, - mint: PublicKey, - dest: PublicKey, - authority: any, - multiSigners: Array, - amount: number, - ): TransactionInstruction { - const dataLayout = BufferLayout.struct([ - BufferLayout.u8('instruction'), - Layout.uint64('amount'), - ]); - - const data = Buffer.alloc(dataLayout.span); - dataLayout.encode( - { - instruction: 7, // MintTo instruction - amount: new u64(amount).toBuffer(), - }, - data, - ); - - let keys = [ - {pubkey: mint, isSigner: false, isWritable: true}, - {pubkey: dest, isSigner: false, isWritable: true}, - ]; - if (isAccount(authority)) { - keys.push({ - pubkey: authority.publicKey, - isSigner: true, - isWritable: false, - }); - } else { - keys.push({pubkey: authority, isSigner: false, isWritable: false}); - multiSigners.forEach(signer => - keys.push({ - pubkey: signer.publicKey, - isSigner: true, - isWritable: false, - }), - ); - } - - return new TransactionInstruction({ - keys, - programId: programId, - data, - }); - } - - /** - * Construct a Burn instruction - * - * @param account Account to burn tokens from - * @param authority Public key account owner - * @param multiSigners Signing accounts if `authority` is a multiSig - * @param amount ammount to burn - */ - static createBurnInstruction( - programId: PublicKey, - account: PublicKey, - authority: any, - multiSigners: Array, - amount: number, - ): TransactionInstruction { - const dataLayout = BufferLayout.struct([ - BufferLayout.u8('instruction'), - Layout.uint64('amount'), - ]); - - const data = Buffer.alloc(dataLayout.span); - dataLayout.encode( - { - instruction: 8, // Burn instruction - amount: new u64(amount).toBuffer(), - }, - data, - ); - - let keys = [{pubkey: account, isSigner: false, isWritable: true}]; - if (isAccount(authority)) { - keys.push({ - pubkey: authority.publicKey, - isSigner: true, - isWritable: false, - }); - } else { - keys.push({pubkey: authority, isSigner: false, isWritable: false}); - multiSigners.forEach(signer => - keys.push({ - pubkey: signer.publicKey, - isSigner: true, - isWritable: false, - }), - ); - } - - return new TransactionInstruction({ - keys, - programId: programId, - data, - }); - } - - /** - * Construct a Burn instruction - * - * @param account Account to burn tokens from - * @param owner account owner - * @param multiSigners Signing accounts if `owner` is a multiSig - */ - static createCloseAccountInstruction( - programId: PublicKey, - account: PublicKey, - dest: PublicKey, - owner: any, - multiSigners: Array, - ): TransactionInstruction { - const dataLayout = BufferLayout.struct([BufferLayout.u8('instruction')]); - const data = Buffer.alloc(dataLayout.span); - dataLayout.encode( - { - instruction: 9, // CloseAccount instruction - }, - data, - ); - - let keys = [ - {pubkey: account, isSigner: false, isWritable: true}, - {pubkey: dest, isSigner: false, isWritable: true}, - ]; - if (isAccount(owner)) { - keys.push({pubkey: owner.publicKey, isSigner: true, isWritable: false}); - } else { - keys.push({pubkey: owner, isSigner: false, isWritable: false}); - multiSigners.forEach(signer => - keys.push({ - pubkey: signer.publicKey, - isSigner: true, - isWritable: false, - }), - ); - } - - return new TransactionInstruction({ - keys, - programId: programId, - data, - }); - } -} diff --git a/program/js/client/util/new-account-with-lamports.js b/program/js/client/util/new-account-with-lamports.js deleted file mode 100644 index 423972f..0000000 --- a/program/js/client/util/new-account-with-lamports.js +++ /dev/null @@ -1,25 +0,0 @@ -// @flow - -import {Account, Connection} from '@solana/web3.js'; - -import {sleep} from './sleep'; - -export async function newAccountWithLamports( - connection: Connection, - lamports: number = 1000000, -): Promise { - const account = new Account(); - - let retries = 30; - await connection.requestAirdrop(account.publicKey, lamports); - for (;;) { - await sleep(500); - if (lamports == (await connection.getBalance(account.publicKey))) { - return account; - } - if (--retries <= 0) { - break; - } - } - throw new Error(`Airdrop of ${lamports} failed`); -} diff --git a/program/js/client/util/new-system-account-with-airdrop.js b/program/js/client/util/new-system-account-with-airdrop.js deleted file mode 100644 index 2df5779..0000000 --- a/program/js/client/util/new-system-account-with-airdrop.js +++ /dev/null @@ -1,17 +0,0 @@ -// @flow - -import {Account, Connection} from '@solana/web3.js'; - -/** - * Create a new system account and airdrop it some lamports - * - * @private - */ -export async function newSystemAccountWithAirdrop( - connection: Connection, - lamports: number = 1, -): Promise { - const account = new Account(); - await connection.requestAirdrop(account.publicKey, lamports); - return account; -} diff --git a/program/js/client/util/send-and-confirm-transaction.js b/program/js/client/util/send-and-confirm-transaction.js deleted file mode 100644 index 1088c55..0000000 --- a/program/js/client/util/send-and-confirm-transaction.js +++ /dev/null @@ -1,21 +0,0 @@ -// @flow - -import {sendAndConfirmTransaction as realSendAndConfirmTransaction} from '@solana/web3.js'; -import type { - Account, - Connection, - Transaction, - TransactionSignature, -} from '@solana/web3.js'; - -export function sendAndConfirmTransaction( - title: string, - connection: Connection, - transaction: Transaction, - ...signers: Array -): Promise { - return realSendAndConfirmTransaction(connection, transaction, signers, { - confirmations: 1, - skipPreflight: true, - }); -} diff --git a/program/js/client/util/sleep.js b/program/js/client/util/sleep.js deleted file mode 100644 index 961d48c..0000000 --- a/program/js/client/util/sleep.js +++ /dev/null @@ -1,6 +0,0 @@ -// @flow - -// zzz -export function sleep(ms: number): Promise { - return new Promise(resolve => setTimeout(resolve, ms)); -} diff --git a/program/js/client/util/store.js b/program/js/client/util/store.js deleted file mode 100644 index fdafb9d..0000000 --- a/program/js/client/util/store.js +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Simple file-based datastore - * - * @flow - */ - -import path from 'path'; -import fs from 'mz/fs'; -import mkdirp from 'mkdirp-promise'; - -export class Store { - dir = path.join(__dirname, 'store'); - - async load(uri: string): Promise { - const filename = path.join(this.dir, uri); - const data = await fs.readFile(filename, 'utf8'); - const config = JSON.parse(data); - return config; - } - - async save(uri: string, config: Object): Promise { - await mkdirp(this.dir); - const filename = path.join(this.dir, uri); - await fs.writeFile(filename, JSON.stringify(config), 'utf8'); - } -} diff --git a/program/js/cluster-devnet.env b/program/js/cluster-devnet.env deleted file mode 100644 index eccad41..0000000 --- a/program/js/cluster-devnet.env +++ /dev/null @@ -1,2 +0,0 @@ -LIVE=1 -CLUSTER=devnet diff --git a/program/js/cluster-mainnet-beta.env b/program/js/cluster-mainnet-beta.env deleted file mode 100644 index 17239e5..0000000 --- a/program/js/cluster-mainnet-beta.env +++ /dev/null @@ -1,2 +0,0 @@ -LIVE=1 -CLUSTER=mainnet-beta diff --git a/program/js/cluster-testnet.env b/program/js/cluster-testnet.env deleted file mode 100644 index 9bcfb25..0000000 --- a/program/js/cluster-testnet.env +++ /dev/null @@ -1,2 +0,0 @@ -LIVE=1 -CLUSTER=testnet diff --git a/program/js/flow-typed/bn.js.js b/program/js/flow-typed/bn.js.js deleted file mode 100644 index 5412142..0000000 --- a/program/js/flow-typed/bn.js.js +++ /dev/null @@ -1,4 +0,0 @@ -declare module 'bn.js' { - // TODO: Fill in types - declare module.exports: any; -} diff --git a/program/js/flow-typed/bs58.js b/program/js/flow-typed/bs58.js deleted file mode 100644 index 9223fad..0000000 --- a/program/js/flow-typed/bs58.js +++ /dev/null @@ -1,6 +0,0 @@ -declare module 'bs58' { - declare module.exports: { - encode(input: Buffer): string; - decode(input: string): Buffer; - }; -} diff --git a/program/js/flow-typed/buffer-layout.js b/program/js/flow-typed/buffer-layout.js deleted file mode 100644 index 9dd66fe..0000000 --- a/program/js/flow-typed/buffer-layout.js +++ /dev/null @@ -1,4 +0,0 @@ -declare module 'buffer-layout' { - // TODO: Fill in types - declare module.exports: any; -} diff --git a/program/js/flow-typed/mkdirp-promise_vx.x.x.js b/program/js/flow-typed/mkdirp-promise_vx.x.x.js deleted file mode 100644 index 35bb720..0000000 --- a/program/js/flow-typed/mkdirp-promise_vx.x.x.js +++ /dev/null @@ -1,32 +0,0 @@ -// flow-typed signature: 65e18196703cbb222ea294226e99826d -// flow-typed version: <>/mkdirp-promise_v5.0.1/flow_v0.84.0 - -/** - * This is an autogenerated libdef stub for: - * - * 'mkdirp-promise' - * - * Fill this stub out by replacing all the `any` types. - * - * Once filled out, we encourage you to share your work with the - * community by sending a pull request to: - * https://github.com/flowtype/flow-typed - */ - -declare module 'mkdirp-promise' { - declare module.exports: any; -} - -/** - * We include stubs for each file inside this npm package in case you need to - * require those files directly. Feel free to delete any files that aren't - * needed. - */ -declare module 'mkdirp-promise/lib/index' { - declare module.exports: any; -} - -// Filename aliases -declare module 'mkdirp-promise/lib/index.js' { - declare module.exports: $Exports<'mkdirp-promise/lib/index'>; -} diff --git a/program/js/flow-typed/npm/mz_vx.x.x.js b/program/js/flow-typed/npm/mz_vx.x.x.js deleted file mode 100644 index b4b12f4..0000000 --- a/program/js/flow-typed/npm/mz_vx.x.x.js +++ /dev/null @@ -1,73 +0,0 @@ -// flow-typed signature: ed29f42bf4f4916e4f3ba1f5e7343c9d -// flow-typed version: <>/mz_v2.7.0/flow_v0.81.0 - -/** - * This is an autogenerated libdef stub for: - * - * 'mz' - * - * Fill this stub out by replacing all the `any` types. - * - * Once filled out, we encourage you to share your work with the - * community by sending a pull request to: - * https://github.com/flowtype/flow-typed - */ - -declare module 'mz' { - declare module.exports: any; -} - -/** - * We include stubs for each file inside this npm package in case you need to - * require those files directly. Feel free to delete any files that aren't - * needed. - */ -declare module 'mz/child_process' { - declare module.exports: any; -} - -declare module 'mz/crypto' { - declare module.exports: any; -} - -declare module 'mz/dns' { - declare module.exports: any; -} - -declare module 'mz/fs' { - declare module.exports: any; -} - -declare module 'mz/readline' { - declare module.exports: any; -} - -declare module 'mz/zlib' { - declare module.exports: any; -} - -// Filename aliases -declare module 'mz/child_process.js' { - declare module.exports: $Exports<'mz/child_process'>; -} -declare module 'mz/crypto.js' { - declare module.exports: $Exports<'mz/crypto'>; -} -declare module 'mz/dns.js' { - declare module.exports: $Exports<'mz/dns'>; -} -declare module 'mz/fs.js' { - declare module.exports: $Exports<'mz/fs'>; -} -declare module 'mz/index' { - declare module.exports: $Exports<'mz'>; -} -declare module 'mz/index.js' { - declare module.exports: $Exports<'mz'>; -} -declare module 'mz/readline.js' { - declare module.exports: $Exports<'mz/readline'>; -} -declare module 'mz/zlib.js' { - declare module.exports: $Exports<'mz/zlib'>; -} diff --git a/program/js/flow-typed/semver.js b/program/js/flow-typed/semver.js deleted file mode 100644 index 3d6c800..0000000 --- a/program/js/flow-typed/semver.js +++ /dev/null @@ -1,3 +0,0 @@ -declare module 'semver' { - declare module.exports: any; -} diff --git a/program/js/module.d.ts b/program/js/module.d.ts deleted file mode 100644 index 4295bad..0000000 --- a/program/js/module.d.ts +++ /dev/null @@ -1,160 +0,0 @@ -declare module '@solana/spl-token' { - import {Buffer} from 'buffer'; - import { PublicKey, TransactionInstruction, TransactionSignature, Connection } from "@solana/web3.js"; - import BN from 'bn.js'; - - // === client/token.js === - export class u64 extends BN { - toBuffer(): Buffer; - static fromBuffer(buffer: Buffer): u64; - } - export type MintInfo = { - owner: null | PublicKey, - decimals: number, - initialized: boolean, - }; - export type AccountInfo = { - mint: PublicKey, - owner: PublicKey, - amount: u64, - delegate: null | PublicKey, - delegatedAmount: u64, - isInitialized: boolean, - isNative: boolean, - }; - export type MultisigInfo = { - m: number, - n: number, - initialized: boolean, - signer1: PublicKey, - signer2: PublicKey, - signer3: PublicKey, - signer4: PublicKey, - signer5: PublicKey, - signer6: PublicKey, - signer7: PublicKey, - signer8: PublicKey, - signer9: PublicKey, - signer10: PublicKey, - signer11: PublicKey, - }; - export type TokenAndPublicKey = [Token, PublicKey]; - export class Token { - constructor( - connection: Connection, - publicKey: PublicKey, - programId: PublicKey, - payer: Account, - ); - static createMint( - connection: Connection, - payer: Account, - mintOwner: PublicKey, - accountOwner: PublicKey, - supply: u64, - decimals: number, - programId: PublicKey, - is_owned: boolean, - ): Promise; - static getAccount(connection: Connection): Promise; - createAccount(owner: PublicKey): Promise; - createMultisig(m: number, signers: Array): Promise; - getMintInfo(): Promise; - getAccountInfo(account: PublicKey): Promise; - getMultisigInfo(multisig: PublicKey): Promise; - transfer( - source: PublicKey, - destination: PublicKey, - authority: Account | PublicKey, - multiSigners: Array, - amount: number | u64, - ): Promise; - approve( - account: PublicKey, - delegate: PublicKey, - owner: Account | PublicKey, - multiSigners: Array, - amount: number | u64, - ): Promise; - revoke( - account: PublicKey, - owner: Account | PublicKey, - multiSigners: Array, - ): Promise; - setOwner( - owned: PublicKey, - newOwner: PublicKey, - owner: Account | PublicKey, - multiSigners: Array, - ): Promise; - mintTo( - dest: PublicKey, - authority: Account | PublicKey, - multiSigners: Array, - amount: number, - ): Promise; - burn( - account: PublicKey, - authority: Account | PublicKey, - multiSigners: Array, - amount: number, - ): Promise; - closeAccount( - account: PublicKey, - dest: PublicKey, - owner: Account | PublicKey, - multiSigners: Array, - ): Promise; - static createTransferInstruction( - programId: PublicKey, - source: PublicKey, - destination: PublicKey, - authority: Account | PublicKey, - multiSigners: Array, - amount: number | u64, - ): TransactionInstruction; - static createApproveInstruction( - programId: PublicKey, - account: PublicKey, - delegate: PublicKey, - owner: Account | PublicKey, - multiSigners: Array, - amount: number | u64, - ): TransactionInstruction; - static createRevokeInstruction( - programId: PublicKey, - account: PublicKey, - owner: Account | PublicKey, - multiSigners: Array, - ): TransactionInstruction; - static createSetOwnerInstruction( - programId: PublicKey, - owned: PublicKey, - newOwner: PublicKey, - owner: Account | PublicKey, - multiSigners: Array, - ): TransactionInstruction; - static createMintToInstruction( - programId: PublicKey, - mint: PublicKey, - dest: PublicKey, - authority: Account | PublicKey, - multiSigners: Array, - amount: number, - ): TransactionInstruction; - static createBurnInstruction( - programId: PublicKey, - account: PublicKey, - authority: Account | PublicKey, - multiSigners: Array, - amount: number, - ): TransactionInstruction; - static createCloseAccountInstruction( - programId: PublicKey, - account: PublicKey, - dest: PublicKey, - owner: Account | PublicKey, - multiSigners: Array, - ): TransactionInstruction; - } -} diff --git a/program/js/module.flow.js b/program/js/module.flow.js deleted file mode 100644 index 1d81229..0000000 --- a/program/js/module.flow.js +++ /dev/null @@ -1,163 +0,0 @@ -/** - * Flow Library definition for spl-token - * - * This file is manually maintained - * - */ - -declare module '@solana/spl-token' { - // === client/token.js === - declare export class u64 extends BN { - toBuffer(): Buffer; - static fromBuffer(buffer: Buffer): u64; - } - declare export type MintInfo = {| - owner: null | PublicKey, - decimals: number, - initialized: boolean, - |}; - declare export type AccountInfo = {| - mint: PublicKey, - owner: PublicKey, - amount: u64, - delegate: null | PublicKey, - delegatedAmount: u64, - isInitialized: boolean, - isNative: boolean, - |}; - declare export type MultisigInfo = {| - m: number, - n: number, - initialized: boolean, - signer1: PublicKey, - signer2: PublicKey, - signer3: PublicKey, - signer4: PublicKey, - signer5: PublicKey, - signer6: PublicKey, - signer7: PublicKey, - signer8: PublicKey, - signer9: PublicKey, - signer10: PublicKey, - signer11: PublicKey, - |}; - declare export type TokenAndPublicKey = [Token, PublicKey]; - declare export class Token { - constructor( - connection: Connection, - publicKey: PublicKey, - programId: PublicKey, - payer: Account, - ): Token; - static createMint( - connection: Connection, - payer: Account, - mintOwner: PublicKey, - accountOwner: PublicKey, - supply: u64, - decimals: number, - programId: PublicKey, - is_owned: boolean, - ): Promise; - static getAccount(connection: Connection): Promise; - createAccount(owner: PublicKey): Promise; - createMultisig(m: number, signers: Array): Promise; - getMintInfo(): Promise; - getAccountInfo(account: PublicKey): Promise; - getMultisigInfo(multisig: PublicKey): Promise; - transfer( - source: PublicKey, - destination: PublicKey, - authority: Account | PublicKey, - multiSigners: Array, - amount: number | u64, - ): Promise; - approve( - account: PublicKey, - delegate: PublicKey, - owner: Account | PublicKey, - multiSigners: Array, - amount: number | u64, - ): Promise; - revoke( - account: PublicKey, - owner: Account | PublicKey, - multiSigners: Array, - ): Promise; - setOwner( - owned: PublicKey, - newOwner: PublicKey, - owner: Account | PublicKey, - multiSigners: Array, - ): Promise; - mintTo( - dest: PublicKey, - authority: Account | PublicKey, - multiSigners: Array, - amount: number, - ): Promise; - burn( - account: PublicKey, - authority: Account | PublicKey, - multiSigners: Array, - amount: number, - ): Promise; - closeAccount( - account: PublicKey, - dest: PublicKey, - owner: Account | PublicKey, - multiSigners: Array, - ): Promise; - static createTransferInstruction( - programId: PublicKey, - source: PublicKey, - destination: PublicKey, - authority: Account | PublicKey, - multiSigners: Array, - amount: number | u64, - ): TransactionInstruction; - static createApproveInstruction( - programId: PublicKey, - account: PublicKey, - delegate: PublicKey, - owner: Account | PublicKey, - multiSigners: Array, - amount: number | u64, - ): TransactionInstruction; - static createRevokeInstruction( - programId: PublicKey, - account: PublicKey, - owner: Account | PublicKey, - multiSigners: Array, - ): TransactionInstruction; - static createSetOwnerInstruction( - programId: PublicKey, - owned: PublicKey, - newOwner: PublicKey, - owner: Account | PublicKey, - multiSigners: Array, - ): TransactionInstruction; - static createMintToInstruction( - programId: PublicKey, - mint: PublicKey, - dest: PublicKey, - authority: Account | PublicKey, - multiSigners: Array, - amount: number, - ): TransactionInstruction; - static createBurnInstruction( - programId: PublicKey, - account: PublicKey, - authority: Account | PublicKey, - multiSigners: Array, - amount: number, - ): TransactionInstruction; - static createCloseAccountInstruction( - programId: PublicKey, - account: PublicKey, - dest: PublicKey, - owner: Account | PublicKey, - multiSigners: Array, - ): TransactionInstructio; - } -} diff --git a/program/js/package-lock.json b/program/js/package-lock.json deleted file mode 100644 index 91be906..0000000 --- a/program/js/package-lock.json +++ /dev/null @@ -1,8383 +0,0 @@ -{ - "name": "@solana/spl-token", - "version": "0.0.5", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "101": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/101/-/101-1.6.3.tgz", - "integrity": "sha512-4dmQ45yY0Dx24Qxp+zAsNLlMF6tteCyfVzgbulvSyC7tCyd3V8sW76sS0tHq8NpcbXfWTKasfyfzU1Kd86oKzw==", - "requires": { - "clone": "^1.0.2", - "deep-eql": "^0.1.3", - "keypather": "^1.10.2" - } - }, - "@babel/cli": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.10.5.tgz", - "integrity": "sha512-j9H9qSf3kLdM0Ao3aGPbGZ73mEA9XazuupcS6cDGWuiyAcANoguhP0r2Lx32H5JGw4sSSoHG3x/mxVnHgvOoyA==", - "dev": true, - "requires": { - "chokidar": "^2.1.8", - "commander": "^4.0.1", - "convert-source-map": "^1.1.0", - "fs-readdir-recursive": "^1.1.0", - "glob": "^7.0.0", - "lodash": "^4.17.19", - "make-dir": "^2.1.0", - "slash": "^2.0.0", - "source-map": "^0.5.0" - }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "optional": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "optional": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dev": true, - "optional": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } - }, - "commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "optional": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "optional": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "optional": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "@babel/code-frame": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", - "integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==", - "dev": true, - "requires": { - "@babel/highlight": "^7.0.0" - } - }, - "@babel/compat-data": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.11.0.tgz", - "integrity": "sha512-TPSvJfv73ng0pfnEOh17bYMPQbI95+nGWc71Ss4vZdRBHTDqmM9Z8ZV4rYz8Ks7sfzc95n30k6ODIq5UGnXcYQ==", - "dev": true, - "requires": { - "browserslist": "^4.12.0", - "invariant": "^2.2.4", - "semver": "^5.5.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "@babel/core": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.1.tgz", - "integrity": "sha512-XqF7F6FWQdKGGWAzGELL+aCO1p+lRY5Tj5/tbT3St1G8NaH70jhhDIKknIZaDans0OQBG5wRAldROLHSt44BgQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.0", - "@babel/helper-module-transforms": "^7.11.0", - "@babel/helpers": "^7.10.4", - "@babel/parser": "^7.11.1", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.11.0", - "@babel/types": "^7.11.0", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.19", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/generator": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz", - "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==", - "dev": true, - "requires": { - "@babel/types": "^7.11.0", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-module-transforms": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz", - "integrity": "sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-simple-access": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/template": "^7.10.4", - "@babel/types": "^7.11.0", - "lodash": "^4.17.19" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", - "dev": true, - "requires": { - "@babel/types": "^7.11.0" - } - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.11.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.1.tgz", - "integrity": "sha512-u9QMIRdKVF7hfEkb3nu2LgZDIzCQPv+yHD9Eg6ruoJLjkrQ9fFz4IBSlF/9XwoNri9+2F1IY+dYuOfZrXq8t3w==", - "dev": true - }, - "@babel/traverse": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz", - "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.11.0", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.11.0", - "@babel/parser": "^7.11.0", - "@babel/types": "^7.11.0", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" - } - }, - "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.5.tgz", - "integrity": "sha512-3vXxr3FEW7E7lJZiWQ3bM4+v/Vyr9C+hpolQ8BGFr9Y8Ri2tFLWTixmwKBafDujO1WVah4fhZBeU1bieKdghig==", - "dev": true, - "requires": { - "@babel/types": "^7.10.5", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-annotate-as-pure": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.10.4.tgz", - "integrity": "sha512-XQlqKQP4vXFB7BN8fEEerrmYvHp3fK/rBkRFz9jaJbzK0B1DSfej9Kc7ZzE8Z/OnId1jpJdNAZ3BFQjWG68rcA==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-builder-binary-assignment-operator-visitor": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.10.4.tgz", - "integrity": "sha512-L0zGlFrGWZK4PbT8AszSfLTM5sDU1+Az/En9VrdT8/LmEiJt4zXt+Jve9DCAnQcbqDhCI+29y/L93mrDzddCcg==", - "dev": true, - "requires": { - "@babel/helper-explode-assignable-expression": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.10.4.tgz", - "integrity": "sha512-a3rYhlsGV0UHNDvrtOXBg8/OpfV0OKTkxKPzIplS1zpx7CygDcWWxckxZeDd3gzPzC4kUT0A4nVFDK0wGMh4MQ==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.10.4", - "browserslist": "^4.12.0", - "invariant": "^2.2.4", - "levenary": "^1.1.1", - "semver": "^5.5.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "@babel/helper-create-class-features-plugin": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz", - "integrity": "sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-member-expression-to-functions": "^7.10.5", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4" - } - }, - "@babel/helper-create-regexp-features-plugin": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.10.4.tgz", - "integrity": "sha512-2/hu58IEPKeoLF45DBwx3XFqsbCXmkdAay4spVr2x0jYgRxrSNp+ePwvSsy9g6YSaNDcKIQVPXk1Ov8S2edk2g==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-regex": "^7.10.4", - "regexpu-core": "^4.7.0" - } - }, - "@babel/helper-define-map": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-define-map/-/helper-define-map-7.10.5.tgz", - "integrity": "sha512-fMw4kgFB720aQFXSVaXr79pjjcW5puTCM16+rECJ/plGS+zByelE8l9nCpV1GibxTnFVmUuYG9U8wYfQHdzOEQ==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/types": "^7.10.5", - "lodash": "^4.17.19" - } - }, - "@babel/helper-explode-assignable-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.10.4.tgz", - "integrity": "sha512-4K71RyRQNPRrR85sr5QY4X3VwG4wtVoXZB9+L3r1Gp38DhELyHCtovqydRi7c1Ovb17eRGiQ/FD5s8JdU0Uy5A==", - "dev": true, - "requires": { - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.10.4.tgz", - "integrity": "sha512-wljroF5PgCk2juF69kanHVs6vrLwIPNp6DLD+Lrl3hoQ3PpPPikaDRNFA+0t81NOoMt2DL6WW/mdU8k4k6ZzuA==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.5.tgz", - "integrity": "sha512-HiqJpYD5+WopCXIAbQDG0zye5XYVvcO9w/DHp5GsaGkRUaamLj2bEtu6i8rnGGprAhHM3qidCMgp71HF4endhA==", - "dev": true, - "requires": { - "@babel/types": "^7.10.5" - }, - "dependencies": { - "@babel/types": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.5.tgz", - "integrity": "sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - } - } - }, - "@babel/helper-module-imports": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", - "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - }, - "dependencies": { - "@babel/types": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.5.tgz", - "integrity": "sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - } - } - }, - "@babel/helper-module-transforms": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.10.5.tgz", - "integrity": "sha512-4P+CWMJ6/j1W915ITJaUkadLObmCRRSC234uctJfn/vHrsLNxsR8dwlcXv9ZhJWzl77awf+mWXSZEKt5t0OnlA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-simple-access": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.5", - "lodash": "^4.17.19" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz", - "integrity": "sha512-pySBTeoUff56fL5CBU2hWm9TesA4r/rOkI9DyJLvvgz09MB9YtfIYe3iBriVaYNaPe+Alua0vBIOVOLs2buWhg==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.5.tgz", - "integrity": "sha512-wfryxy4bE1UivvQKSQDU4/X6dr+i8bctjUjj8Zyt3DQy7NtPizJXT8M52nqpNKL+nq2PW8lxk4ZqLj0fD4B4hQ==", - "dev": true - }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/types": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.5.tgz", - "integrity": "sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - } - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", - "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - }, - "dependencies": { - "@babel/types": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.5.tgz", - "integrity": "sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - } - } - }, - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - }, - "@babel/helper-regex": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-regex/-/helper-regex-7.10.5.tgz", - "integrity": "sha512-68kdUAzDrljqBrio7DYAEgCoJHxppJOERHOgOrDN7WjOzP0ZQ1LsSDRXcemzVZaLvjaJsJEESb6qt+znNuENDg==", - "dev": true, - "requires": { - "lodash": "^4.17.19" - } - }, - "@babel/helper-remap-async-to-generator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.10.4.tgz", - "integrity": "sha512-86Lsr6NNw3qTNl+TBcF1oRZMaVzJtbWTyTko+CQL/tvNvcGYEFKbLXDPxtW0HKk3McNOk4KzY55itGWCAGK5tg==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-wrap-function": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-replace-supers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", - "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/generator": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.5.tgz", - "integrity": "sha512-3vXxr3FEW7E7lJZiWQ3bM4+v/Vyr9C+hpolQ8BGFr9Y8Ri2tFLWTixmwKBafDujO1WVah4fhZBeU1bieKdghig==", - "dev": true, - "requires": { - "@babel/types": "^7.10.5", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - } - }, - "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz", - "integrity": "sha512-pySBTeoUff56fL5CBU2hWm9TesA4r/rOkI9DyJLvvgz09MB9YtfIYe3iBriVaYNaPe+Alua0vBIOVOLs2buWhg==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.5.tgz", - "integrity": "sha512-wfryxy4bE1UivvQKSQDU4/X6dr+i8bctjUjj8Zyt3DQy7NtPizJXT8M52nqpNKL+nq2PW8lxk4ZqLj0fD4B4hQ==", - "dev": true - }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/traverse": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.10.5.tgz", - "integrity": "sha512-yc/fyv2gUjPqzTz0WHeRJH2pv7jA9kA7mBX2tXl/x5iOE81uaVPuGPtaYk7wmkx4b67mQ7NqI8rmT2pF47KYKQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.10.5", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4", - "@babel/parser": "^7.10.5", - "@babel/types": "^7.10.5", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" - } - }, - "@babel/types": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.5.tgz", - "integrity": "sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - } - } - }, - "@babel/helper-simple-access": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz", - "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==", - "dev": true, - "requires": { - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.5.tgz", - "integrity": "sha512-wfryxy4bE1UivvQKSQDU4/X6dr+i8bctjUjj8Zyt3DQy7NtPizJXT8M52nqpNKL+nq2PW8lxk4ZqLj0fD4B4hQ==", - "dev": true - }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/types": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.5.tgz", - "integrity": "sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - } - } - }, - "@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.11.0.tgz", - "integrity": "sha512-0XIdiQln4Elglgjbwo9wuJpL/K7AGCY26kmEt0+pRP0TAj4jjyNq1MjoRvikrTVqKcx4Gysxt4cXvVFXP/JO2Q==", - "dev": true, - "requires": { - "@babel/types": "^7.11.0" - }, - "dependencies": { - "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - } - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.4.tgz", - "integrity": "sha512-pySBTeoUff56fL5CBU2hWm9TesA4r/rOkI9DyJLvvgz09MB9YtfIYe3iBriVaYNaPe+Alua0vBIOVOLs2buWhg==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true - }, - "@babel/helper-wrap-function": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.10.4.tgz", - "integrity": "sha512-6py45WvEF0MhiLrdxtRjKjufwLL1/ob2qDJgg5JgNdojBAZSAKnAjkyOCNug6n+OBl4VW76XjvgSFTdaMcW0Ug==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/helpers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz", - "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==", - "dev": true, - "requires": { - "@babel/template": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - } - }, - "@babel/highlight": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", - "integrity": "sha512-UFMC4ZeFC48Tpvj7C8UgLvtkaUuovQX+5xNWrsIoMG8o2z+XFKjKaN9iVmS84dPwVN00W4wPmqvYoZF3EGAsfw==", - "dev": true, - "requires": { - "chalk": "^2.0.0", - "esutils": "^2.0.2", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.1.tgz", - "integrity": "sha512-ObN6h1v2fTJSmUXoS3nMQ92LbDK9be4TV+6G+omQlGJFdcUX5heKi1LZ1YnRMIgwTLEj3E24bT6tYni50rlCfQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/node": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/node/-/node-7.10.5.tgz", - "integrity": "sha512-suosS7zZ2roj+fYVCnDuVezUbRc0sdoyF0Gj/1FzWxD4ebbGiBGtL5qyqHH4NO34B5m4vWWYWgyNhSsrqS8vwA==", - "dev": true, - "requires": { - "@babel/register": "^7.10.5", - "commander": "^4.0.1", - "core-js": "^3.2.1", - "lodash": "^4.17.19", - "node-environment-flags": "^1.0.5", - "regenerator-runtime": "^0.13.4", - "resolve": "^1.13.1", - "v8flags": "^3.1.1" - }, - "dependencies": { - "commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true - }, - "core-js": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.6.5.tgz", - "integrity": "sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA==", - "dev": true - }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==", - "dev": true - }, - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - } - } - }, - "@babel/parser": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.5.tgz", - "integrity": "sha512-wfryxy4bE1UivvQKSQDU4/X6dr+i8bctjUjj8Zyt3DQy7NtPizJXT8M52nqpNKL+nq2PW8lxk4ZqLj0fD4B4hQ==", - "dev": true - }, - "@babel/plugin-proposal-async-generator-functions": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.10.5.tgz", - "integrity": "sha512-cNMCVezQbrRGvXJwm9fu/1sJj9bHdGAgKodZdLqOQIpfoH3raqmRPBM17+lh7CzhiKRRBrGtZL9WcjxSoGYUSg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.10.4", - "@babel/plugin-syntax-async-generators": "^7.8.0" - } - }, - "@babel/plugin-proposal-class-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz", - "integrity": "sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-proposal-dynamic-import": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.10.4.tgz", - "integrity": "sha512-up6oID1LeidOOASNXgv/CFbgBqTuKJ0cJjz6An5tWD+NVBNlp3VNSBxv2ZdU7SYl3NxJC7agAQDApZusV6uFwQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-dynamic-import": "^7.8.0" - } - }, - "@babel/plugin-proposal-export-namespace-from": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.10.4.tgz", - "integrity": "sha512-aNdf0LY6/3WXkhh0Fdb6Zk9j1NMD8ovj3F6r0+3j837Pn1S1PdNtcwJ5EG9WkVPNHPxyJDaxMaAOVq4eki0qbg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3" - } - }, - "@babel/plugin-proposal-json-strings": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.10.4.tgz", - "integrity": "sha512-fCL7QF0Jo83uy1K0P2YXrfX11tj3lkpN7l4dMv9Y9VkowkhkQDwFHFd8IiwyK5MZjE8UpbgokkgtcReH88Abaw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.0" - } - }, - "@babel/plugin-proposal-logical-assignment-operators": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.11.0.tgz", - "integrity": "sha512-/f8p4z+Auz0Uaf+i8Ekf1iM7wUNLcViFUGiPxKeXvxTSl63B875YPiVdUDdem7hREcI0E0kSpEhS8tF5RphK7Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" - } - }, - "@babel/plugin-proposal-nullish-coalescing-operator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.10.4.tgz", - "integrity": "sha512-wq5n1M3ZUlHl9sqT2ok1T2/MTt6AXE0e1Lz4WzWBr95LsAZ5qDXe4KnFuauYyEyLiohvXFMdbsOTMyLZs91Zlw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0" - } - }, - "@babel/plugin-proposal-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.10.4.tgz", - "integrity": "sha512-73/G7QoRoeNkLZFxsoCCvlg4ezE4eM+57PnOqgaPOozd5myfj7p0muD1mRVJvbUWbOzD+q3No2bWbaKy+DJ8DA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-numeric-separator": "^7.10.4" - } - }, - "@babel/plugin-proposal-object-rest-spread": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.11.0.tgz", - "integrity": "sha512-wzch41N4yztwoRw0ak+37wxwJM2oiIiy6huGCoqkvSTA9acYWcPfn9Y4aJqmFFJ70KTJUu29f3DQ43uJ9HXzEA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-transform-parameters": "^7.10.4" - } - }, - "@babel/plugin-proposal-optional-catch-binding": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.10.4.tgz", - "integrity": "sha512-LflT6nPh+GK2MnFiKDyLiqSqVHkQnVf7hdoAvyTnnKj9xB3docGRsdPuxp6qqqW19ifK3xgc9U5/FwrSaCNX5g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0" - } - }, - "@babel/plugin-proposal-optional-chaining": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.11.0.tgz", - "integrity": "sha512-v9fZIu3Y8562RRwhm1BbMRxtqZNFmFA2EG+pT2diuU8PT3H6T/KXoZ54KgYisfOFZHV6PfvAiBIZ9Rcz+/JCxA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-skip-transparent-expression-wrappers": "^7.11.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.0" - } - }, - "@babel/plugin-proposal-private-methods": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.10.4.tgz", - "integrity": "sha512-wh5GJleuI8k3emgTg5KkJK6kHNsGEr0uBTDBuQUBJwckk9xs1ez79ioheEVVxMLyPscB0LfkbVHslQqIzWV6Bw==", - "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-proposal-unicode-property-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.10.4.tgz", - "integrity": "sha512-H+3fOgPnEXFL9zGYtKQe4IDOPKYlZdF1kqFDQRRb8PK4B8af1vAGK04tF5iQAAsui+mHNBQSAtd2/ndEDe9wuA==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz", - "integrity": "sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-dynamic-import": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz", - "integrity": "sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-export-namespace-from": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz", - "integrity": "sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-flow": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.10.4.tgz", - "integrity": "sha512-yxQsX1dJixF4qEEdzVbst3SZQ58Nrooz8NV9Z9GL4byTE25BvJgl5lf0RECUf0fh28rZBb/RYTWn/eeKwCMrZQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.10.4.tgz", - "integrity": "sha512-ni1brg4lXEmWyafKr0ccFWkJG0CeMt4WV1oyeBW6EFObF4oOHclbkj5cARxAPQyAQ2UTuplJyK4nfkXIMMFvsQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-arrow-functions": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.10.4.tgz", - "integrity": "sha512-9J/oD1jV0ZCBcgnoFWFq1vJd4msoKb/TCpGNFyyLt0zABdcvgK3aYikZ8HjzB14c26bc7E3Q1yugpwGy2aTPNA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-async-to-generator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.10.4.tgz", - "integrity": "sha512-F6nREOan7J5UXTLsDsZG3DXmZSVofr2tGNwfdrVwkDWHfQckbQXnXSPfD7iO+c/2HGqycwyLST3DnZ16n+cBJQ==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-remap-async-to-generator": "^7.10.4" - } - }, - "@babel/plugin-transform-block-scoped-functions": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.10.4.tgz", - "integrity": "sha512-WzXDarQXYYfjaV1szJvN3AD7rZgZzC1JtjJZ8dMHUyiK8mxPRahynp14zzNjU3VkPqPsO38CzxiWO1c9ARZ8JA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-block-scoping": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.10.5.tgz", - "integrity": "sha512-6Ycw3hjpQti0qssQcA6AMSFDHeNJ++R6dIMnpRqUjFeBBTmTDPa8zgF90OVfTvAo11mXZTlVUViY1g8ffrURLg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-classes": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.10.4.tgz", - "integrity": "sha512-2oZ9qLjt161dn1ZE0Ms66xBncQH4In8Sqw1YWgBUZuGVJJS5c0OFZXL6dP2MRHrkU/eKhWg8CzFJhRQl50rQxA==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-define-map": "^7.10.4", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4", - "globals": "^11.1.0" - } - }, - "@babel/plugin-transform-computed-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.10.4.tgz", - "integrity": "sha512-JFwVDXcP/hM/TbyzGq3l/XWGut7p46Z3QvqFMXTfk6/09m7xZHJUN9xHfsv7vqqD4YnfI5ueYdSJtXqqBLyjBw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-destructuring": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.10.4.tgz", - "integrity": "sha512-+WmfvyfsyF603iPa6825mq6Qrb7uLjTOsa3XOFzlYcYDHSS4QmpOWOL0NNBY5qMbvrcf3tq0Cw+v4lxswOBpgA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-dotall-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.10.4.tgz", - "integrity": "sha512-ZEAVvUTCMlMFAbASYSVQoxIbHm2OkG2MseW6bV2JjIygOjdVv8tuxrCTzj1+Rynh7ODb8GivUy7dzEXzEhuPaA==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-duplicate-keys": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.10.4.tgz", - "integrity": "sha512-GL0/fJnmgMclHiBTTWXNlYjYsA7rDrtsazHG6mglaGSTh0KsrW04qml+Bbz9FL0LcJIRwBWL5ZqlNHKTkU3xAA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-exponentiation-operator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.10.4.tgz", - "integrity": "sha512-S5HgLVgkBcRdyQAHbKj+7KyuWx8C6t5oETmUuwz1pt3WTWJhsUV0WIIXuVvfXMxl/QQyHKlSCNNtaIamG8fysw==", - "dev": true, - "requires": { - "@babel/helper-builder-binary-assignment-operator-visitor": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-flow-strip-types": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.10.4.tgz", - "integrity": "sha512-XTadyuqNst88UWBTdLjM+wEY7BFnY2sYtPyAidfC7M/QaZnSuIZpMvLxqGT7phAcnGyWh/XQFLKcGf04CnvxSQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-flow": "^7.10.4" - } - }, - "@babel/plugin-transform-for-of": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.4.tgz", - "integrity": "sha512-ItdQfAzu9AlEqmusA/65TqJ79eRcgGmpPPFvBnGILXZH975G0LNjP1yjHvGgfuCxqrPPueXOPe+FsvxmxKiHHQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.10.4.tgz", - "integrity": "sha512-OcDCq2y5+E0dVD5MagT5X+yTRbcvFjDI2ZVAottGH6tzqjx/LKpgkUepu3hp/u4tZBzxxpNGwLsAvGBvQ2mJzg==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.10.4.tgz", - "integrity": "sha512-Xd/dFSTEVuUWnyZiMu76/InZxLTYilOSr1UlHV+p115Z/Le2Fi1KXkJUYz0b42DfndostYlPub3m8ZTQlMaiqQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-member-expression-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.10.4.tgz", - "integrity": "sha512-0bFOvPyAoTBhtcJLr9VcwZqKmSjFml1iVxvPL0ReomGU53CX53HsM4h2SzckNdkQcHox1bpAqzxBI1Y09LlBSw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-modules-amd": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.10.5.tgz", - "integrity": "sha512-elm5uruNio7CTLFItVC/rIzKLfQ17+fX7EVz5W0TMgIHFo1zY0Ozzx+lgwhL4plzl8OzVn6Qasx5DeEFyoNiRw==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.10.5", - "@babel/helper-plugin-utils": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-commonjs": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.10.4.tgz", - "integrity": "sha512-Xj7Uq5o80HDLlW64rVfDBhao6OX89HKUmb+9vWYaLXBZOma4gA6tw4Ni1O5qVDoZWUV0fxMYA0aYzOawz0l+1w==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-simple-access": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-systemjs": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.10.5.tgz", - "integrity": "sha512-f4RLO/OL14/FP1AEbcsWMzpbUz6tssRaeQg11RH1BP/XnPpRoVwgeYViMFacnkaw4k4wjRSjn3ip1Uw9TaXuMw==", - "dev": true, - "requires": { - "@babel/helper-hoist-variables": "^7.10.4", - "@babel/helper-module-transforms": "^7.10.5", - "@babel/helper-plugin-utils": "^7.10.4", - "babel-plugin-dynamic-import-node": "^2.3.3" - } - }, - "@babel/plugin-transform-modules-umd": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.10.4.tgz", - "integrity": "sha512-mohW5q3uAEt8T45YT7Qc5ws6mWgJAaL/8BfWD9Dodo1A3RKWli8wTS+WiQ/knF+tXlPirW/1/MqzzGfCExKECA==", - "dev": true, - "requires": { - "@babel/helper-module-transforms": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.10.4.tgz", - "integrity": "sha512-V6LuOnD31kTkxQPhKiVYzYC/Jgdq53irJC/xBSmqcNcqFGV+PER4l6rU5SH2Vl7bH9mLDHcc0+l9HUOe4RNGKA==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4" - } - }, - "@babel/plugin-transform-new-target": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.10.4.tgz", - "integrity": "sha512-YXwWUDAH/J6dlfwqlWsztI2Puz1NtUAubXhOPLQ5gjR/qmQ5U96DY4FQO8At33JN4XPBhrjB8I4eMmLROjjLjw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-object-super": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.10.4.tgz", - "integrity": "sha512-5iTw0JkdRdJvr7sY0vHqTpnruUpTea32JHmq/atIWqsnNussbRzjEDyWep8UNztt1B5IusBYg8Irb0bLbiEBCQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4" - } - }, - "@babel/plugin-transform-parameters": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.10.5.tgz", - "integrity": "sha512-xPHwUj5RdFV8l1wuYiu5S9fqWGM2DrYc24TMvUiRrPVm+SM3XeqU9BcokQX/kEUe+p2RBwy+yoiR1w/Blq6ubw==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-property-literals": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.10.4.tgz", - "integrity": "sha512-ofsAcKiUxQ8TY4sScgsGeR2vJIsfrzqvFb9GvJ5UdXDzl+MyYCaBj/FGzXuv7qE0aJcjWMILny1epqelnFlz8g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-regenerator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.10.4.tgz", - "integrity": "sha512-3thAHwtor39A7C04XucbMg17RcZ3Qppfxr22wYzZNcVIkPHfpM9J0SO8zuCV6SZa265kxBJSrfKTvDCYqBFXGw==", - "dev": true, - "requires": { - "regenerator-transform": "^0.14.2" - } - }, - "@babel/plugin-transform-reserved-words": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.10.4.tgz", - "integrity": "sha512-hGsw1O6Rew1fkFbDImZIEqA8GoidwTAilwCyWqLBM9f+e/u/sQMQu7uX6dyokfOayRuuVfKOW4O7HvaBWM+JlQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-runtime": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.11.0.tgz", - "integrity": "sha512-LFEsP+t3wkYBlis8w6/kmnd6Kb1dxTd+wGJ8MlxTGzQo//ehtqlVL4S9DNUa53+dtPSQobN2CXx4d81FqC58cw==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "resolve": "^1.8.1", - "semver": "^5.5.1" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "@babel/plugin-transform-shorthand-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.10.4.tgz", - "integrity": "sha512-AC2K/t7o07KeTIxMoHneyX90v3zkm5cjHJEokrPEAGEy3UCp8sLKfnfOIGdZ194fyN4wfX/zZUWT9trJZ0qc+Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-spread": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.11.0.tgz", - "integrity": "sha512-UwQYGOqIdQJe4aWNyS7noqAnN2VbaczPLiEtln+zPowRNlD+79w3oi2TWfYe0eZgd+gjZCbsydN7lzWysDt+gw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-skip-transparent-expression-wrappers": "^7.11.0" - } - }, - "@babel/plugin-transform-sticky-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.10.4.tgz", - "integrity": "sha512-Ddy3QZfIbEV0VYcVtFDCjeE4xwVTJWTmUtorAJkn6u/92Z/nWJNV+mILyqHKrUxXYKA2EoCilgoPePymKL4DvQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-regex": "^7.10.4" - } - }, - "@babel/plugin-transform-template-literals": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.10.5.tgz", - "integrity": "sha512-V/lnPGIb+KT12OQikDvgSuesRX14ck5FfJXt6+tXhdkJ+Vsd0lDCVtF6jcB4rNClYFzaB2jusZ+lNISDk2mMMw==", - "dev": true, - "requires": { - "@babel/helper-annotate-as-pure": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-typeof-symbol": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.10.4.tgz", - "integrity": "sha512-QqNgYwuuW0y0H+kUE/GWSR45t/ccRhe14Fs/4ZRouNNQsyd4o3PG4OtHiIrepbM2WKUBDAXKCAK/Lk4VhzTaGA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-unicode-escapes": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.10.4.tgz", - "integrity": "sha512-y5XJ9waMti2J+e7ij20e+aH+fho7Wb7W8rNuu72aKRwCHFqQdhkdU2lo3uZ9tQuboEJcUFayXdARhcxLQ3+6Fg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-transform-unicode-regex": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.10.4.tgz", - "integrity": "sha512-wNfsc4s8N2qnIwpO/WP2ZiSyjfpTamT2C9V9FDH/Ljub9zw6P3SjkXcFmc0RQUt96k2fmIvtla2MMjgTwIAC+A==", - "dev": true, - "requires": { - "@babel/helper-create-regexp-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/preset-env": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.11.0.tgz", - "integrity": "sha512-2u1/k7rG/gTh02dylX2kL3S0IJNF+J6bfDSp4DI2Ma8QN6Y9x9pmAax59fsCk6QUQG0yqH47yJWA+u1I1LccAg==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.11.0", - "@babel/helper-compilation-targets": "^7.10.4", - "@babel/helper-module-imports": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-proposal-async-generator-functions": "^7.10.4", - "@babel/plugin-proposal-class-properties": "^7.10.4", - "@babel/plugin-proposal-dynamic-import": "^7.10.4", - "@babel/plugin-proposal-export-namespace-from": "^7.10.4", - "@babel/plugin-proposal-json-strings": "^7.10.4", - "@babel/plugin-proposal-logical-assignment-operators": "^7.11.0", - "@babel/plugin-proposal-nullish-coalescing-operator": "^7.10.4", - "@babel/plugin-proposal-numeric-separator": "^7.10.4", - "@babel/plugin-proposal-object-rest-spread": "^7.11.0", - "@babel/plugin-proposal-optional-catch-binding": "^7.10.4", - "@babel/plugin-proposal-optional-chaining": "^7.11.0", - "@babel/plugin-proposal-private-methods": "^7.10.4", - "@babel/plugin-proposal-unicode-property-regex": "^7.10.4", - "@babel/plugin-syntax-async-generators": "^7.8.0", - "@babel/plugin-syntax-class-properties": "^7.10.4", - "@babel/plugin-syntax-dynamic-import": "^7.8.0", - "@babel/plugin-syntax-export-namespace-from": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.0", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.0", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.0", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.0", - "@babel/plugin-syntax-optional-chaining": "^7.8.0", - "@babel/plugin-syntax-top-level-await": "^7.10.4", - "@babel/plugin-transform-arrow-functions": "^7.10.4", - "@babel/plugin-transform-async-to-generator": "^7.10.4", - "@babel/plugin-transform-block-scoped-functions": "^7.10.4", - "@babel/plugin-transform-block-scoping": "^7.10.4", - "@babel/plugin-transform-classes": "^7.10.4", - "@babel/plugin-transform-computed-properties": "^7.10.4", - "@babel/plugin-transform-destructuring": "^7.10.4", - "@babel/plugin-transform-dotall-regex": "^7.10.4", - "@babel/plugin-transform-duplicate-keys": "^7.10.4", - "@babel/plugin-transform-exponentiation-operator": "^7.10.4", - "@babel/plugin-transform-for-of": "^7.10.4", - "@babel/plugin-transform-function-name": "^7.10.4", - "@babel/plugin-transform-literals": "^7.10.4", - "@babel/plugin-transform-member-expression-literals": "^7.10.4", - "@babel/plugin-transform-modules-amd": "^7.10.4", - "@babel/plugin-transform-modules-commonjs": "^7.10.4", - "@babel/plugin-transform-modules-systemjs": "^7.10.4", - "@babel/plugin-transform-modules-umd": "^7.10.4", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.10.4", - "@babel/plugin-transform-new-target": "^7.10.4", - "@babel/plugin-transform-object-super": "^7.10.4", - "@babel/plugin-transform-parameters": "^7.10.4", - "@babel/plugin-transform-property-literals": "^7.10.4", - "@babel/plugin-transform-regenerator": "^7.10.4", - "@babel/plugin-transform-reserved-words": "^7.10.4", - "@babel/plugin-transform-shorthand-properties": "^7.10.4", - "@babel/plugin-transform-spread": "^7.11.0", - "@babel/plugin-transform-sticky-regex": "^7.10.4", - "@babel/plugin-transform-template-literals": "^7.10.4", - "@babel/plugin-transform-typeof-symbol": "^7.10.4", - "@babel/plugin-transform-unicode-escapes": "^7.10.4", - "@babel/plugin-transform-unicode-regex": "^7.10.4", - "@babel/preset-modules": "^0.1.3", - "@babel/types": "^7.11.0", - "browserslist": "^4.12.0", - "core-js-compat": "^3.6.2", - "invariant": "^2.2.2", - "levenary": "^1.1.1", - "semver": "^5.5.0" - }, - "dependencies": { - "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "@babel/preset-flow": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.10.4.tgz", - "integrity": "sha512-XI6l1CptQCOBv+ZKYwynyswhtOKwpZZp5n0LG1QKCo8erRhqjoQV6nvx61Eg30JHpysWQSBwA2AWRU3pBbSY5g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-transform-flow-strip-types": "^7.10.4" - } - }, - "@babel/preset-modules": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.3.tgz", - "integrity": "sha512-Ra3JXOHBq2xd56xSF7lMKXdjBn3T772Y1Wet3yWnkDly9zHvJki029tAFzvAAK5cf4YV3yoxuP61crYRol6SVg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@babel/plugin-proposal-unicode-property-regex": "^7.4.4", - "@babel/plugin-transform-dotall-regex": "^7.4.4", - "@babel/types": "^7.4.4", - "esutils": "^2.0.2" - } - }, - "@babel/register": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.10.5.tgz", - "integrity": "sha512-eYHdLv43nyvmPn9bfNfrcC4+iYNwdQ8Pxk1MFJuU/U5LpSYl/PH4dFMazCYZDFVi8ueG3shvO+AQfLrxpYulQw==", - "dev": true, - "requires": { - "find-cache-dir": "^2.0.0", - "lodash": "^4.17.19", - "make-dir": "^2.1.0", - "pirates": "^4.0.0", - "source-map-support": "^0.5.16" - } - }, - "@babel/runtime": { - "version": "7.11.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz", - "integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==", - "requires": { - "regenerator-runtime": "^0.13.4" - }, - "dependencies": { - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" - } - } - }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - } - } - }, - "@babel/traverse": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.10.5.tgz", - "integrity": "sha512-yc/fyv2gUjPqzTz0WHeRJH2pv7jA9kA7mBX2tXl/x5iOE81uaVPuGPtaYk7wmkx4b67mQ7NqI8rmT2pF47KYKQ==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/generator": "^7.10.5", - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4", - "@babel/parser": "^7.10.5", - "@babel/types": "^7.10.5", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.19" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "@babel/types": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.5.tgz", - "integrity": "sha512-ixV66KWfCI6GKoA/2H9v6bQdbfXEwwpOdQ8cRvb4F+eyvhlaHxWFMQB4+3d9QFJXZsiiiqVrewNV0DFEQpyT4Q==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", - "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.3", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", - "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", - "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.3", - "fastq": "^1.6.0" - } - }, - "@octokit/auth-token": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.4.2.tgz", - "integrity": "sha512-jE/lE/IKIz2v1+/P0u4fJqv0kYwXOTujKemJMFr6FeopsxlIK3+wKDCJGnysg81XID5TgZQbIfuJ5J0lnTiuyQ==", - "dev": true, - "requires": { - "@octokit/types": "^5.0.0" - } - }, - "@octokit/endpoint": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.5.tgz", - "integrity": "sha512-70K5u6zd45ItOny6aHQAsea8HHQjlQq85yqOMe+Aj8dkhN2qSJ9T+Q3YjUjEYfPRBcuUWNgMn62DQnP/4LAIiQ==", - "dev": true, - "requires": { - "@octokit/types": "^5.0.0", - "is-plain-object": "^4.0.0", - "universal-user-agent": "^6.0.0" - }, - "dependencies": { - "is-plain-object": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-4.1.1.tgz", - "integrity": "sha512-5Aw8LLVsDlZsETVMhoMXzqsXwQqr/0vlnBYzIXJbYo2F4yYlhLHs+Ez7Bod7IIQKWkJbJfxrWD7pA1Dw1TKrwA==", - "dev": true - }, - "universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", - "dev": true - } - } - }, - "@octokit/plugin-paginate-rest": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-1.1.2.tgz", - "integrity": "sha512-jbsSoi5Q1pj63sC16XIUboklNw+8tL9VOnJsWycWYR78TKss5PVpIPb1TUUcMQ+bBh7cY579cVAWmf5qG+dw+Q==", - "dev": true, - "requires": { - "@octokit/types": "^2.0.1" - }, - "dependencies": { - "@octokit/types": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.16.2.tgz", - "integrity": "sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q==", - "dev": true, - "requires": { - "@types/node": ">= 8" - } - } - } - }, - "@octokit/plugin-request-log": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz", - "integrity": "sha512-ywoxP68aOT3zHCLgWZgwUJatiENeHE7xJzYjfz8WI0goynp96wETBF+d95b8g/uL4QmS6owPVlaxiz3wyMAzcw==", - "dev": true - }, - "@octokit/plugin-rest-endpoint-methods": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-2.4.0.tgz", - "integrity": "sha512-EZi/AWhtkdfAYi01obpX0DF7U6b1VRr30QNQ5xSFPITMdLSfhcBqjamE3F+sKcxPbD7eZuMHu3Qkk2V+JGxBDQ==", - "dev": true, - "requires": { - "@octokit/types": "^2.0.1", - "deprecation": "^2.3.1" - }, - "dependencies": { - "@octokit/types": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.16.2.tgz", - "integrity": "sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q==", - "dev": true, - "requires": { - "@types/node": ">= 8" - } - } - } - }, - "@octokit/request": { - "version": "5.4.7", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.4.7.tgz", - "integrity": "sha512-FN22xUDP0i0uF38YMbOfx6TotpcENP5W8yJM1e/LieGXn6IoRxDMnBf7tx5RKSW4xuUZ/1P04NFZy5iY3Rax1A==", - "dev": true, - "requires": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.0.0", - "@octokit/types": "^5.0.0", - "deprecation": "^2.0.0", - "is-plain-object": "^4.0.0", - "node-fetch": "^2.3.0", - "once": "^1.4.0", - "universal-user-agent": "^6.0.0" - }, - "dependencies": { - "@octokit/request-error": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.2.tgz", - "integrity": "sha512-2BrmnvVSV1MXQvEkrb9zwzP0wXFNbPJij922kYBTLIlIafukrGOb+ABBT2+c6wZiuyWDH1K1zmjGQ0toN/wMWw==", - "dev": true, - "requires": { - "@octokit/types": "^5.0.1", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "is-plain-object": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-4.1.1.tgz", - "integrity": "sha512-5Aw8LLVsDlZsETVMhoMXzqsXwQqr/0vlnBYzIXJbYo2F4yYlhLHs+Ez7Bod7IIQKWkJbJfxrWD7pA1Dw1TKrwA==", - "dev": true - }, - "universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", - "dev": true - } - } - }, - "@octokit/request-error": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.2.1.tgz", - "integrity": "sha512-+6yDyk1EES6WK+l3viRDElw96MvwfJxCt45GvmjDUKWjYIb3PJZQkq3i46TwGwoPD4h8NmTrENmtyA1FwbmhRA==", - "dev": true, - "requires": { - "@octokit/types": "^2.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - }, - "dependencies": { - "@octokit/types": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.16.2.tgz", - "integrity": "sha512-O75k56TYvJ8WpAakWwYRN8Bgu60KrmX0z1KqFp1kNiFNkgW+JW+9EBKZ+S33PU6SLvbihqd+3drvPxKK68Ee8Q==", - "dev": true, - "requires": { - "@types/node": ">= 8" - } - } - } - }, - "@octokit/rest": { - "version": "16.43.2", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.43.2.tgz", - "integrity": "sha512-ngDBevLbBTFfrHZeiS7SAMAZ6ssuVmXuya+F/7RaVvlysgGa1JKJkKWY+jV6TCJYcW0OALfJ7nTIGXcBXzycfQ==", - "dev": true, - "requires": { - "@octokit/auth-token": "^2.4.0", - "@octokit/plugin-paginate-rest": "^1.1.1", - "@octokit/plugin-request-log": "^1.0.0", - "@octokit/plugin-rest-endpoint-methods": "2.4.0", - "@octokit/request": "^5.2.0", - "@octokit/request-error": "^1.0.2", - "atob-lite": "^2.0.0", - "before-after-hook": "^2.0.0", - "btoa-lite": "^1.0.0", - "deprecation": "^2.0.0", - "lodash.get": "^4.4.2", - "lodash.set": "^4.3.2", - "lodash.uniq": "^4.5.0", - "octokit-pagination-methods": "^1.1.0", - "once": "^1.4.0", - "universal-user-agent": "^4.0.0" - } - }, - "@octokit/types": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-5.2.0.tgz", - "integrity": "sha512-XjOk9y4m8xTLIKPe1NFxNWBdzA2/z3PFFA/bwf4EoH6oS8hM0Y46mEa4Cb+KCyj/tFDznJFahzQ0Aj3o1FYq4A==", - "dev": true, - "requires": { - "@types/node": ">= 8" - } - }, - "@rollup/plugin-babel": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.1.0.tgz", - "integrity": "sha512-zXBEYmfiLAMvB+ZBa6m/q9hsQYAq1sUFdjuP1F6C2pf6uQcpHwAWQveZgzS63zXdKPUYHD3Dr7BhjCqcr0bbLw==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.7.4", - "@rollup/pluginutils": "^3.0.8" - } - }, - "@rollup/plugin-commonjs": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-14.0.0.tgz", - "integrity": "sha512-+PSmD9ePwTAeU106i9FRdc+Zb3XUWyW26mo5Atr2mk82hor8+nPwkztEjFo8/B1fJKfaQDg9aM2bzQkjhi7zOw==", - "dev": true, - "requires": { - "@rollup/pluginutils": "^3.0.8", - "commondir": "^1.0.1", - "estree-walker": "^1.0.1", - "glob": "^7.1.2", - "is-reference": "^1.1.2", - "magic-string": "^0.25.2", - "resolve": "^1.11.0" - } - }, - "@rollup/pluginutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", - "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", - "dev": true, - "requires": { - "@types/estree": "0.0.39", - "estree-walker": "^1.0.1", - "picomatch": "^2.2.2" - } - }, - "@sindresorhus/is": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.1.tgz", - "integrity": "sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg==", - "dev": true - }, - "@solana/web3.js": { - "version": "0.64.0", - "resolved": "https://registry.npmjs.org/@solana/web3.js/-/web3.js-0.64.0.tgz", - "integrity": "sha512-DlNzAXgNdk7k4Pt6CfcaAutaiXJiog9hxswtzItf0q/0/Um8JvDI1YjnMONE3IKI/jyjmTaxhsQHWAQE42KofQ==", - "requires": { - "@babel/runtime": "^7.3.1", - "bn.js": "^5.0.0", - "bs58": "^4.0.1", - "buffer": "^5.4.3", - "buffer-layout": "^1.2.0", - "crypto-hash": "^1.2.2", - "esdoc-inject-style-plugin": "^1.0.0", - "jayson": "^3.0.1", - "mz": "^2.7.0", - "node-fetch": "^2.2.0", - "npm-run-all": "^4.1.5", - "rpc-websockets": "^5.0.8", - "superstruct": "^0.8.3", - "tweetnacl": "^1.0.0", - "ws": "^7.0.0" - } - }, - "@szmarczak/http-timer": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz", - "integrity": "sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==", - "dev": true, - "requires": { - "defer-to-connect": "^2.0.0" - } - }, - "@types/cacheable-request": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz", - "integrity": "sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==", - "dev": true, - "requires": { - "@types/http-cache-semantics": "*", - "@types/keyv": "*", - "@types/node": "*", - "@types/responselike": "*" - } - }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true - }, - "@types/connect": { - "version": "3.4.33", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", - "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", - "requires": { - "@types/node": "*" - } - }, - "@types/estree": { - "version": "0.0.39", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", - "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", - "dev": true - }, - "@types/express-serve-static-core": { - "version": "4.17.9", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.9.tgz", - "integrity": "sha512-DG0BYg6yO+ePW+XoDENYz8zhNGC3jDDEpComMYn7WJc4mY1Us8Rw9ax2YhJXxpyk2SF47PQAoQ0YyVT1a0bEkA==", - "requires": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*" - } - }, - "@types/fs-extra": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.1.tgz", - "integrity": "sha512-TcUlBem321DFQzBNuz8p0CLLKp0VvF/XH9E4KHNmgwyp4E3AfgI5cjiIVZWlbfThBop2qxFIh4+LeY6hVWWZ2w==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w==", - "dev": true, - "requires": { - "@types/minimatch": "*", - "@types/node": "*" - } - }, - "@types/http-cache-semantics": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz", - "integrity": "sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==", - "dev": true - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", - "dev": true - }, - "@types/keyv": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz", - "integrity": "sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/lodash": { - "version": "4.14.158", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.158.tgz", - "integrity": "sha512-InCEXJNTv/59yO4VSfuvNrZHt7eeNtWQEgnieIA+mIC+MOWM9arOWG2eQ8Vhk6NbOre6/BidiXhkZYeDY9U35w==" - }, - "@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", - "dev": true - }, - "@types/node": { - "version": "10.14.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.6.tgz", - "integrity": "sha512-Fvm24+u85lGmV4hT5G++aht2C5I4Z4dYlWZIh62FAfFO/TfzXtPpoLI6I7AuBWkIFqZCnhFOoTT7RjjaIL5Fjg==" - }, - "@types/qs": { - "version": "6.9.4", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.4.tgz", - "integrity": "sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ==" - }, - "@types/range-parser": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", - "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" - }, - "@types/responselike": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz", - "integrity": "sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "JSONStream": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", - "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", - "requires": { - "jsonparse": "^1.2.0", - "through": ">=2.2.7 <3" - } - }, - "acorn": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz", - "integrity": "sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA==", - "dev": true - }, - "acorn-jsx": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz", - "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==", - "dev": true - }, - "ajv": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", - "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" - } - }, - "any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true, - "optional": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true, - "optional": true - }, - "array-includes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", - "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0", - "is-string": "^1.0.5" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "array.prototype.flat": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", - "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, - "assert-args": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/assert-args/-/assert-args-1.2.1.tgz", - "integrity": "sha1-QEEDoUUqMv53iYgR5U5ZCoqTc70=", - "requires": { - "101": "^1.2.0", - "compound-subject": "0.0.1", - "debug": "^2.2.0", - "get-prototype-of": "0.0.0", - "is-capitalized": "^1.0.0", - "is-class": "0.0.4" - } - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true, - "optional": true - }, - "astral-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", - "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", - "dev": true - }, - "async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", - "dev": true, - "optional": true - }, - "async-limiter": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true, - "optional": true - }, - "atob-lite": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/atob-lite/-/atob-lite-2.0.0.tgz", - "integrity": "sha1-D+9a1G8b16hQLGVyfwNn1e5D1pY=", - "dev": true - }, - "babel-eslint": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.1.0.tgz", - "integrity": "sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@babel/parser": "^7.7.0", - "@babel/traverse": "^7.7.0", - "@babel/types": "^7.7.0", - "eslint-visitor-keys": "^1.0.0", - "resolve": "^1.12.0" - }, - "dependencies": { - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - } - } - }, - "babel-plugin-dynamic-import-node": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz", - "integrity": "sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ==", - "dev": true, - "requires": { - "object.assign": "^4.1.0" - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "optional": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "base-x": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.8.tgz", - "integrity": "sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" - }, - "before-after-hook": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz", - "integrity": "sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==", - "dev": true - }, - "big-integer": { - "version": "1.6.48", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz", - "integrity": "sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==", - "dev": true - }, - "binary": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", - "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", - "dev": true, - "requires": { - "buffers": "~0.1.1", - "chainsaw": "~0.1.0" - } - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true, - "optional": true - }, - "bluebird": { - "version": "3.4.7", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", - "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=", - "dev": true - }, - "bn.js": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.2.tgz", - "integrity": "sha512-40rZaf3bUNKTVYu9sIeeEGOg7g14Yvnj9kH7b50EiwX0Q7A6umbvfI5tvHaOERH0XigqKkfLkFQxzb4e6CIXnA==" - }, - "boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=" - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "optional": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true, - "optional": true - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "browserslist": { - "version": "4.13.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.13.0.tgz", - "integrity": "sha512-MINatJ5ZNrLnQ6blGvePd/QOz9Xtu+Ne+x29iQSCHfkU5BugKVJwZKn/iiL8UbpIpa3JhviKjz+XxMo0m2caFQ==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001093", - "electron-to-chromium": "^1.3.488", - "escalade": "^3.0.1", - "node-releases": "^1.1.58" - } - }, - "bs58": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", - "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", - "requires": { - "base-x": "^3.0.2" - } - }, - "btoa-lite": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", - "integrity": "sha1-M3dm2hWAEhD92VbCLpxokaudAzc=", - "dev": true - }, - "buffer": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", - "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } - }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "buffer-indexof-polyfill": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.1.tgz", - "integrity": "sha1-qfuAbOgUXVQoUQznLyeLs2OmOL8=", - "dev": true - }, - "buffer-layout": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-layout/-/buffer-layout-1.2.0.tgz", - "integrity": "sha512-iiyRoho/ERzBUv6kFvfsrLNgTlVwOkqQcSQN7WrO3Y+c5SeuEhCn6+y1KwhM0V3ndptF5mI/RI44zkw0qcR5Jg==" - }, - "buffers": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", - "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=", - "dev": true - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "optional": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "cacheable-lookup": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-2.0.1.tgz", - "integrity": "sha512-EMMbsiOTcdngM/K6gV/OxF2x0t07+vMOWxZNSCRQMjO2MY2nhZQ6OYhOOpyQrbhqsgtvKGI7hcq6xjnA92USjg==", - "dev": true, - "requires": { - "@types/keyv": "^3.1.1", - "keyv": "^4.0.0" - } - }, - "cacheable-request": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz", - "integrity": "sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==", - "dev": true, - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^2.0.0" - }, - "dependencies": { - "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - } - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001109", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001109.tgz", - "integrity": "sha512-4JIXRodHzdS3HdK8nSgIqXYLExOvG+D2/EenSvcub2Kp3QEADjo2v2oUn5g0n0D+UNwG9BtwKOyGcSq2qvQXvQ==", - "dev": true - }, - "chainsaw": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", - "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", - "dev": true, - "requires": { - "traverse": ">=0.3.0 <0.4" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=", - "dev": true - }, - "cheerio": { - "version": "0.22.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-0.22.0.tgz", - "integrity": "sha1-qbqoYKP5tZWmuBsahocxIe06Jp4=", - "requires": { - "css-select": "~1.2.0", - "dom-serializer": "~0.1.0", - "entities": "~1.1.1", - "htmlparser2": "^3.9.1", - "lodash.assignin": "^4.0.9", - "lodash.bind": "^4.1.4", - "lodash.defaults": "^4.0.1", - "lodash.filter": "^4.4.0", - "lodash.flatten": "^4.2.0", - "lodash.foreach": "^4.3.0", - "lodash.map": "^4.4.0", - "lodash.merge": "^4.4.0", - "lodash.pick": "^4.2.1", - "lodash.reduce": "^4.4.0", - "lodash.reject": "^4.4.0", - "lodash.some": "^4.4.0" - } - }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "dev": true, - "optional": true - }, - "circular-json": { - "version": "0.5.9", - "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz", - "integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==" - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "optional": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - }, - "dependencies": { - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "clone": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=" - }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" - }, - "dependencies": { - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true - } - } - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "optional": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" - }, - "colorette": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", - "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==", - "dev": true - }, - "colors": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", - "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", - "dev": true - }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" - }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true - }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", - "dev": true, - "optional": true - }, - "compound-subject": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/compound-subject/-/compound-subject-0.0.1.tgz", - "integrity": "sha1-JxVUaYoVrmCLHfyv0wt7oeqJLEs=" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true - }, - "convert-source-map": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", - "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true, - "optional": true - }, - "core-js": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", - "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" - }, - "core-js-compat": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.6.5.tgz", - "integrity": "sha512-7ItTKOhOZbznhXAQ2g/slGg1PJV5zDO/WdkTwi7UEOJmkvsE32PWvx6mKtDjiMpjnR2CNf6BAD6sSxIlv7ptng==", - "dev": true, - "requires": { - "browserslist": "^4.8.5", - "semver": "7.0.0" - }, - "dependencies": { - "semver": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", - "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", - "dev": true - } - } - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } - } - }, - "crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=", - "dev": true - }, - "crypto-hash": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/crypto-hash/-/crypto-hash-1.2.2.tgz", - "integrity": "sha512-rXXMXepuKg9gIfqE7I1jtVa6saLhzIkDQ2u3kTGUWYiUGsHcUa3LTsfrjPEdOY8kxKlryQtsOmJOU0F23yRJTg==" - }, - "css-select": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz", - "integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=", - "requires": { - "boolbase": "~1.0.0", - "css-what": "2.1", - "domutils": "1.5.1", - "nth-check": "~1.0.1" - } - }, - "css-what": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz", - "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==" - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true, - "optional": true - }, - "decompress-response": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-5.0.0.tgz", - "integrity": "sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw==", - "dev": true, - "requires": { - "mimic-response": "^2.0.0" - } - }, - "deep-eql": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", - "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", - "requires": { - "type-detect": "0.1.1" - } - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "defer-to-connect": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz", - "integrity": "sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==", - "dev": true - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "requires": { - "object-keys": "^1.0.12" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - }, - "dependencies": { - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - } - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "dom-serializer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", - "integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==", - "requires": { - "domelementtype": "^1.3.0", - "entities": "^1.1.1" - } - }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" - }, - "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "requires": { - "domelementtype": "1" - } - }, - "domutils": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", - "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", - "requires": { - "dom-serializer": "0", - "domelementtype": "1" - } - }, - "dotenv": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.2.0.tgz", - "integrity": "sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==" - }, - "duplexer2": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", - "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", - "dev": true, - "requires": { - "readable-stream": "^2.0.2" - } - }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true - }, - "electron-to-chromium": { - "version": "1.3.514", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.514.tgz", - "integrity": "sha512-8vb8zKIeGlZigeDzNWWthmGeLzo5CC43Lc+CZshMs7UXFVMPNLtXJGa/txedpu3OJFrXXVheBwp9PqOJJlHQ8w==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - }, - "dependencies": { - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - } - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "requires": { - "es6-promise": "^4.0.3" - } - }, - "escalade": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.0.2.tgz", - "integrity": "sha512-gPYAU37hYCUhW5euPeR+Y74F7BL+IBsV93j5cvGriSaD1aG6MGsqsV1yamRdrWrb2j3aiZvb0X+UBOWpx3JWtQ==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" - }, - "esdoc-inject-style-plugin": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/esdoc-inject-style-plugin/-/esdoc-inject-style-plugin-1.0.0.tgz", - "integrity": "sha1-oTWXNou5+4nDZeBmSVyvl6Tey7E=", - "requires": { - "cheerio": "0.22.0", - "fs-extra": "1.0.0" - } - }, - "eslint": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.6.0.tgz", - "integrity": "sha512-QlAManNtqr7sozWm5TF4wIH9gmUm2hE3vNRUvyoYAa4y1l5/jxD/PQStEjBMQtCqZmSep8UxrcecI60hOpe61w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.0", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^1.3.0", - "espree": "^7.2.0", - "esquery": "^1.2.0", - "esutils": "^2.0.2", - "file-entry-cache": "^5.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash": "^4.17.19", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^5.2.3", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - }, - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "eslint-import-resolver-node": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", - "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "resolve": "^1.13.1" - }, - "dependencies": { - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - } - } - }, - "eslint-module-utils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", - "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "pkg-dir": "^2.0.0" - } - }, - "eslint-plugin-import": { - "version": "2.22.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.0.tgz", - "integrity": "sha512-66Fpf1Ln6aIS5Gr/55ts19eUuoDhAbZgnr6UxK5hbDx6l/QgQgx61AePq+BV4PP2uXQFClgMVzep5zZ94qqsxg==", - "dev": true, - "requires": { - "array-includes": "^3.1.1", - "array.prototype.flat": "^1.2.3", - "contains-path": "^0.1.0", - "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.3", - "eslint-module-utils": "^2.6.0", - "has": "^1.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.1", - "read-pkg-up": "^2.0.0", - "resolve": "^1.17.0", - "tsconfig-paths": "^3.9.0" - }, - "dependencies": { - "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - } - }, - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - } - } - }, - "eslint-scope": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.0.tgz", - "integrity": "sha512-iiGRvtxWqgtx5m8EyQUJihBloE4EnYeGE/bz1wSPwJE6tZuJUtHlhqDM4Xj2ukE8Dyy1+HCZ4hE0fzIVMzb58w==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - }, - "eslint-visitor-keys": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", - "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", - "dev": true - }, - "espree": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.2.0.tgz", - "integrity": "sha512-H+cQ3+3JYRMEIOl87e7QdHX70ocly5iW4+dttuR8iYSPr/hXKFb+7dBsZ7+u1adC4VrnPlTkv0+OwuPnDop19g==", - "dev": true, - "requires": { - "acorn": "^7.3.1", - "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.3.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", - "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.1.0.tgz", - "integrity": "sha512-FyohXK+R0vE+y1nHLoBM7ZTyqRpqAlhdZHCWIWEviFLiGB8b04H6bQs8G+XTthacvT8VuwvteiP7RJSxMs8UEw==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "estree-walker": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", - "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", - "dev": true - }, - "esutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", - "dev": true - }, - "eventemitter3": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", - "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" - }, - "exec-sh": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.2.2.tgz", - "integrity": "sha512-FIUCJz1RbuS0FKTdaAafAByGS0CPvU3R0MeHxgtl+djzCc//F8HakL8GzmVNZanasTbTAY/3DRFA0KpVqj/eAw==", - "dev": true, - "requires": { - "merge": "^1.2.0" - } - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "optional": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "optional": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "eyes": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", - "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-glob": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz", - "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - }, - "dependencies": { - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } - } - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fastq": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz", - "integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "file-entry-cache": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", - "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==", - "dev": true, - "requires": { - "flat-cache": "^2.0.1" - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "optional": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "dependencies": { - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - } - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "flat-cache": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", - "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", - "dev": true, - "requires": { - "flatted": "^2.0.0", - "rimraf": "2.6.3", - "write": "1.0.3" - } - }, - "flatted": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz", - "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==", - "dev": true - }, - "flow-bin": { - "version": "0.131.0", - "resolved": "https://registry.npmjs.org/flow-bin/-/flow-bin-0.131.0.tgz", - "integrity": "sha512-fZmoIBcDrtLhy/NNMxwJysSYzMr1ksRcAOMi3AHSoYXfcuQqTvhGJx+wqjlIOqIwz8RRYm8J4V4JrSJbIKP+Xg==", - "dev": true - }, - "flow-typed": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/flow-typed/-/flow-typed-3.2.1.tgz", - "integrity": "sha512-vWQLZmndgdEHdy1TKTeI1DkLLa4078p6vhcKz/IZ6fcpaWLElTSG7rZi3BxlcSWfEQPxsymuSBNwAT7dCpXm6g==", - "dev": true, - "requires": { - "@octokit/rest": "^16.43.1", - "colors": "^1.4.0", - "flowgen": "^1.10.0", - "fs-extra": "^8.1.0", - "glob": "^7.1.6", - "got": "^10.5.7", - "md5": "^2.2.1", - "mkdirp": "^1.0.3", - "prettier": "^1.19.1", - "rimraf": "^3.0.2", - "semver": "^7.3.2", - "table": "^5.4.6", - "through": "^2.3.8", - "unzipper": "^0.10.8", - "which": "^2.0.2", - "yargs": "^15.1.0" - }, - "dependencies": { - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "flowgen": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/flowgen/-/flowgen-1.11.0.tgz", - "integrity": "sha512-WpoBjzcZadnAw5FatlUbvFWUWXkI2/LjrwTl5fl3MVDh+KdvYgFzgRXDDKH/O2uUlwjfpveiJJJx8TwL7Se84A==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/highlight": "^7.9.0", - "commander": "^5.1.0", - "lodash": "^4.17.15", - "prettier": "^2.0.5", - "shelljs": "^0.8.4", - "typescript": "^3.4", - "typescript-compiler": "^1.4.1-2" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "commander": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", - "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", - "dev": true - } - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true, - "optional": true - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "optional": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "fs-extra": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz", - "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=", - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^2.1.0", - "klaw": "^1.0.0" - }, - "dependencies": { - "jsonfile": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", - "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", - "requires": { - "graceful-fs": "^4.1.6" - } - } - } - }, - "fs-minipass": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", - "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.6.0" - } - }, - "fs-readdir-recursive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", - "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", - "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", - "dev": true, - "optional": true, - "requires": { - "nan": "^2.12.1", - "node-pre-gyp": "^0.12.0" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true, - "optional": true - }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", - "dev": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", - "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", - "dev": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true, - "optional": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "optional": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", - "dev": true, - "optional": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true, - "optional": true - }, - "console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", - "dev": true, - "optional": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true, - "optional": true - }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "optional": true, - "requires": { - "ms": "^2.1.1" - } - }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", - "dev": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", - "dev": true, - "optional": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", - "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", - "dev": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", - "dev": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "optional": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore-walk": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", - "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", - "dev": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true, - "optional": true - }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "dev": true, - "optional": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "optional": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "optional": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true, - "optional": true - }, - "needle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/needle/-/needle-2.3.0.tgz", - "integrity": "sha512-QBZu7aAFR0522EyaXZM0FZ9GLpq6lvQ3uq8gteiDUp7wKdy0lSd2hPlgFwVuW1CBkfEs9PfDQsQzZghLs/psdg==", - "dev": true, - "optional": true, - "requires": { - "debug": "^4.1.0", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz", - "integrity": "sha512-4KghwV8vH5k+g2ylT+sLTjy5wmUOb9vPhnM8NHvRf9dHmnW/CndrFXy2aRPaPST6dugXSdHXfeaHQm77PIz/1A==", - "dev": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.1", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.2.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", - "dev": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.0.6.tgz", - "integrity": "sha512-8/JCaftHwbd//k6y2rEWp6k1wxVfpFzB6t1p825+cUb7Ym2XQfhwIC5KwhrvzZRJu+LtDE585zVaS32+CGtf0g==", - "dev": true, - "optional": true - }, - "npm-packlist": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.1.tgz", - "integrity": "sha512-+TcdO7HJJ8peiiYhvPxsEDhF3PJFGUGRcFsGve3vxvxdcpO2Z4Z7rkosRM0kWj6LfbK/P0gu3dzk5RU1ffvFcw==", - "dev": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", - "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", - "dev": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", - "dev": true, - "optional": true - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "optional": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", - "dev": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", - "dev": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "dev": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true, - "optional": true - }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "optional": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "optional": true, - "requires": { - "glob": "^7.1.3" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "optional": true - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true, - "optional": true - }, - "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==", - "dev": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "dev": true, - "optional": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "optional": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true, - "optional": true - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true, - "optional": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "optional": true, - "requires": { - "string-width": "^1.0.2 || 2" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true, - "optional": true - } - } - }, - "fstream": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", - "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-prototype-of": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/get-prototype-of/-/get-prototype-of-0.0.0.tgz", - "integrity": "sha1-mHcr0QcW0W3rSzIlFsRp78oorEQ=" - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true, - "optional": true - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "globby": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.1.tgz", - "integrity": "sha512-sSs4inE1FB2YQiymcmTv6NWENryABjUNPeWhOvmn4SjtKybglsyPZxFB3U1/+L1bYi0rNZDqCLlHyLYDl1Pq5A==", - "dev": true, - "requires": { - "@types/glob": "^7.1.1", - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.0.3", - "glob": "^7.1.3", - "ignore": "^5.1.1", - "merge2": "^1.2.3", - "slash": "^3.0.0" - }, - "dependencies": { - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - } - } - }, - "got": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/got/-/got-10.7.0.tgz", - "integrity": "sha512-aWTDeNw9g+XqEZNcTjMMZSy7B7yE9toWOFYip7ofFTLleJhvZwUxxTxkTpKvF+p1SAA4VHmuEy7PiHTHyq8tJg==", - "dev": true, - "requires": { - "@sindresorhus/is": "^2.0.0", - "@szmarczak/http-timer": "^4.0.0", - "@types/cacheable-request": "^6.0.1", - "cacheable-lookup": "^2.0.0", - "cacheable-request": "^7.0.1", - "decompress-response": "^5.0.0", - "duplexer3": "^0.1.4", - "get-stream": "^5.0.0", - "lowercase-keys": "^2.0.0", - "mimic-response": "^2.1.0", - "p-cancelable": "^2.0.0", - "p-event": "^4.0.0", - "responselike": "^2.0.0", - "to-readable-stream": "^2.0.0", - "type-fest": "^0.10.0" - }, - "dependencies": { - "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "type-fest": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.10.0.tgz", - "integrity": "sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw==", - "dev": true - } - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" - }, - "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=" - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "optional": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "optional": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "homedir-polyfill": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", - "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", - "dev": true, - "requires": { - "parse-passwd": "^1.0.0" - } - }, - "hosted-git-info": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.7.1.tgz", - "integrity": "sha512-7T/BxH19zbcCTa8XkMlbK5lTo1WtgkFi3GvdWEyNuc4Vex7/9Dqbnpsf4JMydcfj9HCg4zUWFTL3Za6lapg5/w==" - }, - "htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "requires": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true - }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "import-fresh": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" - }, - "interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true - }, - "invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, - "requires": { - "loose-envify": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=" - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "optional": true, - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" - }, - "is-capitalized": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-capitalized/-/is-capitalized-1.0.0.tgz", - "integrity": "sha1-TIRktNkdPk7rRIid0s2PGwrEwTY=" - }, - "is-class": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/is-class/-/is-class-0.0.4.tgz", - "integrity": "sha1-4FdFFwW7NOOePjNZjJOpg3KWtzY=" - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=" - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "optional": true - } - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true, - "optional": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "optional": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "is-reference": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", - "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", - "dev": true, - "requires": { - "@types/estree": "*" - } - }, - "is-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", - "requires": { - "has-symbols": "^1.0.1" - }, - "dependencies": { - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" - } - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", - "dev": true - }, - "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", - "requires": { - "has-symbols": "^1.0.0" - } - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "optional": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, - "optional": true - }, - "jayson": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/jayson/-/jayson-3.3.3.tgz", - "integrity": "sha512-0bQ/vNvWyi+fzNoMvRK63m7BGU+PWwaJRVaGgJWZeAL5euOSqJtqzqyCrfiS3Sdw3OrvgKWF5f5N8ut87gxrng==", - "requires": { - "@types/connect": "^3.4.32", - "@types/express-serve-static-core": "^4.16.9", - "@types/lodash": "^4.14.139", - "@types/node": "^12.7.7", - "JSONStream": "^1.3.1", - "commander": "^2.12.2", - "es6-promisify": "^5.0.0", - "eyes": "^0.1.8", - "json-stringify-safe": "^5.0.1", - "lodash": "^4.17.15", - "uuid": "^3.2.1" - }, - "dependencies": { - "@types/node": { - "version": "12.12.53", - "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.53.tgz", - "integrity": "sha512-51MYTDTyCziHb70wtGNFRwB4l+5JNvdqzFSkbDvpbftEgVUBEE+T5f7pROhWMp/fxp07oNIEQZd5bbfAH22ohQ==" - } - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "jsonparse": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", - "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" - }, - "keypather": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/keypather/-/keypather-1.10.2.tgz", - "integrity": "sha1-4ESWMtSz5RbyHMAUznxWRP3c5hQ=", - "requires": { - "101": "^1.0.0" - } - }, - "keyv": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.0.1.tgz", - "integrity": "sha512-xz6Jv6oNkbhrFCvCP7HQa8AaII8y8LRpoSm661NOKLr4uHuBwhX4epXrPQgF3+xdJnN4Esm5X0xwY4bOlALOtw==", - "dev": true, - "requires": { - "json-buffer": "3.0.1" - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==" - }, - "klaw": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", - "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", - "requires": { - "graceful-fs": "^4.1.9" - } - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "levenary": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/levenary/-/levenary-1.1.1.tgz", - "integrity": "sha512-mkAdOIt79FD6irqjYSs4rdbnlT5vRonMEvBVPVb3XmevfS8kgRXwfes0dhPdEtzTWD/1eNE/Bm/G1iRt6DcnQQ==", - "dev": true, - "requires": { - "leven": "^3.1.0" - } - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "listenercount": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", - "integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=", - "dev": true - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" - }, - "lodash.assignin": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz", - "integrity": "sha1-uo31+4QesKPoBEIysOJjqNxqKKI=" - }, - "lodash.bind": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/lodash.bind/-/lodash.bind-4.2.1.tgz", - "integrity": "sha1-euMBfpOWIqwxt9fX3LGzTbFpDTU=" - }, - "lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" - }, - "lodash.filter": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.filter/-/lodash.filter-4.6.0.tgz", - "integrity": "sha1-ZosdSYFgOuHMWm+nYBQ+SAtMSs4=" - }, - "lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" - }, - "lodash.foreach": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.foreach/-/lodash.foreach-4.5.0.tgz", - "integrity": "sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM=" - }, - "lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=", - "dev": true - }, - "lodash.map": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.map/-/lodash.map-4.6.0.tgz", - "integrity": "sha1-dx7Hg540c9nEzeKLGTlMNWL09tM=" - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" - }, - "lodash.pick": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", - "integrity": "sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM=" - }, - "lodash.reduce": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reduce/-/lodash.reduce-4.6.0.tgz", - "integrity": "sha1-8atrg5KZrUj3hKu/R2WW8DuRTTs=" - }, - "lodash.reject": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.reject/-/lodash.reject-4.6.0.tgz", - "integrity": "sha1-gNZJLcFHCGS79YNTO2UfQqn1JBU=" - }, - "lodash.set": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", - "dev": true - }, - "lodash.some": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", - "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=" - }, - "lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=", - "dev": true - }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true - }, - "macos-release": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.4.1.tgz", - "integrity": "sha512-H/QHeBIN1fIGJX517pvK8IEK53yQOW7YcEI55oYtgjDdoCQQz7eJS94qt5kNrscReEyuD/JcdFCm2XBEcGOITg==", - "dev": true - }, - "magic-string": { - "version": "0.25.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", - "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", - "dev": true, - "requires": { - "sourcemap-codec": "^1.4.4" - } - }, - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "dependencies": { - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true, - "optional": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "optional": true, - "requires": { - "object-visit": "^1.0.0" - } - }, - "md5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz", - "integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=", - "dev": true, - "requires": { - "charenc": "~0.0.1", - "crypt": "~0.0.1", - "is-buffer": "~1.1.1" - } - }, - "memorystream": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", - "integrity": "sha1-htcJCzDORV1j+64S3aUaR93K+bI=" - }, - "merge": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz", - "integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "optional": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true, - "optional": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true, - "optional": true - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "optional": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "optional": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "optional": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "mimic-response": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", - "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "minipass": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", - "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", - "dev": true, - "optional": true, - "requires": { - "safe-buffer": "^5.1.2", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", - "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", - "dev": true, - "optional": true, - "requires": { - "minipass": "^2.9.0" - } - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "optional": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "optional": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "requires": { - "minimist": "^1.2.5" - } - }, - "mkdirp-promise": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", - "integrity": "sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE=", - "requires": { - "mkdirp": "*" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "requires": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "nan": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", - "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", - "dev": true, - "optional": true - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "optional": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true, - "optional": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true, - "optional": true - } - } - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" - }, - "node-environment-flags": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", - "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", - "dev": true, - "requires": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "node-fetch": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", - "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" - }, - "node-modules-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", - "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", - "dev": true - }, - "node-releases": { - "version": "1.1.60", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz", - "integrity": "sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA==", - "dev": true - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } - } - }, - "normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", - "dev": true - }, - "npm-run-all": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", - "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", - "requires": { - "ansi-styles": "^3.2.1", - "chalk": "^2.4.1", - "cross-spawn": "^6.0.5", - "memorystream": "^0.3.1", - "minimatch": "^3.0.4", - "pidtree": "^0.3.0", - "read-pkg": "^3.0.0", - "shell-quote": "^1.6.1", - "string.prototype.padend": "^3.0.0" - }, - "dependencies": { - "load-json-file": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", - "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^4.0.0", - "pify": "^3.0.0", - "strip-bom": "^3.0.0" - } - }, - "parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", - "requires": { - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1" - } - }, - "path-type": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", - "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", - "requires": { - "pify": "^3.0.0" - } - }, - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" - }, - "read-pkg": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", - "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", - "requires": { - "load-json-file": "^4.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^3.0.0" - } - } - } - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "nth-check": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz", - "integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==", - "requires": { - "boolbase": "~1.0.0" - } - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "optional": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" - }, - "object-keys": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", - "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==" - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "optional": true, - "requires": { - "isobject": "^3.0.0" - } - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "optional": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "object.values": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", - "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "has": "^1.0.3" - } - }, - "octokit-pagination-methods": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/octokit-pagination-methods/-/octokit-pagination-methods-1.1.0.tgz", - "integrity": "sha512-fZ4qZdQ2nxJvtcasX7Ghl+WlWS/d9IgnBIwFZXVNNZUmzpno91SX5bc5vuxiuKoCtK78XxGGNuSCrDC7xYB3OQ==", - "dev": true - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "os-name": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/os-name/-/os-name-3.1.0.tgz", - "integrity": "sha512-h8L+8aNjNcMpo/mAIBPn5PXCM16iyPGjHNWo6U1YO8sJTMHtEtyczI6QJnLoplswm6goopQkqc7OAnjhWcugVg==", - "dev": true, - "requires": { - "macos-release": "^2.2.0", - "windows-release": "^3.1.0" - } - }, - "p-cancelable": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz", - "integrity": "sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==", - "dev": true - }, - "p-event": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz", - "integrity": "sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==", - "dev": true, - "requires": { - "p-timeout": "^3.1.0" - } - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-timeout": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", - "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", - "dev": true, - "requires": { - "p-finally": "^1.0.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "parse-passwd": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", - "dev": true - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true, - "optional": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true, - "optional": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true - }, - "pidtree": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", - "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==" - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", - "dev": true, - "requires": { - "node-modules-regexp": "^1.0.0" - } - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true, - "optional": true - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "prettier": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", - "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "dev": true - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - } - }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "optional": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true, - "optional": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true, - "optional": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "optional": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "optional": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "optional": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "optional": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "optional": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "optional": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - } - } - }, - "rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q=", - "dev": true, - "requires": { - "resolve": "^1.1.6" - } - }, - "regenerate": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", - "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==", - "dev": true - }, - "regenerate-unicode-properties": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz", - "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==", - "dev": true, - "requires": { - "regenerate": "^1.4.0" - } - }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" - }, - "regenerator-transform": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.5.tgz", - "integrity": "sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw==", - "dev": true, - "requires": { - "@babel/runtime": "^7.8.4" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "optional": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", - "dev": true - }, - "regexpu-core": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz", - "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==", - "dev": true, - "requires": { - "regenerate": "^1.4.0", - "regenerate-unicode-properties": "^8.2.0", - "regjsgen": "^0.5.1", - "regjsparser": "^0.6.4", - "unicode-match-property-ecmascript": "^1.0.4", - "unicode-match-property-value-ecmascript": "^1.2.0" - } - }, - "regjsgen": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.2.tgz", - "integrity": "sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A==", - "dev": true - }, - "regjsparser": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz", - "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==", - "dev": true, - "requires": { - "jsesc": "~0.5.0" - }, - "dependencies": { - "jsesc": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", - "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=", - "dev": true - } - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true, - "optional": true - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true, - "optional": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true, - "optional": true - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "resolve": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.0.tgz", - "integrity": "sha512-WL2pBDjqT6pGUNSUzMw00o4T7If+z4H2x3Gz893WoUQ5KW8Vr9txp00ykiP16VBaZF5+j/OcXJHZ9+PCvdiDKw==", - "requires": { - "path-parse": "^1.0.6" - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true, - "optional": true - }, - "responselike": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz", - "integrity": "sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==", - "dev": true, - "requires": { - "lowercase-keys": "^2.0.0" - } - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true, - "optional": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "rollup": { - "version": "2.23.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.23.1.tgz", - "integrity": "sha512-Heyl885+lyN/giQwxA8AYT2GY3U+gOlTqVLrMQYno8Z1X9lAOpfXPiKiZCyPc25e9BLJM3Zlh957dpTlO4pa8A==", - "dev": true, - "requires": { - "fsevents": "~2.1.2" - }, - "dependencies": { - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, - "optional": true - } - } - }, - "rollup-plugin-copy": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/rollup-plugin-copy/-/rollup-plugin-copy-3.3.0.tgz", - "integrity": "sha512-euDjCUSBXZa06nqnwCNADbkAcYDfzwowfZQkto9K/TFhiH+QG7I4PUsEMwM9tDgomGWJc//z7KLW8t+tZwxADA==", - "dev": true, - "requires": { - "@types/fs-extra": "^8.0.1", - "colorette": "^1.1.0", - "fs-extra": "^8.1.0", - "globby": "10.0.1", - "is-plain-object": "^3.0.0" - }, - "dependencies": { - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "is-plain-object": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-3.0.1.tgz", - "integrity": "sha512-Xnpx182SBMrr/aBik8y+GuR4U1L9FqMSojwDQwPMmxyC6bvEqly9UBCxhauBF5vNh2gwWJNX6oDV7O+OM4z34g==", - "dev": true - } - } - }, - "rpc-websockets": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/rpc-websockets/-/rpc-websockets-5.2.4.tgz", - "integrity": "sha512-6jqeJK/18hPTsmeiN+K9O4miZiAmrIncgxfPXHwuGWs9BClA2zC3fOnTThRWo4blkrjH59oKKi0KMxSK+wdtNw==", - "requires": { - "@babel/runtime": "^7.8.7", - "assert-args": "^1.2.1", - "babel-runtime": "^6.26.0", - "circular-json": "^0.5.9", - "eventemitter3": "^3.1.2", - "uuid": "^3.4.0", - "ws": "^5.2.2" - }, - "dependencies": { - "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", - "requires": { - "async-limiter": "~1.0.0" - } - } - } - }, - "run-parallel": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", - "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", - "dev": true - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "optional": true, - "requires": { - "ret": "~0.1.10" - } - }, - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "optional": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" - }, - "shell-quote": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", - "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==" - }, - "shelljs": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz", - "integrity": "sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ==", - "dev": true, - "requires": { - "glob": "^7.0.0", - "interpret": "^1.0.0", - "rechoir": "^0.6.2" - } - }, - "signal-exit": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", - "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", - "dev": true - }, - "slash": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", - "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", - "dev": true - }, - "slice-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", - "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.0", - "astral-regex": "^1.0.0", - "is-fullwidth-code-point": "^2.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - } - } - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "optional": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "optional": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "optional": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "optional": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - }, - "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", - "dev": true, - "optional": true, - "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true, - "optional": true - }, - "sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "dev": true - }, - "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==" - }, - "spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.4.tgz", - "integrity": "sha512-7j8LYJLeY/Yb6ACbQ7F76qy5jHkp0U6jgBfJsk97bwWlVUnUWsAgpyaCvo17h0/RQGnQ036tVDomiwoI4pDkQA==" - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "optional": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "optional": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "optional": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "string.prototype.padend": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.0.tgz", - "integrity": "sha512-3aIv8Ffdp8EZj8iLwREGpQaUZiPyrWrpzMBHvkiSW/bK/EGve9np07Vwy7IJ5waydpGXzQZu/F8Oze2/IWkBaA==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" - }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" - }, - "is-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", - "requires": { - "has-symbols": "^1.0.1" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - } - } - }, - "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - }, - "dependencies": { - "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" - }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" - }, - "is-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", - "requires": { - "has-symbols": "^1.0.1" - } - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" - } - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=" - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "superstruct": { - "version": "0.8.4", - "resolved": "https://registry.npmjs.org/superstruct/-/superstruct-0.8.4.tgz", - "integrity": "sha512-48Ors8IVWZm/tMr8r0Si6+mJiB7mkD7jqvIzktjJ4+EnP5tBp0qOpiM1J8sCUorKx+TXWrfb3i1UcjdD1YK/wA==", - "requires": { - "kind-of": "^6.0.2", - "tiny-invariant": "^1.0.6" - } - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" - } - }, - "table": { - "version": "5.4.6", - "resolved": "https://registry.npmjs.org/table/-/table-5.4.6.tgz", - "integrity": "sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==", - "dev": true, - "requires": { - "ajv": "^6.10.2", - "lodash": "^4.17.14", - "slice-ansi": "^2.1.0", - "string-width": "^3.0.0" - }, - "dependencies": { - "ajv": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", - "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - } - } - }, - "tar": { - "version": "4.4.13", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", - "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", - "dev": true, - "optional": true, - "requires": { - "chownr": "^1.1.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.8.6", - "minizlib": "^1.2.1", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.2", - "yallist": "^3.0.3" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "thenify": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.0.tgz", - "integrity": "sha1-5p44obq+lpsBCCB5eLn2K4hgSDk=", - "requires": { - "any-promise": "^1.0.0" - } - }, - "thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha1-GhkY1ALY/D+Y+/I02wvMjMEOlyY=", - "requires": { - "thenify": ">= 3.1.0 < 4" - } - }, - "through": { - "version": "2.3.8", - "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "tiny-invariant": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.1.0.tgz", - "integrity": "sha512-ytxQvrb1cPc9WBEI/HSeYYoGD0kWnGEOR8RY6KomWLBVhqz0RgTwVO9dLrGz7dC+nN9llyI7OKAgRq8Vq4ZBSw==" - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-readable-stream": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-2.1.0.tgz", - "integrity": "sha512-o3Qa6DGg1CEXshSdvWNX2sN4QHqg03SPq7U6jPXRahlQdl5dK8oXjkU/2/sGrnOZKeGV1zLSO8qPwyKklPPE7w==", - "dev": true - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "optional": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "optional": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "optional": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "optional": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "traverse": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", - "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=", - "dev": true - }, - "tsconfig-paths": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", - "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.0", - "strip-bom": "^3.0.0" - } - }, - "tweetnacl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", - "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-detect": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", - "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=" - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - }, - "typescript": { - "version": "3.9.7", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz", - "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==", - "dev": true - }, - "typescript-compiler": { - "version": "1.4.1-2", - "resolved": "https://registry.npmjs.org/typescript-compiler/-/typescript-compiler-1.4.1-2.tgz", - "integrity": "sha1-uk99si2RU0oZKdkACdzhYety/T8=", - "dev": true - }, - "unicode-canonical-property-names-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz", - "integrity": "sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ==", - "dev": true - }, - "unicode-match-property-ecmascript": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz", - "integrity": "sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg==", - "dev": true, - "requires": { - "unicode-canonical-property-names-ecmascript": "^1.0.4", - "unicode-property-aliases-ecmascript": "^1.0.4" - } - }, - "unicode-match-property-value-ecmascript": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz", - "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==", - "dev": true - }, - "unicode-property-aliases-ecmascript": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz", - "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==", - "dev": true - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "optional": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "universal-user-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-4.0.1.tgz", - "integrity": "sha512-LnST3ebHwVL2aNe4mejI9IQh2HfZ1RLo8Io2HugSif8ekzD1TlWpHpColOB/eh8JHMLkGH3Akqf040I+4ylNxg==", - "dev": true, - "requires": { - "os-name": "^3.1.0" - } - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "optional": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "optional": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "optional": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true, - "optional": true - } - } - }, - "unzipper": { - "version": "0.10.11", - "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz", - "integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==", - "dev": true, - "requires": { - "big-integer": "^1.6.17", - "binary": "~0.3.0", - "bluebird": "~3.4.1", - "buffer-indexof-polyfill": "~1.0.0", - "duplexer2": "~0.1.4", - "fstream": "^1.0.12", - "graceful-fs": "^4.2.2", - "listenercount": "~1.0.1", - "readable-stream": "~2.3.6", - "setimmediate": "~1.0.4" - }, - "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - } - } - }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true, - "optional": true - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true, - "optional": true - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true, - "optional": true - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, - "v8-compile-cache": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz", - "integrity": "sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ==", - "dev": true - }, - "v8flags": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", - "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", - "dev": true, - "requires": { - "homedir-polyfill": "^1.0.1" - } - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "watch": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/watch/-/watch-1.0.2.tgz", - "integrity": "sha1-NApxe952Vyb6CqB9ch4BR6VR3ww=", - "dev": true, - "requires": { - "exec-sh": "^0.2.0", - "minimist": "^1.2.0" - } - }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "windows-release": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-3.3.1.tgz", - "integrity": "sha512-Pngk/RDCaI/DkuHPlGTdIkDiTAnAkyMjoQMZqRsxydNl1qGXNIoZrB7RK8g53F2tEgQBMqQJHQdYZuQEEAu54A==", - "dev": true, - "requires": { - "execa": "^1.0.0" - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "write": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", - "integrity": "sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig==", - "dev": true, - "requires": { - "mkdirp": "^0.5.1" - } - }, - "ws": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz", - "integrity": "sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA==" - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "optional": true - }, - "yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "dependencies": { - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - } - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - } - } -} diff --git a/program/js/package.json b/program/js/package.json deleted file mode 100644 index 4016239..0000000 --- a/program/js/package.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "name": "@solana/spl-token", - "version": "0.0.5", - "description": "SPL Token Javascript API", - "license": "MIT", - "author": "Solana Maintainers ", - "homepage": "https://solana.com/", - "repository": { - "type": "git", - "url": "https://github.com/solana-labs/solana-program-library" - }, - "bugs": { - "url": "https://github.com/solana-labs/solana-program-library/issues" - }, - "publishConfig": { - "access": "public" - }, - "main": "lib/index.cjs.js", - "module": "lib/index.esm.js", - "types": "lib/index.d.ts", - "files": [ - "/lib", - "/module.flow.js" - ], - "testnetDefaultChannel": "v1.3.1", - "scripts": { - "build": "rollup -c", - "start": "babel-node cli/main.js", - "lint": "npm run pretty && eslint .", - "lint:fix": "npm run lint -- --fix", - "flow": "flow", - "flow:watch": "watch 'flow' . --wait=1 --ignoreDirectoryPattern=/doc/", - "lint:watch": "watch 'npm run lint:fix' . --wait=1", - "build:program": "rm client/util/store/config.json; ../../do.sh build token", - "cluster:localnet": "rm -f .env", - "cluster:devnet": "cp cluster-devnet.env .env", - "cluster:testnet": "cp cluster-testnet.env .env", - "cluster:mainnet-beta": "cp cluster-mainnet-beta.env .env", - "localnet:update": "solana-localnet update", - "localnet:up": "rm client/util/store/config.json; set -x; solana-localnet down; set -e; solana-localnet up", - "localnet:down": "solana-localnet down", - "localnet:logs": "solana-localnet logs -f", - "pretty": "prettier --write '{,cli*/**/}*.js'" - }, - "dependencies": { - "@babel/runtime": "^7.10.5", - "@solana/web3.js": "^0.64.0", - "bn.js": "^5.0.0", - "buffer-layout": "^1.2.0", - "dotenv": "8.2.0", - "mkdirp-promise": "^5.0.1" - }, - "devDependencies": { - "@babel/cli": "^7.10.5", - "@babel/core": "^7.10.5", - "@babel/node": "^7.10.5", - "@babel/plugin-proposal-class-properties": "^7.10.4", - "@babel/plugin-transform-runtime": "^7.10.5", - "@babel/preset-env": "^7.10.4", - "@babel/preset-flow": "^7.10.4", - "@rollup/plugin-babel": "^5.1.0", - "@rollup/plugin-commonjs": "^14.0.0", - "babel-eslint": "^10.1.0", - "eslint": "^7.4.0", - "eslint-plugin-import": "^2.22.0", - "flow-bin": "0.131.0", - "flow-typed": "^3.2.0", - "mz": "^2.7.0", - "prettier": "^2.0.5", - "rollup": "^2.23.0", - "rollup-plugin-copy": "^3.3.0", - "semver": "^7.0.0", - "watch": "^1.0.2" - }, - "engines": { - "node": ">= 10" - } -} diff --git a/program/js/rollup.config.js b/program/js/rollup.config.js deleted file mode 100644 index 211e4b7..0000000 --- a/program/js/rollup.config.js +++ /dev/null @@ -1,74 +0,0 @@ -import babel from '@rollup/plugin-babel'; -import commonjs from '@rollup/plugin-commonjs'; -import copy from 'rollup-plugin-copy'; - -function generateConfig(configType) { - const config = { - input: 'client/token.js', - plugins: [ - babel({ - configFile: './babel.rollup.config.json', - exclude: 'node_modules/**', - babelHelpers: 'runtime', - }), - commonjs(), - copy({ - targets: [{src: 'module.d.ts', dest: 'lib', rename: 'index.d.ts'}], - }), - ], - }; - - switch (configType) { - case 'browser': - // TODO: Add support - break; - case 'node': - config.output = [ - { - file: 'lib/index.cjs.js', - format: 'cjs', - sourcemap: true, - }, - { - file: 'lib/index.esm.js', - format: 'es', - sourcemap: true, - }, - ]; - - // Quash 'Unresolved dependencies' complaints for modules listed in the - // package.json "dependencies" section. Unfortunately this list is manually - // maintained. - config.external = [ - 'assert', - '@babel/runtime/core-js/get-iterator', - '@babel/runtime/core-js/json/stringify', - '@babel/runtime/core-js/object/assign', - '@babel/runtime/core-js/object/get-prototype-of', - '@babel/runtime/core-js/object/keys', - '@babel/runtime/core-js/promise', - '@babel/runtime/helpers/asyncToGenerator', - '@babel/runtime/helpers/classCallCheck', - '@babel/runtime/helpers/createClass', - '@babel/runtime/helpers/defineProperty', - '@babel/runtime/helpers/get', - '@babel/runtime/helpers/getPrototypeOf', - '@babel/runtime/helpers/inherits', - '@babel/runtime/helpers/possibleConstructorReturn', - '@babel/runtime/helpers/slicedToArray', - '@babel/runtime/helpers/toConsumableArray', - '@babel/runtime/helpers/typeof', - '@babel/runtime/regenerator', - 'bn.js', - 'buffer-layout', - '@solana/web3.js', - ]; - break; - default: - throw new Error(`Unknown configType: ${configType}`); - } - - return config; -} - -export default [generateConfig('node')]; diff --git a/program/js/url.js b/program/js/url.js deleted file mode 100644 index 3138a7c..0000000 --- a/program/js/url.js +++ /dev/null @@ -1,31 +0,0 @@ -// To connect to a public cluster, set `export LIVE=1` in your -// environment. By default, `LIVE=1` will connect to the devnet cluster. - -import {clusterApiUrl, Cluster} from '@solana/web3.js'; -import dotenv from 'dotenv'; - -function chooseCluster(): Cluster | undefined { - dotenv.config(); - if (!process.env.LIVE) return; - switch (process.env.CLUSTER) { - case 'devnet': - case 'testnet': - case 'mainnet-beta': { - return process.env.CLUSTER; - } - } - throw 'Unknown cluster "' + process.env.CLUSTER + '", check the .env file'; -} - -export const cluster = chooseCluster(); - -export const url = - process.env.RPC_URL || - (process.env.LIVE ? clusterApiUrl(cluster, false) : 'http://localhost:8899'); - -export const urlTls = - process.env.RPC_URL || - (process.env.LIVE ? clusterApiUrl(cluster, true) : 'http://localhost:8899'); - -export let walletUrl = - process.env.WALLET_URL || 'https://solana-example-webwallet.herokuapp.com/'; From 9be628619ce339c7b51a2ae6145172c47cdd447a Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Sat, 8 Aug 2020 12:41:34 -0700 Subject: [PATCH 003/248] Update token.h --- program/inc/token.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/program/inc/token.h b/program/inc/token.h index 2efb6c3..e93170e 100644 --- a/program/inc/token.h +++ b/program/inc/token.h @@ -11,6 +11,11 @@ #define TOKEN_MINOR_VERSION 0 #define TOKEN_PATCH_VERSION 6 +/** + * There are 10^9 lamports in one SOL + */ +#define Token_DECIMALS 9 + /** * Maximum number of multisignature signers (max N) */ From 505883b64c35e5385648f0357438b5db46cd8c34 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Sat, 8 Aug 2020 15:27:08 -0700 Subject: [PATCH 004/248] Move program READMEs back --- program/README.md | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 program/README.md diff --git a/program/README.md b/program/README.md deleted file mode 100644 index 53b4e01..0000000 --- a/program/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# Token program - -A Fungible Token program on the Solana blockchain. - -This program provides an interface and implementation that third parties can -utilize to create and use their tokens. - -Full documentation is available at https://spl.solana.com - -Javascript binding are available in the `./js` directory. From 149abc363720e7a9f29fec55458f0731f5db4f41 Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Tue, 11 Aug 2020 16:41:56 -0600 Subject: [PATCH 005/248] Truncate SPL Token instruction data field (#260) --- program/src/instruction.rs | 151 ++++++++++++++++++++++++++++++++----- 1 file changed, 133 insertions(+), 18 deletions(-) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index e3e78e7..d1a950d 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -256,51 +256,85 @@ impl TokenInstruction { /// Packs a [TokenInstruction](enum.TokenInstruction.html) into a byte buffer. pub fn pack(self: &Self) -> Result, ProgramError> { let mut output = vec![0u8; size_of::()]; + let mut output_len = 0; match self { Self::InitializeMint { amount, decimals } => { - output[0] = 0; + output[output_len] = 0; + output_len += size_of::(); + #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[size_of::()] as *mut u8 as *mut u64) }; + let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; *value = *amount; - let value = - unsafe { &mut *(&mut output[size_of::() + size_of::()] as *mut u8) }; + output_len += size_of::(); + + let value = unsafe { &mut *(&mut output[output_len] as *mut u8) }; *value = *decimals; + output_len += size_of::(); + } + Self::InitializeAccount => { + output[output_len] = 1; + output_len += size_of::(); } - Self::InitializeAccount => output[0] = 1, Self::InitializeMultisig { m } => { - output[0] = 2; + output[output_len] = 2; + output_len += size_of::(); + #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[size_of::()] as *mut u8 as *mut u8) }; + let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u8) }; *value = *m; + output_len += size_of::(); } Self::Transfer { amount } => { - output[0] = 3; + output[output_len] = 3; + output_len += size_of::(); + #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[size_of::()] as *mut u8 as *mut u64) }; + let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; *value = *amount; + output_len += size_of::(); } Self::Approve { amount } => { - output[0] = 4; + output[output_len] = 4; + output_len += size_of::(); + #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[size_of::()] as *mut u8 as *mut u64) }; + let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; *value = *amount; + output_len += size_of::(); + } + Self::Revoke => { + output[output_len] = 5; + output_len += size_of::(); + } + Self::SetOwner => { + output[output_len] = 6; + output_len += size_of::(); } - Self::Revoke => output[0] = 5, - Self::SetOwner => output[0] = 6, Self::MintTo { amount } => { - output[0] = 7; + output[output_len] = 7; + output_len += size_of::(); + #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[size_of::()] as *mut u8 as *mut u64) }; + let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; *value = *amount; + output_len += size_of::(); } Self::Burn { amount } => { - output[0] = 8; + output[output_len] = 8; + output_len += size_of::(); + #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[size_of::()] as *mut u8 as *mut u64) }; + let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; *value = *amount; + output_len += size_of::(); + } + Self::CloseAccount => { + output[output_len] = 9; + output_len += size_of::(); } - Self::CloseAccount => output[0] = 9, } + + output.truncate(output_len); Ok(output) } } @@ -591,3 +625,84 @@ pub fn close_account( pub fn is_valid_signer_index(index: usize) -> bool { !(index < MIN_SIGNERS || index > MAX_SIGNERS) } + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_instruction_packing() { + let check = TokenInstruction::InitializeMint { + amount: 1, + decimals: 2, + }; + let packed = check.pack().unwrap(); + let expect = Vec::from([0u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::InitializeAccount; + let packed = check.pack().unwrap(); + let expect = Vec::from([1u8]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::InitializeMultisig { m: 1 }; + let packed = check.pack().unwrap(); + let expect = Vec::from([2u8, 1]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::Transfer { amount: 1 }; + let packed = check.pack().unwrap(); + let expect = Vec::from([3u8, 1, 0, 0, 0, 0, 0, 0, 0]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::Approve { amount: 1 }; + let packed = check.pack().unwrap(); + let expect = Vec::from([4u8, 1, 0, 0, 0, 0, 0, 0, 0]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::Revoke; + let packed = check.pack().unwrap(); + let expect = Vec::from([5u8]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::SetOwner; + let packed = check.pack().unwrap(); + let expect = Vec::from([6u8]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::MintTo { amount: 1 }; + let packed = check.pack().unwrap(); + let expect = Vec::from([7u8, 1, 0, 0, 0, 0, 0, 0, 0]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::Burn { amount: 1 }; + let packed = check.pack().unwrap(); + let expect = Vec::from([8u8, 1, 0, 0, 0, 0, 0, 0, 0]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::CloseAccount; + let packed = check.pack().unwrap(); + let expect = Vec::from([9u8]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + } +} From 99946e2e14534b83337f49b386c4c188e1c72696 Mon Sep 17 00:00:00 2001 From: Jack May Date: Tue, 11 Aug 2020 16:09:36 -0700 Subject: [PATCH 006/248] cbindgen does not produce deterministic header files (#261) --- program/inc/token.h | 2 +- program/src/instruction.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/program/inc/token.h b/program/inc/token.h index e93170e..7019ff4 100644 --- a/program/inc/token.h +++ b/program/inc/token.h @@ -43,7 +43,7 @@ typedef enum Token_TokenInstruction_Tag { * 1. * * If supply is non-zero: `[writable]` The account to hold all the newly minted tokens. * * If supply is zero: `[]` The owner/multisignature of the mint. - * 2. `[]` (optional) The owner/multisignature of the mint if supply is non-zero, if + * 2. `[]` (optional) The owner/multisignature of the mint if supply is non-zero, if * present then further minting is supported. * */ diff --git a/program/src/instruction.rs b/program/src/instruction.rs index d1a950d..efb6b4d 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -29,7 +29,7 @@ pub enum TokenInstruction { /// 1. /// * If supply is non-zero: `[writable]` The account to hold all the newly minted tokens. /// * If supply is zero: `[]` The owner/multisignature of the mint. - /// 2. `[]` (optional) The owner/multisignature of the mint if supply is non-zero, if + /// 2. `[]` (optional) The owner/multisignature of the mint if supply is non-zero, if /// present then further minting is supported. /// InitializeMint { From 352ede03658529161efccebc0357df5e11aafd59 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Wed, 12 Aug 2020 21:15:41 -0700 Subject: [PATCH 007/248] clippy --- program/src/instruction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index efb6b4d..d29f459 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -254,7 +254,7 @@ impl TokenInstruction { } /// Packs a [TokenInstruction](enum.TokenInstruction.html) into a byte buffer. - pub fn pack(self: &Self) -> Result, ProgramError> { + pub fn pack(&self) -> Result, ProgramError> { let mut output = vec![0u8; size_of::()]; let mut output_len = 0; match self { From a93349ae7a9f6b22720bce783d45c38abb132c4b Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Wed, 12 Aug 2020 21:45:43 -0700 Subject: [PATCH 008/248] Bump version to v1.0.7 --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index f370b73..68bd7c7 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "spl-token" -version = "1.0.6" +version = "1.0.7" description = "Solana Program Library Token" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" From bcc59ff6ed2dd68ec89b4ade9e6967037fe9de88 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Thu, 13 Aug 2020 22:08:32 -0700 Subject: [PATCH 009/248] clippy --- program/src/native_mint.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/program/src/native_mint.rs b/program/src/native_mint.rs index 6e80b28..41307b3 100644 --- a/program/src/native_mint.rs +++ b/program/src/native_mint.rs @@ -13,9 +13,8 @@ mod tests { #[test] fn test_decimals() { - assert_eq!( - lamports_to_sol(42), - crate::amount_to_ui_amount(42, DECIMALS) + assert!( + lamports_to_sol(42) - crate::amount_to_ui_amount(42, DECIMALS).abs() < f64::EPSILON ); assert_eq!( sol_to_lamports(42.), From 675cfc4eb67a54c9434127c00015268cbc56deab Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Thu, 13 Aug 2020 22:34:27 -0700 Subject: [PATCH 010/248] Bump token version --- program/inc/token.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/inc/token.h b/program/inc/token.h index 7019ff4..8676b6e 100644 --- a/program/inc/token.h +++ b/program/inc/token.h @@ -9,7 +9,7 @@ #define TOKEN_MAJOR_VERSION 1 #define TOKEN_MINOR_VERSION 0 -#define TOKEN_PATCH_VERSION 6 +#define TOKEN_PATCH_VERSION 7 /** * There are 10^9 lamports in one SOL From 4d20952e6007eaf0e18fed430e35a6e2cb690add Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Fri, 14 Aug 2020 08:42:04 -0700 Subject: [PATCH 011/248] Remove extra whitespace --- program/inc/token.h | 2 +- program/src/instruction.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/program/inc/token.h b/program/inc/token.h index 8676b6e..7b7be0e 100644 --- a/program/inc/token.h +++ b/program/inc/token.h @@ -43,7 +43,7 @@ typedef enum Token_TokenInstruction_Tag { * 1. * * If supply is non-zero: `[writable]` The account to hold all the newly minted tokens. * * If supply is zero: `[]` The owner/multisignature of the mint. - * 2. `[]` (optional) The owner/multisignature of the mint if supply is non-zero, if + * 2. `[]` (optional) The owner/multisignature of the mint if supply is non-zero, if * present then further minting is supported. * */ diff --git a/program/src/instruction.rs b/program/src/instruction.rs index d29f459..fc8d493 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -29,7 +29,7 @@ pub enum TokenInstruction { /// 1. /// * If supply is non-zero: `[writable]` The account to hold all the newly minted tokens. /// * If supply is zero: `[]` The owner/multisignature of the mint. - /// 2. `[]` (optional) The owner/multisignature of the mint if supply is non-zero, if + /// 2. `[]` (optional) The owner/multisignature of the mint if supply is non-zero, if /// present then further minting is supported. /// InitializeMint { From d1a5489118f3198cdfb0bf2da4ca47ad67f67a4b Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Fri, 14 Aug 2020 09:22:03 -0700 Subject: [PATCH 012/248] Exclude DECIMALS --- program/build.rs | 1 + program/inc/token.h | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/program/build.rs b/program/build.rs index 161616c..c85fabd 100644 --- a/program/build.rs +++ b/program/build.rs @@ -47,6 +47,7 @@ fn main() { "Account".to_string(), "Multisig".to_string(), ], + exclude: vec!["DECIMALS".to_string()], ..cbindgen::ExportConfig::default() }, parse: cbindgen::ParseConfig { diff --git a/program/inc/token.h b/program/inc/token.h index 7b7be0e..5bf5c5b 100644 --- a/program/inc/token.h +++ b/program/inc/token.h @@ -11,11 +11,6 @@ #define TOKEN_MINOR_VERSION 0 #define TOKEN_PATCH_VERSION 7 -/** - * There are 10^9 lamports in one SOL - */ -#define Token_DECIMALS 9 - /** * Maximum number of multisignature signers (max N) */ From 965a351af444d689814dd5eb4fe98b850d62fda2 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Fri, 14 Aug 2020 14:13:35 -0700 Subject: [PATCH 013/248] Add token program2 --- program/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 68bd7c7..cb2951b 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -31,5 +31,4 @@ rand = { version = "0.7.0"} cbindgen = "=0.14.2" [lib] -name = "spl_token" crate-type = ["cdylib", "lib"] From bf0e10a7aa701911f009e7bf3693af10dddac5af Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Fri, 14 Aug 2020 14:49:01 -0700 Subject: [PATCH 014/248] Back out 'Add support for self transfers (#210)' --- program/src/processor.rs | 39 --------------------------------------- 1 file changed, 39 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index c50d474..759edb6 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -133,10 +133,6 @@ impl Processor { let dest_account_info = next_account_info(account_info_iter)?; let authority_info = next_account_info(account_info_iter)?; - if source_account_info.key == dest_account_info.key { - return Ok(()); - } - let mut source_data = source_account_info.data.borrow_mut(); let mut source_account: &mut Account = state::unpack(&mut source_data)?; let mut dest_data = dest_account_info.data.borrow_mut(); @@ -853,41 +849,6 @@ mod tests { ) .unwrap(); - // transfer to self - { - let instruction = transfer( - &program_id, - &account_key, - &account_key, - &owner_key, - &[], - 500, - ) - .unwrap(); - let account_account_info = AccountInfo::from(( - &instruction.accounts[0].pubkey, - instruction.accounts[0].is_signer, - &mut account_account, - )); - let owner_account_info = AccountInfo::from(( - &instruction.accounts[2].pubkey, - instruction.accounts[2].is_signer, - &mut owner_account, - )); - Processor::process( - &instruction.program_id, - &[ - account_account_info.clone(), - account_account_info, - owner_account_info, - ], - &instruction.data, - ) - .unwrap() - } - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(account.amount, 1000); - // insufficient funds assert_eq!( Err(TokenError::InsufficientFunds.into()), From 6ae02415e3da3691fedcff25bbb98447d2c1381b Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Fri, 14 Aug 2020 18:08:46 -0700 Subject: [PATCH 015/248] Bump version to 1.0.8 --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index cb2951b..a6e3a65 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "spl-token" -version = "1.0.7" +version = "1.0.8" description = "Solana Program Library Token" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" From c49d7bf85a7178272f77d423bb3ecd04da433ccb Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Thu, 20 Aug 2020 13:41:11 -0700 Subject: [PATCH 016/248] Update version --- program/inc/token.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/inc/token.h b/program/inc/token.h index 5bf5c5b..d9ef5d2 100644 --- a/program/inc/token.h +++ b/program/inc/token.h @@ -9,7 +9,7 @@ #define TOKEN_MAJOR_VERSION 1 #define TOKEN_MINOR_VERSION 0 -#define TOKEN_PATCH_VERSION 7 +#define TOKEN_PATCH_VERSION 8 /** * Maximum number of multisignature signers (max N) From 2dda84bd17815f6ef4cd1baa26fb2d603ae74d14 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Thu, 20 Aug 2020 17:47:49 -0700 Subject: [PATCH 017/248] Remove duplicate --- program/build.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/program/build.rs b/program/build.rs index c85fabd..4862075 100644 --- a/program/build.rs +++ b/program/build.rs @@ -41,7 +41,6 @@ fn main() { export: cbindgen::ExportConfig { prefix: Some("Token_".to_string()), include: vec![ - "TokenInstruction".to_string(), "TokenInstruction".to_string(), "Mint".to_string(), "Account".to_string(), From a7c3911f4579c5c1b879f4263f03d9780048fc59 Mon Sep 17 00:00:00 2001 From: Jack May Date: Tue, 25 Aug 2020 08:48:26 -0700 Subject: [PATCH 018/248] Fix overflow when amounts are u64::MAX (#310) --- program/src/error.rs | 3 + program/src/processor.rs | 160 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 161 insertions(+), 2 deletions(-) diff --git a/program/src/error.rs b/program/src/error.rs index 32f9822..46bf59c 100644 --- a/program/src/error.rs +++ b/program/src/error.rs @@ -43,6 +43,9 @@ pub enum TokenError { /// Invalid instruction #[error("Invalid instruction")] InvalidInstruction, + /// Operation overflowed + #[error("Operation overflowed")] + Overflow, } impl From for ProgramError { fn from(e: TokenError) -> Self { diff --git a/program/src/processor.rs b/program/src/processor.rs index 759edb6..238c0d1 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -170,7 +170,10 @@ impl Processor { }; source_account.amount -= amount; - dest_account.amount += amount; + dest_account.amount = dest_account + .amount + .checked_add(amount) + .ok_or(TokenError::Overflow)?; if source_account.is_native { **source_account_info.lamports.borrow_mut() -= amount; @@ -304,7 +307,10 @@ impl Processor { } } - dest_account.amount += amount; + dest_account.amount = dest_account + .amount + .checked_add(amount) + .ok_or(TokenError::Overflow)?; Ok(()) } @@ -497,6 +503,7 @@ impl PrintProgramError for TokenError { info!("Error: Instruction does not support non-native tokens") } TokenError::InvalidInstruction => info!("Error: Invalid instruction"), + TokenError::Overflow => info!("Error: Operation overflowed"), } } } @@ -2190,4 +2197,153 @@ mod tests { assert_eq!(account.amount, 0); assert_eq!(account3_account.lamports, 4); } + + #[test] + fn test_overflow() { + let program_id = pubkey_rand(); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = pubkey_rand(); + let mut owner2_account = SolanaAccount::default(); + let mint_owner_key = pubkey_rand(); + let mut mint_owner_account = SolanaAccount::default(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + + // create victim account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint_key, &owner2_key).unwrap(), + vec![ + &mut account2_account, + &mut mint_account, + &mut owner2_account, + ], + ) + .unwrap(); + + // create new mint with owner + do_process_instruction( + initialize_mint(&program_id, &mint_key, None, Some(&mint_owner_key), 0, 2).unwrap(), + vec![&mut mint_account, &mut mint_owner_account], + ) + .unwrap(); + + // mint the max to attacker + do_process_instruction( + mint_to( + &program_id, + &mint_key, + &account2_key, + &mint_owner_key, + &[], + 42, + ) + .unwrap(), + vec![ + &mut mint_account, + &mut account2_account, + &mut mint_owner_account, + ], + ) + .unwrap(); + let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); + assert_eq!(account.amount, 42); + + // mint the max to victum + do_process_instruction( + mint_to( + &program_id, + &mint_key, + &account_key, + &mint_owner_key, + &[], + u64::MAX, + ) + .unwrap(), + vec![ + &mut mint_account, + &mut account_account, + &mut mint_owner_account, + ], + ) + .unwrap(); + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + assert_eq!(account.amount, u64::MAX); + + // mint one more + assert_eq!( + Err(TokenError::Overflow.into()), + do_process_instruction( + mint_to( + &program_id, + &mint_key, + &account_key, + &mint_owner_key, + &[], + 1, + ) + .unwrap(), + vec![ + &mut mint_account, + &mut account_account, + &mut mint_owner_account, + ], + ) + ); + + // mint back to large amount + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + account.amount = 0; + do_process_instruction( + mint_to( + &program_id, + &mint_key, + &account_key, + &mint_owner_key, + &[], + u64::MAX, + ) + .unwrap(), + vec![ + &mut mint_account, + &mut account_account, + &mut mint_owner_account, + ], + ) + .unwrap(); + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + assert_eq!(account.amount, u64::MAX); + + // transfer to burn victim + assert_eq!( + Err(TokenError::Overflow.into()), + do_process_instruction( + transfer( + &program_id, + &account2_key, + &account_key, + &owner2_key, + &[], + 1, + ) + .unwrap(), + vec![ + &mut account2_account, + &mut account_account, + &mut owner2_account, + ], + ) + ); + } } From df2e3dd34b0520bab0c7b2dbfcfa51d2d2482206 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 21 Aug 2020 00:47:01 +0000 Subject: [PATCH 019/248] Bump solana-sdk from 1.3.2 to 1.3.4 Bumps [solana-sdk](https://github.com/solana-labs/solana) from 1.3.2 to 1.3.4. - [Release notes](https://github.com/solana-labs/solana/releases) - [Changelog](https://github.com/solana-labs/solana/blob/master/RELEASE.md) - [Commits](https://github.com/solana-labs/solana/compare/v1.3.2...v1.3.4) Signed-off-by: dependabot[bot] --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index a6e3a65..cc045fc 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -21,7 +21,7 @@ default = ["solana-sdk/default"] num-derive = "0.3" num-traits = "0.2" remove_dir_all = "=0.5.0" -solana-sdk = { version = "1.2.17", default-features = false, optional = true } +solana-sdk = { version = "1.3.4", default-features = false, optional = true } thiserror = "1.0" [dev-dependencies] From 810a78e1d199dbbadc99d233e46fc19ce5472e1c Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Wed, 26 Aug 2020 22:08:27 -0700 Subject: [PATCH 020/248] Remove token 1.0 --- program/Cargo.toml | 34 - program/Xargo.toml | 2 - program/build.rs | 65 - program/inc/token.h | 359 ------ program/program-id.md | 1 - program/src/entrypoint.rs | 24 - program/src/error.rs | 59 - program/src/instruction.rs | 708 ----------- program/src/lib.rs | 28 - program/src/native_mint.rs | 24 - program/src/option.rs | 965 --------------- program/src/processor.rs | 2349 ------------------------------------ program/src/state.rs | 92 -- 13 files changed, 4710 deletions(-) delete mode 100644 program/Cargo.toml delete mode 100644 program/Xargo.toml delete mode 100644 program/build.rs delete mode 100644 program/inc/token.h delete mode 100644 program/program-id.md delete mode 100644 program/src/entrypoint.rs delete mode 100644 program/src/error.rs delete mode 100644 program/src/instruction.rs delete mode 100644 program/src/lib.rs delete mode 100644 program/src/native_mint.rs delete mode 100644 program/src/option.rs delete mode 100644 program/src/processor.rs delete mode 100644 program/src/state.rs diff --git a/program/Cargo.toml b/program/Cargo.toml deleted file mode 100644 index cc045fc..0000000 --- a/program/Cargo.toml +++ /dev/null @@ -1,34 +0,0 @@ - -# Note: This crate must be built using do.sh - -[package] -name = "spl-token" -version = "1.0.8" -description = "Solana Program Library Token" -authors = ["Solana Maintainers "] -repository = "https://github.com/solana-labs/solana-program-library" -license = "Apache-2.0" -edition = "2018" -exclude = ["js/**"] - -[features] -no-entrypoint = [] -skip-no-mangle = ["solana-sdk/skip-no-mangle"] -program = ["solana-sdk/program"] -default = ["solana-sdk/default"] - -[dependencies] -num-derive = "0.3" -num-traits = "0.2" -remove_dir_all = "=0.5.0" -solana-sdk = { version = "1.3.4", default-features = false, optional = true } -thiserror = "1.0" - -[dev-dependencies] -rand = { version = "0.7.0"} - -[build-dependencies] -cbindgen = "=0.14.2" - -[lib] -crate-type = ["cdylib", "lib"] diff --git a/program/Xargo.toml b/program/Xargo.toml deleted file mode 100644 index 1744f09..0000000 --- a/program/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/program/build.rs b/program/build.rs deleted file mode 100644 index 4862075..0000000 --- a/program/build.rs +++ /dev/null @@ -1,65 +0,0 @@ -extern crate cbindgen; - -use std::env; - -fn main() { - println!("cargo:rerun-if-env-changed=SPL_CBINDGEN"); - println!("cargo:rerun-if-changed=inc/token.h"); - if std::path::Path::new("inc/token.h").exists() && env::var("SPL_CBINDGEN").is_err() { - return; - } - - println!("cargo:warning=Generating inc/token.h"); - let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); - let config = cbindgen::Config { - header: Some("/* Autogenerated SPL Token program C Bindings */".to_string()), - after_includes: Some(format!( - "{}{}{}", - format!( - "\n#define TOKEN_MAJOR_VERSION {}", - env!("CARGO_PKG_VERSION_MAJOR") - ), - format!( - "\n#define TOKEN_MINOR_VERSION {}", - env!("CARGO_PKG_VERSION_MINOR") - ), - format!( - "\n#define TOKEN_PATCH_VERSION {}", - env!("CARGO_PKG_VERSION_PATCH") - ) - )), - language: cbindgen::Language::C, - line_length: 80, - style: cbindgen::Style::Both, - tab_width: 4, - cpp_compat: true, - pragma_once: true, - enumeration: cbindgen::EnumConfig { - prefix_with_name: true, - ..cbindgen::EnumConfig::default() - }, - export: cbindgen::ExportConfig { - prefix: Some("Token_".to_string()), - include: vec![ - "TokenInstruction".to_string(), - "Mint".to_string(), - "Account".to_string(), - "Multisig".to_string(), - ], - exclude: vec!["DECIMALS".to_string()], - ..cbindgen::ExportConfig::default() - }, - parse: cbindgen::ParseConfig { - parse_deps: true, - include: Some(vec!["solana-sdk".to_string()]), - ..cbindgen::ParseConfig::default() - }, - ..cbindgen::Config::default() - }; - cbindgen::Builder::new() - .with_crate(crate_dir) - .with_config(config) - .generate() - .unwrap() - .write_to_file("inc/token.h"); -} diff --git a/program/inc/token.h b/program/inc/token.h deleted file mode 100644 index d9ef5d2..0000000 --- a/program/inc/token.h +++ /dev/null @@ -1,359 +0,0 @@ -/* Autogenerated SPL Token program C Bindings */ - -#pragma once - -#include -#include -#include -#include - -#define TOKEN_MAJOR_VERSION 1 -#define TOKEN_MINOR_VERSION 0 -#define TOKEN_PATCH_VERSION 8 - -/** - * Maximum number of multisignature signers (max N) - */ -#define Token_MAX_SIGNERS 11 - -/** - * Minimum number of multisignature signers (min N) - */ -#define Token_MIN_SIGNERS 1 - -/** - * Instructions supported by the token program. - */ -typedef enum Token_TokenInstruction_Tag { - /** - * Initializes a new mint and optionally deposits all the newly minted tokens in an account. - * - * The `InitializeMint` instruction requires no signers and MUST be included within - * the same Transaction as the system program's `CreateInstruction` that creates the account - * being initialized. Otherwise another party can acquire ownership of the uninitialized account. - * - * Accounts expected by this instruction: - * - * 0. `[writable]` The mint to initialize. - * 1. - * * If supply is non-zero: `[writable]` The account to hold all the newly minted tokens. - * * If supply is zero: `[]` The owner/multisignature of the mint. - * 2. `[]` (optional) The owner/multisignature of the mint if supply is non-zero, if - * present then further minting is supported. - * - */ - Token_TokenInstruction_InitializeMint, - /** - * Initializes a new account to hold tokens. If this account is associated with the native mint - * then the token balance of the initialized account will be equal to the amount of SOL in the account. - * - * The `InitializeAccount` instruction requires no signers and MUST be included within - * the same Transaction as the system program's `CreateInstruction` that creates the account - * being initialized. Otherwise another party can acquire ownership of the uninitialized account. - * - * Accounts expected by this instruction: - * - * 0. `[writable]` The account to initialize. - * 1. `[]` The mint this account will be associated with. - * 2. `[]` The new account's owner/multisignature. - */ - Token_TokenInstruction_InitializeAccount, - /** - * Initializes a multisignature account with N provided signers. - * - * Multisignature accounts can used in place of any single owner/delegate accounts in any - * token instruction that require an owner/delegate to be present. The variant field represents the - * number of signers (M) required to validate this multisignature account. - * - * The `InitializeMultisig` instruction requires no signers and MUST be included within - * the same Transaction as the system program's `CreateInstruction` that creates the account - * being initialized. Otherwise another party can acquire ownership of the uninitialized account. - * - * Accounts expected by this instruction: - * - * 0. `[writable]` The multisignature account to initialize. - * 1. ..1+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. - */ - Token_TokenInstruction_InitializeMultisig, - /** - * Transfers tokens from one account to another either directly or via a delegate. If this - * account is associated with the native mint then equal amounts of SOL and Tokens will be - * transferred to the destination account. - * - * Accounts expected by this instruction: - * - * * Single owner/delegate - * 0. `[writable]` The source account. - * 1. `[writable]` The destination account. - * 2. '[signer]' The source account's owner/delegate. - * - * * Multisignature owner/delegate - * 0. `[writable]` The source account. - * 1. `[writable]` The destination account. - * 2. '[]' The source account's multisignature owner/delegate. - * 3. ..3+M '[signer]' M signer accounts. - */ - Token_TokenInstruction_Transfer, - /** - * Approves a delegate. A delegate is given the authority over - * tokens on behalf of the source account's owner. - * Accounts expected by this instruction: - * - * * Single owner - * 0. `[writable]` The source account. - * 1. `[]` The delegate. - * 2. `[signer]` The source account owner. - * - * * Multisignature owner - * 0. `[writable]` The source account. - * 1. `[]` The delegate. - * 2. '[]' The source account's multisignature owner. - * 3. ..3+M '[signer]' M signer accounts - */ - Token_TokenInstruction_Approve, - /** - * Revokes the delegate's authority. - * - * Accounts expected by this instruction: - * - * * Single owner - * 0. `[writable]` The source account. - * 1. `[signer]` The source account owner. - * - * * Multisignature owner - * 0. `[writable]` The source account. - * 1. '[]' The source account's multisignature owner. - * 2. ..2+M '[signer]' M signer accounts - */ - Token_TokenInstruction_Revoke, - /** - * Sets a new owner of a mint or account. - * - * Accounts expected by this instruction: - * - * * Single owner - * 0. `[writable]` The mint or account to change the owner of. - * 1. `[]` The new owner/delegate/multisignature. - * 2. `[signer]` The owner of the mint or account. - * - * * Multisignature owner - * 0. `[writable]` The mint or account to change the owner of. - * 1. `[]` The new owner/delegate/multisignature. - * 2. `[]` The mint's or account's multisignature owner. - * 3. ..3+M '[signer]' M signer accounts - */ - Token_TokenInstruction_SetOwner, - /** - * Mints new tokens to an account. The native mint does not support minting. - * - * Accounts expected by this instruction: - * - * * Single owner - * 0. `[writable]` The mint. - * 1. `[writable]` The account to mint tokens to. - * 2. `[signer]` The mint's owner. - * - * * Multisignature owner - * 0. `[writable]` The mint. - * 1. `[writable]` The account to mint tokens to. - * 2. `[]` The mint's multisignature owner. - * 3. ..3+M '[signer]' M signer accounts. - */ - Token_TokenInstruction_MintTo, - /** - * Burns tokens by removing them from an account. `Burn` does not support accounts - * associated with the native mint, use `CloseAccount` instead. - * - * Accounts expected by this instruction: - * - * * Single owner/delegate - * 0. `[writable]` The account to burn from. - * 1. `[signer]` The account's owner/delegate. - * - * * Multisignature owner/delegate - * 0. `[writable]` The account to burn from. - * 1. `[]` The account's multisignature owner/delegate. - * 2. ..2+M '[signer]' M signer accounts. - */ - Token_TokenInstruction_Burn, - /** - * Close an account by transferring all its SOL to the destination account. - * Non-native accounts may only be closed if its token amount is zero. - * - * Accounts expected by this instruction: - * - * * Single owner - * 0. `[writable]` The account to close. - * 1. '[writable]' The destination account. - * 2. `[signer]` The account's owner. - * - * * Multisignature owner - * 0. `[writable]` The account to close. - * 1. '[writable]' The destination account. - * 2. `[]` The account's multisignature owner. - * 3. ..3+M '[signer]' M signer accounts. - */ - Token_TokenInstruction_CloseAccount, -} Token_TokenInstruction_Tag; - -typedef struct Token_TokenInstruction_Token_InitializeMint_Body { - /** - * Initial amount of tokens to mint. - */ - uint64_t amount; - /** - * Number of base 10 digits to the right of the decimal place. - */ - uint8_t decimals; -} Token_TokenInstruction_Token_InitializeMint_Body; - -typedef struct Token_TokenInstruction_Token_InitializeMultisig_Body { - /** - * The number of signers (M) required to validate this multisignature account. - */ - uint8_t m; -} Token_TokenInstruction_Token_InitializeMultisig_Body; - -typedef struct Token_TokenInstruction_Token_Transfer_Body { - /** - * The amount of tokens to transfer. - */ - uint64_t amount; -} Token_TokenInstruction_Token_Transfer_Body; - -typedef struct Token_TokenInstruction_Token_Approve_Body { - /** - * The amount of tokens the delegate is approved for. - */ - uint64_t amount; -} Token_TokenInstruction_Token_Approve_Body; - -typedef struct Token_TokenInstruction_Token_MintTo_Body { - /** - * The amount of new tokens to mint. - */ - uint64_t amount; -} Token_TokenInstruction_Token_MintTo_Body; - -typedef struct Token_TokenInstruction_Token_Burn_Body { - /** - * The amount of tokens to burn. - */ - uint64_t amount; -} Token_TokenInstruction_Token_Burn_Body; - -typedef struct Token_TokenInstruction { - Token_TokenInstruction_Tag tag; - union { - Token_TokenInstruction_Token_InitializeMint_Body initialize_mint; - Token_TokenInstruction_Token_InitializeMultisig_Body initialize_multisig; - Token_TokenInstruction_Token_Transfer_Body transfer; - Token_TokenInstruction_Token_Approve_Body approve; - Token_TokenInstruction_Token_MintTo_Body mint_to; - Token_TokenInstruction_Token_Burn_Body burn; - }; -} Token_TokenInstruction; - -typedef uint8_t Token_Pubkey[32]; - -/** - * A C representation of Rust's `std::option::Option` - */ -typedef enum Token_COption_Pubkey_Tag { - /** - * No value - */ - Token_COption_Pubkey_None_Pubkey, - /** - * Some value `T` - */ - Token_COption_Pubkey_Some_Pubkey, -} Token_COption_Pubkey_Tag; - -typedef struct Token_COption_Pubkey_Token_Some_Body_Pubkey { - Token_Pubkey _0; -} Token_COption_Pubkey_Token_Some_Body_Pubkey; - -typedef struct Token_COption_Pubkey { - Token_COption_Pubkey_Tag tag; - union { - Token_COption_Pubkey_Token_Some_Body_Pubkey some; - }; -} Token_COption_Pubkey; - -/** - * Mint data. - */ -typedef struct Token_Mint { - /** - * Optional owner, used to mint new tokens. The owner may only - * be provided during mint creation. If no owner is present then the mint - * has a fixed supply and no further tokens may be minted. - */ - Token_COption_Pubkey owner; - /** - * Number of base 10 digits to the right of the decimal place. - */ - uint8_t decimals; - /** - * Is `true` if this structure has been initialized - */ - bool is_initialized; -} Token_Mint; - -/** - * Account data. - */ -typedef struct Token_Account { - /** - * The mint associated with this account - */ - Token_Pubkey mint; - /** - * The owner of this account. - */ - Token_Pubkey owner; - /** - * The amount of tokens this account holds. - */ - uint64_t amount; - /** - * If `delegate` is `Some` then `delegated_amount` represents - * the amount authorized by the delegate - */ - Token_COption_Pubkey delegate; - /** - * Is `true` if this structure has been initialized - */ - bool is_initialized; - /** - * Is this a native token - */ - bool is_native; - /** - * The amount delegated - */ - uint64_t delegated_amount; -} Token_Account; - -/** - * Multisignature data. - */ -typedef struct Token_Multisig { - /** - * Number of signers required - */ - uint8_t m; - /** - * Number of valid signers - */ - uint8_t n; - /** - * Is `true` if this structure has been initialized - */ - bool is_initialized; - /** - * Signer public keys - */ - Token_Pubkey signers[Token_MAX_SIGNERS]; -} Token_Multisig; diff --git a/program/program-id.md b/program/program-id.md deleted file mode 100644 index 77df2a0..0000000 --- a/program/program-id.md +++ /dev/null @@ -1 +0,0 @@ -TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o diff --git a/program/src/entrypoint.rs b/program/src/entrypoint.rs deleted file mode 100644 index b5594ca..0000000 --- a/program/src/entrypoint.rs +++ /dev/null @@ -1,24 +0,0 @@ -//! Program entrypoint - -#![cfg(feature = "program")] -#![cfg(not(feature = "no-entrypoint"))] - -use crate::{error::TokenError, processor::Processor}; -use solana_sdk::{ - account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, - program_error::PrintProgramError, pubkey::Pubkey, -}; - -entrypoint!(process_instruction); -fn process_instruction<'a>( - program_id: &Pubkey, - accounts: &'a [AccountInfo<'a>], - instruction_data: &[u8], -) -> ProgramResult { - if let Err(error) = Processor::process(program_id, accounts, instruction_data) { - // catch the error so we can print it - error.print::(); - return Err(error); - } - Ok(()) -} diff --git a/program/src/error.rs b/program/src/error.rs deleted file mode 100644 index 46bf59c..0000000 --- a/program/src/error.rs +++ /dev/null @@ -1,59 +0,0 @@ -//! Error types - -use num_derive::FromPrimitive; -use solana_sdk::{decode_error::DecodeError, program_error::ProgramError}; -use thiserror::Error; - -/// Errors that may be returned by the Token program. -#[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)] -pub enum TokenError { - /// Insufficient funds for the operation requested. - #[error("Insufficient funds")] - InsufficientFunds, - /// Account not associated with this Mint. - #[error("Account not associated with this Mint")] - MintMismatch, - /// Owner does not match. - #[error("Owner does not match")] - OwnerMismatch, - /// This token's supply is fixed and new tokens cannot be minted. - #[error("Fixed supply")] - FixedSupply, - /// The account cannot be initialized because it is already being used. - #[error("AlreadyInUse")] - AlreadyInUse, - /// An owner is required if initial supply is zero. - #[error("An owner is required if supply is zero")] - OwnerRequiredIfNoInitialSupply, - /// Invalid number of provided signers. - #[error("Invalid number of provided signers")] - InvalidNumberOfProvidedSigners, - /// Invalid number of required signers. - #[error("Invalid number of required signers")] - InvalidNumberOfRequiredSigners, - /// State is uninitialized. - #[error("State is unititialized")] - UninitializedState, - /// Instruction does not support native tokens - #[error("Instruction does not support native tokens")] - NativeNotSupported, - /// Instruction does not support non-native tokens - #[error("Instruction does not support non-native tokens")] - NonNativeNotSupported, - /// Invalid instruction - #[error("Invalid instruction")] - InvalidInstruction, - /// Operation overflowed - #[error("Operation overflowed")] - Overflow, -} -impl From for ProgramError { - fn from(e: TokenError) -> Self { - ProgramError::Custom(e as u32) - } -} -impl DecodeError for TokenError { - fn type_of() -> &'static str { - "TokenError" - } -} diff --git a/program/src/instruction.rs b/program/src/instruction.rs deleted file mode 100644 index fc8d493..0000000 --- a/program/src/instruction.rs +++ /dev/null @@ -1,708 +0,0 @@ -//! Instruction types - -use crate::error::TokenError; -use solana_sdk::{ - instruction::{AccountMeta, Instruction}, - program_error::ProgramError, - pubkey::Pubkey, -}; -use std::mem::size_of; - -/// Minimum number of multisignature signers (min N) -pub const MIN_SIGNERS: usize = 1; -/// Maximum number of multisignature signers (max N) -pub const MAX_SIGNERS: usize = 11; - -/// Instructions supported by the token program. -#[repr(C)] -#[derive(Clone, Debug, PartialEq)] -pub enum TokenInstruction { - /// Initializes a new mint and optionally deposits all the newly minted tokens in an account. - /// - /// The `InitializeMint` instruction requires no signers and MUST be included within - /// the same Transaction as the system program's `CreateInstruction` that creates the account - /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. - /// - /// Accounts expected by this instruction: - /// - /// 0. `[writable]` The mint to initialize. - /// 1. - /// * If supply is non-zero: `[writable]` The account to hold all the newly minted tokens. - /// * If supply is zero: `[]` The owner/multisignature of the mint. - /// 2. `[]` (optional) The owner/multisignature of the mint if supply is non-zero, if - /// present then further minting is supported. - /// - InitializeMint { - /// Initial amount of tokens to mint. - amount: u64, - /// Number of base 10 digits to the right of the decimal place. - decimals: u8, - }, - /// Initializes a new account to hold tokens. If this account is associated with the native mint - /// then the token balance of the initialized account will be equal to the amount of SOL in the account. - /// - /// The `InitializeAccount` instruction requires no signers and MUST be included within - /// the same Transaction as the system program's `CreateInstruction` that creates the account - /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. - /// - /// Accounts expected by this instruction: - /// - /// 0. `[writable]` The account to initialize. - /// 1. `[]` The mint this account will be associated with. - /// 2. `[]` The new account's owner/multisignature. - InitializeAccount, - /// Initializes a multisignature account with N provided signers. - /// - /// Multisignature accounts can used in place of any single owner/delegate accounts in any - /// token instruction that require an owner/delegate to be present. The variant field represents the - /// number of signers (M) required to validate this multisignature account. - /// - /// The `InitializeMultisig` instruction requires no signers and MUST be included within - /// the same Transaction as the system program's `CreateInstruction` that creates the account - /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. - /// - /// Accounts expected by this instruction: - /// - /// 0. `[writable]` The multisignature account to initialize. - /// 1. ..1+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. - InitializeMultisig { - /// The number of signers (M) required to validate this multisignature account. - m: u8, - }, - /// Transfers tokens from one account to another either directly or via a delegate. If this - /// account is associated with the native mint then equal amounts of SOL and Tokens will be - /// transferred to the destination account. - /// - /// Accounts expected by this instruction: - /// - /// * Single owner/delegate - /// 0. `[writable]` The source account. - /// 1. `[writable]` The destination account. - /// 2. '[signer]' The source account's owner/delegate. - /// - /// * Multisignature owner/delegate - /// 0. `[writable]` The source account. - /// 1. `[writable]` The destination account. - /// 2. '[]' The source account's multisignature owner/delegate. - /// 3. ..3+M '[signer]' M signer accounts. - Transfer { - /// The amount of tokens to transfer. - amount: u64, - }, - /// Approves a delegate. A delegate is given the authority over - /// tokens on behalf of the source account's owner. - - /// Accounts expected by this instruction: - /// - /// * Single owner - /// 0. `[writable]` The source account. - /// 1. `[]` The delegate. - /// 2. `[signer]` The source account owner. - /// - /// * Multisignature owner - /// 0. `[writable]` The source account. - /// 1. `[]` The delegate. - /// 2. '[]' The source account's multisignature owner. - /// 3. ..3+M '[signer]' M signer accounts - Approve { - /// The amount of tokens the delegate is approved for. - amount: u64, - }, - /// Revokes the delegate's authority. - /// - /// Accounts expected by this instruction: - /// - /// * Single owner - /// 0. `[writable]` The source account. - /// 1. `[signer]` The source account owner. - /// - /// * Multisignature owner - /// 0. `[writable]` The source account. - /// 1. '[]' The source account's multisignature owner. - /// 2. ..2+M '[signer]' M signer accounts - Revoke, - /// Sets a new owner of a mint or account. - /// - /// Accounts expected by this instruction: - /// - /// * Single owner - /// 0. `[writable]` The mint or account to change the owner of. - /// 1. `[]` The new owner/delegate/multisignature. - /// 2. `[signer]` The owner of the mint or account. - /// - /// * Multisignature owner - /// 0. `[writable]` The mint or account to change the owner of. - /// 1. `[]` The new owner/delegate/multisignature. - /// 2. `[]` The mint's or account's multisignature owner. - /// 3. ..3+M '[signer]' M signer accounts - SetOwner, - /// Mints new tokens to an account. The native mint does not support minting. - /// - /// Accounts expected by this instruction: - /// - /// * Single owner - /// 0. `[writable]` The mint. - /// 1. `[writable]` The account to mint tokens to. - /// 2. `[signer]` The mint's owner. - /// - /// * Multisignature owner - /// 0. `[writable]` The mint. - /// 1. `[writable]` The account to mint tokens to. - /// 2. `[]` The mint's multisignature owner. - /// 3. ..3+M '[signer]' M signer accounts. - MintTo { - /// The amount of new tokens to mint. - amount: u64, - }, - /// Burns tokens by removing them from an account. `Burn` does not support accounts - /// associated with the native mint, use `CloseAccount` instead. - /// - /// Accounts expected by this instruction: - /// - /// * Single owner/delegate - /// 0. `[writable]` The account to burn from. - /// 1. `[signer]` The account's owner/delegate. - /// - /// * Multisignature owner/delegate - /// 0. `[writable]` The account to burn from. - /// 1. `[]` The account's multisignature owner/delegate. - /// 2. ..2+M '[signer]' M signer accounts. - Burn { - /// The amount of tokens to burn. - amount: u64, - }, - /// Close an account by transferring all its SOL to the destination account. - /// Non-native accounts may only be closed if its token amount is zero. - /// - /// Accounts expected by this instruction: - /// - /// * Single owner - /// 0. `[writable]` The account to close. - /// 1. '[writable]' The destination account. - /// 2. `[signer]` The account's owner. - /// - /// * Multisignature owner - /// 0. `[writable]` The account to close. - /// 1. '[writable]' The destination account. - /// 2. `[]` The account's multisignature owner. - /// 3. ..3+M '[signer]' M signer accounts. - CloseAccount, -} -impl TokenInstruction { - /// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html). - pub fn unpack(input: &[u8]) -> Result { - if input.len() < size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - Ok(match input[0] { - 0 => { - if input.len() < size_of::() + size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; - let decimals = - unsafe { *(&input[size_of::() + size_of::()] as *const u8) }; - Self::InitializeMint { amount, decimals } - } - 1 => Self::InitializeAccount, - 2 => { - if input.len() < size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - #[allow(clippy::cast_ptr_alignment)] - let m = unsafe { *(&input[1] as *const u8) }; - Self::InitializeMultisig { m } - } - 3 => { - if input.len() < size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; - Self::Transfer { amount } - } - 4 => { - if input.len() < size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; - Self::Approve { amount } - } - 5 => Self::Revoke, - 6 => Self::SetOwner, - 7 => { - if input.len() < size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; - Self::MintTo { amount } - } - 8 => { - if input.len() < size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; - Self::Burn { amount } - } - 9 => Self::CloseAccount, - _ => return Err(TokenError::InvalidInstruction.into()), - }) - } - - /// Packs a [TokenInstruction](enum.TokenInstruction.html) into a byte buffer. - pub fn pack(&self) -> Result, ProgramError> { - let mut output = vec![0u8; size_of::()]; - let mut output_len = 0; - match self { - Self::InitializeMint { amount, decimals } => { - output[output_len] = 0; - output_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; - *value = *amount; - output_len += size_of::(); - - let value = unsafe { &mut *(&mut output[output_len] as *mut u8) }; - *value = *decimals; - output_len += size_of::(); - } - Self::InitializeAccount => { - output[output_len] = 1; - output_len += size_of::(); - } - Self::InitializeMultisig { m } => { - output[output_len] = 2; - output_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u8) }; - *value = *m; - output_len += size_of::(); - } - Self::Transfer { amount } => { - output[output_len] = 3; - output_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; - *value = *amount; - output_len += size_of::(); - } - Self::Approve { amount } => { - output[output_len] = 4; - output_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; - *value = *amount; - output_len += size_of::(); - } - Self::Revoke => { - output[output_len] = 5; - output_len += size_of::(); - } - Self::SetOwner => { - output[output_len] = 6; - output_len += size_of::(); - } - Self::MintTo { amount } => { - output[output_len] = 7; - output_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; - *value = *amount; - output_len += size_of::(); - } - Self::Burn { amount } => { - output[output_len] = 8; - output_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; - *value = *amount; - output_len += size_of::(); - } - Self::CloseAccount => { - output[output_len] = 9; - output_len += size_of::(); - } - } - - output.truncate(output_len); - Ok(output) - } -} - -/// Creates a 'InitializeMint' instruction. -pub fn initialize_mint( - token_program_id: &Pubkey, - mint_pubkey: &Pubkey, - account_pubkey: Option<&Pubkey>, - owner_pubkey: Option<&Pubkey>, - amount: u64, - decimals: u8, -) -> Result { - let data = TokenInstruction::InitializeMint { amount, decimals }.pack()?; - - let mut accounts = vec![AccountMeta::new(*mint_pubkey, false)]; - if amount != 0 { - match account_pubkey { - Some(pubkey) => accounts.push(AccountMeta::new(*pubkey, false)), - None => { - return Err(ProgramError::NotEnoughAccountKeys); - } - } - } - match owner_pubkey { - Some(pubkey) => accounts.push(AccountMeta::new_readonly(*pubkey, false)), - None => { - if amount == 0 { - return Err(TokenError::OwnerRequiredIfNoInitialSupply.into()); - } - } - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates a `InitializeAccount` instruction. -pub fn initialize_account( - token_program_id: &Pubkey, - account_pubkey: &Pubkey, - mint_pubkey: &Pubkey, - owner_pubkey: &Pubkey, -) -> Result { - let data = TokenInstruction::InitializeAccount.pack()?; - - let accounts = vec![ - AccountMeta::new(*account_pubkey, false), - AccountMeta::new_readonly(*mint_pubkey, false), - AccountMeta::new_readonly(*owner_pubkey, false), - ]; - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates a `InitializeMultisig` instruction. -pub fn initialize_multisig( - token_program_id: &Pubkey, - multisig_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], - m: u8, -) -> Result { - if !is_valid_signer_index(m as usize) - || !is_valid_signer_index(signer_pubkeys.len()) - || m as usize > signer_pubkeys.len() - { - return Err(ProgramError::MissingRequiredSignature); - } - let data = TokenInstruction::InitializeMultisig { m }.pack()?; - - let mut accounts = Vec::with_capacity(1 + signer_pubkeys.len()); - accounts.push(AccountMeta::new(*multisig_pubkey, false)); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new_readonly(**signer_pubkey, false)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates a `Transfer` instruction. -pub fn transfer( - token_program_id: &Pubkey, - source_pubkey: &Pubkey, - destination_pubkey: &Pubkey, - authority_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], - amount: u64, -) -> Result { - let data = TokenInstruction::Transfer { amount }.pack()?; - - let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); - accounts.push(AccountMeta::new(*source_pubkey, false)); - accounts.push(AccountMeta::new(*destination_pubkey, false)); - accounts.push(AccountMeta::new_readonly( - *authority_pubkey, - signer_pubkeys.is_empty(), - )); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates an `Approve` instruction. -pub fn approve( - token_program_id: &Pubkey, - source_pubkey: &Pubkey, - delegate_pubkey: &Pubkey, - owner_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], - amount: u64, -) -> Result { - let data = TokenInstruction::Approve { amount }.pack()?; - - let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); - accounts.push(AccountMeta::new(*source_pubkey, false)); - accounts.push(AccountMeta::new_readonly(*delegate_pubkey, false)); - accounts.push(AccountMeta::new_readonly( - *owner_pubkey, - signer_pubkeys.is_empty(), - )); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates a `Revoke` instruction. -pub fn revoke( - token_program_id: &Pubkey, - source_pubkey: &Pubkey, - owner_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], -) -> Result { - let data = TokenInstruction::Revoke.pack()?; - - let mut accounts = Vec::with_capacity(2 + signer_pubkeys.len()); - accounts.push(AccountMeta::new_readonly(*source_pubkey, false)); - accounts.push(AccountMeta::new_readonly( - *owner_pubkey, - signer_pubkeys.is_empty(), - )); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates a `SetOwner` instruction. -pub fn set_owner( - token_program_id: &Pubkey, - owned_pubkey: &Pubkey, - new_owner_pubkey: &Pubkey, - owner_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], -) -> Result { - let data = TokenInstruction::SetOwner.pack()?; - - let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); - accounts.push(AccountMeta::new(*owned_pubkey, false)); - accounts.push(AccountMeta::new_readonly(*new_owner_pubkey, false)); - accounts.push(AccountMeta::new_readonly( - *owner_pubkey, - signer_pubkeys.is_empty(), - )); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates a `MintTo` instruction. -pub fn mint_to( - token_program_id: &Pubkey, - mint_pubkey: &Pubkey, - account_pubkey: &Pubkey, - owner_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], - amount: u64, -) -> Result { - let data = TokenInstruction::MintTo { amount }.pack()?; - - let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); - accounts.push(AccountMeta::new(*mint_pubkey, false)); - accounts.push(AccountMeta::new(*account_pubkey, false)); - accounts.push(AccountMeta::new_readonly( - *owner_pubkey, - signer_pubkeys.is_empty(), - )); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates a `Burn` instruction. -pub fn burn( - token_program_id: &Pubkey, - account_pubkey: &Pubkey, - authority_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], - amount: u64, -) -> Result { - let data = TokenInstruction::Burn { amount }.pack()?; - - let mut accounts = Vec::with_capacity(2 + signer_pubkeys.len()); - accounts.push(AccountMeta::new(*account_pubkey, false)); - accounts.push(AccountMeta::new_readonly( - *authority_pubkey, - signer_pubkeys.is_empty(), - )); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Creates a `CloseAccount` instruction. -pub fn close_account( - token_program_id: &Pubkey, - account_pubkey: &Pubkey, - destination_pubkey: &Pubkey, - owner_pubkey: &Pubkey, - signer_pubkeys: &[&Pubkey], -) -> Result { - let data = TokenInstruction::CloseAccount.pack()?; - - let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); - accounts.push(AccountMeta::new(*account_pubkey, false)); - accounts.push(AccountMeta::new(*destination_pubkey, false)); - accounts.push(AccountMeta::new_readonly( - *owner_pubkey, - signer_pubkeys.is_empty(), - )); - for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); - } - - Ok(Instruction { - program_id: *token_program_id, - accounts, - data, - }) -} - -/// Utility function that checks index is between MIN_SIGNERS and MAX_SIGNERS -pub fn is_valid_signer_index(index: usize) -> bool { - !(index < MIN_SIGNERS || index > MAX_SIGNERS) -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_instruction_packing() { - let check = TokenInstruction::InitializeMint { - amount: 1, - decimals: 2, - }; - let packed = check.pack().unwrap(); - let expect = Vec::from([0u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]); - assert_eq!(packed, expect); - let unpacked = TokenInstruction::unpack(&expect).unwrap(); - assert_eq!(unpacked, check); - - let check = TokenInstruction::InitializeAccount; - let packed = check.pack().unwrap(); - let expect = Vec::from([1u8]); - assert_eq!(packed, expect); - let unpacked = TokenInstruction::unpack(&expect).unwrap(); - assert_eq!(unpacked, check); - - let check = TokenInstruction::InitializeMultisig { m: 1 }; - let packed = check.pack().unwrap(); - let expect = Vec::from([2u8, 1]); - assert_eq!(packed, expect); - let unpacked = TokenInstruction::unpack(&expect).unwrap(); - assert_eq!(unpacked, check); - - let check = TokenInstruction::Transfer { amount: 1 }; - let packed = check.pack().unwrap(); - let expect = Vec::from([3u8, 1, 0, 0, 0, 0, 0, 0, 0]); - assert_eq!(packed, expect); - let unpacked = TokenInstruction::unpack(&expect).unwrap(); - assert_eq!(unpacked, check); - - let check = TokenInstruction::Approve { amount: 1 }; - let packed = check.pack().unwrap(); - let expect = Vec::from([4u8, 1, 0, 0, 0, 0, 0, 0, 0]); - assert_eq!(packed, expect); - let unpacked = TokenInstruction::unpack(&expect).unwrap(); - assert_eq!(unpacked, check); - - let check = TokenInstruction::Revoke; - let packed = check.pack().unwrap(); - let expect = Vec::from([5u8]); - assert_eq!(packed, expect); - let unpacked = TokenInstruction::unpack(&expect).unwrap(); - assert_eq!(unpacked, check); - - let check = TokenInstruction::SetOwner; - let packed = check.pack().unwrap(); - let expect = Vec::from([6u8]); - assert_eq!(packed, expect); - let unpacked = TokenInstruction::unpack(&expect).unwrap(); - assert_eq!(unpacked, check); - - let check = TokenInstruction::MintTo { amount: 1 }; - let packed = check.pack().unwrap(); - let expect = Vec::from([7u8, 1, 0, 0, 0, 0, 0, 0, 0]); - assert_eq!(packed, expect); - let unpacked = TokenInstruction::unpack(&expect).unwrap(); - assert_eq!(unpacked, check); - - let check = TokenInstruction::Burn { amount: 1 }; - let packed = check.pack().unwrap(); - let expect = Vec::from([8u8, 1, 0, 0, 0, 0, 0, 0, 0]); - assert_eq!(packed, expect); - let unpacked = TokenInstruction::unpack(&expect).unwrap(); - assert_eq!(unpacked, check); - - let check = TokenInstruction::CloseAccount; - let packed = check.pack().unwrap(); - let expect = Vec::from([9u8]); - assert_eq!(packed, expect); - let unpacked = TokenInstruction::unpack(&expect).unwrap(); - assert_eq!(unpacked, check); - } -} diff --git a/program/src/lib.rs b/program/src/lib.rs deleted file mode 100644 index bc39c05..0000000 --- a/program/src/lib.rs +++ /dev/null @@ -1,28 +0,0 @@ -#![deny(missing_docs)] - -//! An ERC20-like Token program for the Solana blockchain - -pub mod entrypoint; -pub mod error; -pub mod instruction; -pub mod native_mint; -pub mod option; -pub mod processor; -pub mod state; - -// Export current solana-sdk types for downstream users who may also be building with a different -// solana-sdk version -pub use solana_sdk; - -/// Convert the UI representation of a token amount (using the decimals field defined in its mint) -/// to the raw amount -pub fn ui_amount_to_amount(ui_amount: f64, decimals: u8) -> u64 { - (ui_amount * 10_usize.pow(decimals as u32) as f64) as u64 -} - -/// Convert a raw amount to its UI representation (using the decimals field defined in its mint) -pub fn amount_to_ui_amount(amount: u64, decimals: u8) -> f64 { - amount as f64 / 10_usize.pow(decimals as u32) as f64 -} - -solana_sdk::declare_id!("TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o"); diff --git a/program/src/native_mint.rs b/program/src/native_mint.rs deleted file mode 100644 index 41307b3..0000000 --- a/program/src/native_mint.rs +++ /dev/null @@ -1,24 +0,0 @@ -//! The Mint that represents the native token - -/// There are 10^9 lamports in one SOL -pub const DECIMALS: u8 = 9; - -// The Mint for native SOL Token accounts -solana_sdk::declare_id!("So11111111111111111111111111111111111111111"); - -#[cfg(test)] -mod tests { - use super::*; - use solana_sdk::native_token::*; - - #[test] - fn test_decimals() { - assert!( - lamports_to_sol(42) - crate::amount_to_ui_amount(42, DECIMALS).abs() < f64::EPSILON - ); - assert_eq!( - sol_to_lamports(42.), - crate::ui_amount_to_amount(42., DECIMALS) - ); - } -} diff --git a/program/src/option.rs b/program/src/option.rs deleted file mode 100644 index 914bb51..0000000 --- a/program/src/option.rs +++ /dev/null @@ -1,965 +0,0 @@ -//! A C representation of Rust's `std::option::Option` used accross the FFI -//! boundary for Solana program interfaces -//! -//! This implementation mostly matches `std::option` except iterators since the iteration -//! trait requires returning `std::option::Option` - -use std::pin::Pin; -use std::{ - convert, hint, mem, - ops::{Deref, DerefMut}, -}; - -/// A C representation of Rust's `std::option::Option` -#[repr(C)] -#[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] -pub enum COption { - /// No value - None, - /// Some value `T` - Some(T), -} - -///////////////////////////////////////////////////////////////////////////// -// Type implementation -///////////////////////////////////////////////////////////////////////////// - -impl COption { - ///////////////////////////////////////////////////////////////////////// - // Querying the contained values - ///////////////////////////////////////////////////////////////////////// - - /// Returns `true` if the option is a [`COption::Some`] value. - /// - /// # Examples - /// - /// ```ignore - /// let x: COption = COption::Some(2); - /// assert_eq!(x.is_some(), true); - /// - /// let x: COption = COption::None; - /// assert_eq!(x.is_some(), false); - /// ``` - /// - /// [`COption::Some`]: #variant.COption::Some - #[must_use = "if you intended to assert that this has a value, consider `.unwrap()` instead"] - #[inline] - pub fn is_some(&self) -> bool { - match *self { - COption::Some(_) => true, - COption::None => false, - } - } - - /// Returns `true` if the option is a [`COption::None`] value. - /// - /// # Examples - /// - /// ```ignore - /// let x: COption = COption::Some(2); - /// assert_eq!(x.is_none(), false); - /// - /// let x: COption = COption::None; - /// assert_eq!(x.is_none(), true); - /// ``` - /// - /// [`COption::None`]: #variant.COption::None - #[must_use = "if you intended to assert that this doesn't have a value, consider \ - `.and_then(|| panic!(\"`COption` had a value when expected `COption::None`\"))` instead"] - #[inline] - pub fn is_none(&self) -> bool { - !self.is_some() - } - - /// Returns `true` if the option is a [`COption::Some`] value containing the given value. - /// - /// # Examples - /// - /// ```ignore - /// #![feature(option_result_contains)] - /// - /// let x: COption = COption::Some(2); - /// assert_eq!(x.contains(&2), true); - /// - /// let x: COption = COption::Some(3); - /// assert_eq!(x.contains(&2), false); - /// - /// let x: COption = COption::None; - /// assert_eq!(x.contains(&2), false); - /// ``` - #[must_use] - #[inline] - pub fn contains(&self, x: &U) -> bool - where - U: PartialEq, - { - match self { - COption::Some(y) => x == y, - COption::None => false, - } - } - - ///////////////////////////////////////////////////////////////////////// - // Adapter for working with references - ///////////////////////////////////////////////////////////////////////// - - /// Converts from `&COption` to `COption<&T>`. - /// - /// # Examples - /// - /// Converts an `COption<`[`String`]`>` into an `COption<`[`usize`]`>`, preserving the original. - /// The [`map`] method takes the `self` argument by value, consuming the original, - /// so this technique uses `as_ref` to first take an `COption` to a reference - /// to the value inside the original. - /// - /// [`map`]: enum.COption.html#method.map - /// [`String`]: ../../std/string/struct.String.html - /// [`usize`]: ../../std/primitive.usize.html - /// - /// ```ignore - /// let text: COption = COption::Some("Hello, world!".to_string()); - /// // First, cast `COption` to `COption<&String>` with `as_ref`, - /// // then consume *that* with `map`, leaving `text` on the stack. - /// let text_length: COption = text.as_ref().map(|s| s.len()); - /// println!("still can print text: {:?}", text); - /// ``` - #[inline] - pub fn as_ref(&self) -> COption<&T> { - match *self { - COption::Some(ref x) => COption::Some(x), - COption::None => COption::None, - } - } - - /// Converts from `&mut COption` to `COption<&mut T>`. - /// - /// # Examples - /// - /// ```ignore - /// let mut x = COption::Some(2); - /// match x.as_mut() { - /// COption::Some(v) => *v = 42, - /// COption::None => {}, - /// } - /// assert_eq!(x, COption::Some(42)); - /// ``` - #[inline] - pub fn as_mut(&mut self) -> COption<&mut T> { - match *self { - COption::Some(ref mut x) => COption::Some(x), - COption::None => COption::None, - } - } - - /// Converts from [`Pin`]`<&COption>` to `COption<`[`Pin`]`<&T>>`. - /// - /// [`Pin`]: ../pin/struct.Pin.html - #[inline] - #[allow(clippy::wrong_self_convention)] - pub fn as_pin_ref(self: Pin<&Self>) -> COption> { - unsafe { Pin::get_ref(self).as_ref().map(|x| Pin::new_unchecked(x)) } - } - - /// Converts from [`Pin`]`<&mut COption>` to `COption<`[`Pin`]`<&mut T>>`. - /// - /// [`Pin`]: ../pin/struct.Pin.html - #[inline] - #[allow(clippy::wrong_self_convention)] - pub fn as_pin_mut(self: Pin<&mut Self>) -> COption> { - unsafe { - Pin::get_unchecked_mut(self) - .as_mut() - .map(|x| Pin::new_unchecked(x)) - } - } - - ///////////////////////////////////////////////////////////////////////// - // Getting to contained values - ///////////////////////////////////////////////////////////////////////// - - /// Unwraps an option, yielding the content of a [`COption::Some`]. - /// - /// # Panics - /// - /// Panics if the value is a [`COption::None`] with a custom panic message provided by - /// `msg`. - /// - /// [`COption::Some`]: #variant.COption::Some - /// [`COption::None`]: #variant.COption::None - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some("value"); - /// assert_eq!(x.expect("the world is ending"), "value"); - /// ``` - /// - /// ```ignore{.should_panic} - /// let x: COption<&str> = COption::None; - /// x.expect("the world is ending"); // panics with `the world is ending` - /// ``` - #[inline] - pub fn expect(self, msg: &str) -> T { - match self { - COption::Some(val) => val, - COption::None => expect_failed(msg), - } - } - - /// Moves the value `v` out of the `COption` if it is [`COption::Some(v)`]. - /// - /// In general, because this function may panic, its use is discouraged. - /// Instead, prefer to use pattern matching and handle the [`COption::None`] - /// case explicitly. - /// - /// # Panics - /// - /// Panics if the self value equals [`COption::None`]. - /// - /// [`COption::Some(v)`]: #variant.COption::Some - /// [`COption::None`]: #variant.COption::None - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some("air"); - /// assert_eq!(x.unwrap(), "air"); - /// ``` - /// - /// ```ignore{.should_panic} - /// let x: COption<&str> = COption::None; - /// assert_eq!(x.unwrap(), "air"); // fails - /// ``` - #[inline] - pub fn unwrap(self) -> T { - match self { - COption::Some(val) => val, - COption::None => panic!("called `COption::unwrap()` on a `COption::None` value"), - } - } - - /// Returns the contained value or a default. - /// - /// Arguments passed to `unwrap_or` are eagerly evaluated; if you are passing - /// the result of a function call, it is recommended to use [`unwrap_or_else`], - /// which is lazily evaluated. - /// - /// [`unwrap_or_else`]: #method.unwrap_or_else - /// - /// # Examples - /// - /// ```ignore - /// assert_eq!(COption::Some("car").unwrap_or("bike"), "car"); - /// assert_eq!(COption::None.unwrap_or("bike"), "bike"); - /// ``` - #[inline] - pub fn unwrap_or(self, def: T) -> T { - match self { - COption::Some(x) => x, - COption::None => def, - } - } - - /// Returns the contained value or computes it from a closure. - /// - /// # Examples - /// - /// ```ignore - /// let k = 10; - /// assert_eq!(COption::Some(4).unwrap_or_else(|| 2 * k), 4); - /// assert_eq!(COption::None.unwrap_or_else(|| 2 * k), 20); - /// ``` - #[inline] - pub fn unwrap_or_else T>(self, f: F) -> T { - match self { - COption::Some(x) => x, - COption::None => f(), - } - } - - ///////////////////////////////////////////////////////////////////////// - // Transforming contained values - ///////////////////////////////////////////////////////////////////////// - - /// Maps an `COption` to `COption` by applying a function to a contained value. - /// - /// # Examples - /// - /// Converts an `COption<`[`String`]`>` into an `COption<`[`usize`]`>`, consuming the original: - /// - /// [`String`]: ../../std/string/struct.String.html - /// [`usize`]: ../../std/primitive.usize.html - /// - /// ```ignore - /// let maybe_some_string = COption::Some(String::from("Hello, World!")); - /// // `COption::map` takes self *by value*, consuming `maybe_some_string` - /// let maybe_some_len = maybe_some_string.map(|s| s.len()); - /// - /// assert_eq!(maybe_some_len, COption::Some(13)); - /// ``` - #[inline] - pub fn map U>(self, f: F) -> COption { - match self { - COption::Some(x) => COption::Some(f(x)), - COption::None => COption::None, - } - } - - /// Applies a function to the contained value (if any), - /// or returns the provided default (if not). - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some("foo"); - /// assert_eq!(x.map_or(42, |v| v.len()), 3); - /// - /// let x: COption<&str> = COption::None; - /// assert_eq!(x.map_or(42, |v| v.len()), 42); - /// ``` - #[inline] - pub fn map_or U>(self, default: U, f: F) -> U { - match self { - COption::Some(t) => f(t), - COption::None => default, - } - } - - /// Applies a function to the contained value (if any), - /// or computes a default (if not). - /// - /// # Examples - /// - /// ```ignore - /// let k = 21; - /// - /// let x = COption::Some("foo"); - /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3); - /// - /// let x: COption<&str> = COption::None; - /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42); - /// ``` - #[inline] - pub fn map_or_else U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U { - match self { - COption::Some(t) => f(t), - COption::None => default(), - } - } - - /// Transforms the `COption` into a [`Result`], mapping [`COption::Some(v)`] to - /// [`Ok(v)`] and [`COption::None`] to [`Err(err)`]. - /// - /// Arguments passed to `ok_or` are eagerly evaluated; if you are passing the - /// result of a function call, it is recommended to use [`ok_or_else`], which is - /// lazily evaluated. - /// - /// [`Result`]: ../../std/result/enum.Result.html - /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok - /// [`Err(err)`]: ../../std/result/enum.Result.html#variant.Err - /// [`COption::None`]: #variant.COption::None - /// [`COption::Some(v)`]: #variant.COption::Some - /// [`ok_or_else`]: #method.ok_or_else - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some("foo"); - /// assert_eq!(x.ok_or(0), Ok("foo")); - /// - /// let x: COption<&str> = COption::None; - /// assert_eq!(x.ok_or(0), Err(0)); - /// ``` - #[inline] - pub fn ok_or(self, err: E) -> Result { - match self { - COption::Some(v) => Ok(v), - COption::None => Err(err), - } - } - - /// Transforms the `COption` into a [`Result`], mapping [`COption::Some(v)`] to - /// [`Ok(v)`] and [`COption::None`] to [`Err(err())`]. - /// - /// [`Result`]: ../../std/result/enum.Result.html - /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok - /// [`Err(err())`]: ../../std/result/enum.Result.html#variant.Err - /// [`COption::None`]: #variant.COption::None - /// [`COption::Some(v)`]: #variant.COption::Some - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some("foo"); - /// assert_eq!(x.ok_or_else(|| 0), Ok("foo")); - /// - /// let x: COption<&str> = COption::None; - /// assert_eq!(x.ok_or_else(|| 0), Err(0)); - /// ``` - #[inline] - pub fn ok_or_else E>(self, err: F) -> Result { - match self { - COption::Some(v) => Ok(v), - COption::None => Err(err()), - } - } - - ///////////////////////////////////////////////////////////////////////// - // Boolean operations on the values, eager and lazy - ///////////////////////////////////////////////////////////////////////// - - /// Returns [`COption::None`] if the option is [`COption::None`], otherwise returns `optb`. - /// - /// [`COption::None`]: #variant.COption::None - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some(2); - /// let y: COption<&str> = COption::None; - /// assert_eq!(x.and(y), COption::None); - /// - /// let x: COption = COption::None; - /// let y = COption::Some("foo"); - /// assert_eq!(x.and(y), COption::None); - /// - /// let x = COption::Some(2); - /// let y = COption::Some("foo"); - /// assert_eq!(x.and(y), COption::Some("foo")); - /// - /// let x: COption = COption::None; - /// let y: COption<&str> = COption::None; - /// assert_eq!(x.and(y), COption::None); - /// ``` - #[inline] - pub fn and(self, optb: COption) -> COption { - match self { - COption::Some(_) => optb, - COption::None => COption::None, - } - } - - /// Returns [`COption::None`] if the option is [`COption::None`], otherwise calls `f` with the - /// wrapped value and returns the result. - /// - /// COption::Some languages call this operation flatmap. - /// - /// [`COption::None`]: #variant.COption::None - /// - /// # Examples - /// - /// ```ignore - /// fn sq(x: u32) -> COption { COption::Some(x * x) } - /// fn nope(_: u32) -> COption { COption::None } - /// - /// assert_eq!(COption::Some(2).and_then(sq).and_then(sq), COption::Some(16)); - /// assert_eq!(COption::Some(2).and_then(sq).and_then(nope), COption::None); - /// assert_eq!(COption::Some(2).and_then(nope).and_then(sq), COption::None); - /// assert_eq!(COption::None.and_then(sq).and_then(sq), COption::None); - /// ``` - #[inline] - pub fn and_then COption>(self, f: F) -> COption { - match self { - COption::Some(x) => f(x), - COption::None => COption::None, - } - } - - /// Returns [`COption::None`] if the option is [`COption::None`], otherwise calls `predicate` - /// with the wrapped value and returns: - /// - /// - [`COption::Some(t)`] if `predicate` returns `true` (where `t` is the wrapped - /// value), and - /// - [`COption::None`] if `predicate` returns `false`. - /// - /// This function works similar to [`Iterator::filter()`]. You can imagine - /// the `COption` being an iterator over one or zero elements. `filter()` - /// lets you decide which elements to keep. - /// - /// # Examples - /// - /// ```ignore - /// fn is_even(n: &i32) -> bool { - /// n % 2 == 0 - /// } - /// - /// assert_eq!(COption::None.filter(is_even), COption::None); - /// assert_eq!(COption::Some(3).filter(is_even), COption::None); - /// assert_eq!(COption::Some(4).filter(is_even), COption::Some(4)); - /// ``` - /// - /// [`COption::None`]: #variant.COption::None - /// [`COption::Some(t)`]: #variant.COption::Some - /// [`Iterator::filter()`]: ../../std/iter/trait.Iterator.html#method.filter - #[inline] - pub fn filter bool>(self, predicate: P) -> Self { - if let COption::Some(x) = self { - if predicate(&x) { - return COption::Some(x); - } - } - COption::None - } - - /// Returns the option if it contains a value, otherwise returns `optb`. - /// - /// Arguments passed to `or` are eagerly evaluated; if you are passing the - /// result of a function call, it is recommended to use [`or_else`], which is - /// lazily evaluated. - /// - /// [`or_else`]: #method.or_else - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some(2); - /// let y = COption::None; - /// assert_eq!(x.or(y), COption::Some(2)); - /// - /// let x = COption::None; - /// let y = COption::Some(100); - /// assert_eq!(x.or(y), COption::Some(100)); - /// - /// let x = COption::Some(2); - /// let y = COption::Some(100); - /// assert_eq!(x.or(y), COption::Some(2)); - /// - /// let x: COption = COption::None; - /// let y = COption::None; - /// assert_eq!(x.or(y), COption::None); - /// ``` - #[inline] - pub fn or(self, optb: COption) -> COption { - match self { - COption::Some(_) => self, - COption::None => optb, - } - } - - /// Returns the option if it contains a value, otherwise calls `f` and - /// returns the result. - /// - /// # Examples - /// - /// ```ignore - /// fn nobody() -> COption<&'static str> { COption::None } - /// fn vikings() -> COption<&'static str> { COption::Some("vikings") } - /// - /// assert_eq!(COption::Some("barbarians").or_else(vikings), COption::Some("barbarians")); - /// assert_eq!(COption::None.or_else(vikings), COption::Some("vikings")); - /// assert_eq!(COption::None.or_else(nobody), COption::None); - /// ``` - #[inline] - pub fn or_else COption>(self, f: F) -> COption { - match self { - COption::Some(_) => self, - COption::None => f(), - } - } - - /// Returns [`COption::Some`] if exactly one of `self`, `optb` is [`COption::Some`], otherwise returns [`COption::None`]. - /// - /// [`COption::Some`]: #variant.COption::Some - /// [`COption::None`]: #variant.COption::None - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some(2); - /// let y: COption = COption::None; - /// assert_eq!(x.xor(y), COption::Some(2)); - /// - /// let x: COption = COption::None; - /// let y = COption::Some(2); - /// assert_eq!(x.xor(y), COption::Some(2)); - /// - /// let x = COption::Some(2); - /// let y = COption::Some(2); - /// assert_eq!(x.xor(y), COption::None); - /// - /// let x: COption = COption::None; - /// let y: COption = COption::None; - /// assert_eq!(x.xor(y), COption::None); - /// ``` - #[inline] - pub fn xor(self, optb: COption) -> COption { - match (self, optb) { - (COption::Some(a), COption::None) => COption::Some(a), - (COption::None, COption::Some(b)) => COption::Some(b), - _ => COption::None, - } - } - - ///////////////////////////////////////////////////////////////////////// - // Entry-like operations to insert if COption::None and return a reference - ///////////////////////////////////////////////////////////////////////// - - /// Inserts `v` into the option if it is [`COption::None`], then - /// returns a mutable reference to the contained value. - /// - /// [`COption::None`]: #variant.COption::None - /// - /// # Examples - /// - /// ```ignore - /// let mut x = COption::None; - /// - /// { - /// let y: &mut u32 = x.get_or_insert(5); - /// assert_eq!(y, &5); - /// - /// *y = 7; - /// } - /// - /// assert_eq!(x, COption::Some(7)); - /// ``` - #[inline] - pub fn get_or_insert(&mut self, v: T) -> &mut T { - self.get_or_insert_with(|| v) - } - - /// Inserts a value computed from `f` into the option if it is [`COption::None`], then - /// returns a mutable reference to the contained value. - /// - /// [`COption::None`]: #variant.COption::None - /// - /// # Examples - /// - /// ```ignore - /// let mut x = COption::None; - /// - /// { - /// let y: &mut u32 = x.get_or_insert_with(|| 5); - /// assert_eq!(y, &5); - /// - /// *y = 7; - /// } - /// - /// assert_eq!(x, COption::Some(7)); - /// ``` - #[inline] - pub fn get_or_insert_with T>(&mut self, f: F) -> &mut T { - if let COption::None = *self { - *self = COption::Some(f()) - } - - match *self { - COption::Some(ref mut v) => v, - COption::None => unsafe { hint::unreachable_unchecked() }, - } - } - - ///////////////////////////////////////////////////////////////////////// - // Misc - ///////////////////////////////////////////////////////////////////////// - - /// Replaces the actual value in the option by the value given in parameter, - /// returning the old value if present, - /// leaving a [`COption::Some`] in its place without deinitializing either one. - /// - /// [`COption::Some`]: #variant.COption::Some - /// - /// # Examples - /// - /// ```ignore - /// let mut x = COption::Some(2); - /// let old = x.replace(5); - /// assert_eq!(x, COption::Some(5)); - /// assert_eq!(old, COption::Some(2)); - /// - /// let mut x = COption::None; - /// let old = x.replace(3); - /// assert_eq!(x, COption::Some(3)); - /// assert_eq!(old, COption::None); - /// ``` - #[inline] - pub fn replace(&mut self, value: T) -> COption { - mem::replace(self, COption::Some(value)) - } -} - -impl COption<&T> { - /// Maps an `COption<&T>` to an `COption` by copying the contents of the - /// option. - /// - /// # Examples - /// - /// ```ignore - /// let x = 12; - /// let opt_x = COption::Some(&x); - /// assert_eq!(opt_x, COption::Some(&12)); - /// let copied = opt_x.copied(); - /// assert_eq!(copied, COption::Some(12)); - /// ``` - pub fn copied(self) -> COption { - self.map(|&t| t) - } -} - -impl COption<&mut T> { - /// Maps an `COption<&mut T>` to an `COption` by copying the contents of the - /// option. - /// - /// # Examples - /// - /// ```ignore - /// let mut x = 12; - /// let opt_x = COption::Some(&mut x); - /// assert_eq!(opt_x, COption::Some(&mut 12)); - /// let copied = opt_x.copied(); - /// assert_eq!(copied, COption::Some(12)); - /// ``` - pub fn copied(self) -> COption { - self.map(|&mut t| t) - } -} - -impl COption<&T> { - /// Maps an `COption<&T>` to an `COption` by cloning the contents of the - /// option. - /// - /// # Examples - /// - /// ```ignore - /// let x = 12; - /// let opt_x = COption::Some(&x); - /// assert_eq!(opt_x, COption::Some(&12)); - /// let cloned = opt_x.cloned(); - /// assert_eq!(cloned, COption::Some(12)); - /// ``` - pub fn cloned(self) -> COption { - self.map(|t| t.clone()) - } -} - -impl COption<&mut T> { - /// Maps an `COption<&mut T>` to an `COption` by cloning the contents of the - /// option. - /// - /// # Examples - /// - /// ```ignore - /// let mut x = 12; - /// let opt_x = COption::Some(&mut x); - /// assert_eq!(opt_x, COption::Some(&mut 12)); - /// let cloned = opt_x.cloned(); - /// assert_eq!(cloned, COption::Some(12)); - /// ``` - pub fn cloned(self) -> COption { - self.map(|t| t.clone()) - } -} - -impl COption { - /// Returns the contained value or a default - /// - /// Consumes the `self` argument then, if [`COption::Some`], returns the contained - /// value, otherwise if [`COption::None`], returns the [default value] for that - /// type. - /// - /// # Examples - /// - /// Converts a string to an integer, turning poorly-formed strings - /// into 0 (the default value for integers). [`parse`] converts - /// a string to any other type that implements [`FromStr`], returning - /// [`COption::None`] on error. - /// - /// ```ignore - /// let good_year_from_input = "1909"; - /// let bad_year_from_input = "190blarg"; - /// let good_year = good_year_from_input.parse().ok().unwrap_or_default(); - /// let bad_year = bad_year_from_input.parse().ok().unwrap_or_default(); - /// - /// assert_eq!(1909, good_year); - /// assert_eq!(0, bad_year); - /// ``` - /// - /// [`COption::Some`]: #variant.COption::Some - /// [`COption::None`]: #variant.COption::None - /// [default value]: ../default/trait.Default.html#tymethod.default - /// [`parse`]: ../../std/primitive.str.html#method.parse - /// [`FromStr`]: ../../std/str/trait.FromStr.html - #[inline] - pub fn unwrap_or_default(self) -> T { - match self { - COption::Some(x) => x, - COption::None => Default::default(), - } - } -} - -impl COption { - /// Converts from `COption` (or `&COption`) to `COption<&T::Target>`. - /// - /// Leaves the original COption in-place, creating a new one with a reference - /// to the original one, additionally coercing the contents via [`Deref`]. - /// - /// [`Deref`]: ../../std/ops/trait.Deref.html - /// - /// # Examples - /// - /// ```ignore - /// #![feature(inner_deref)] - /// - /// let x: COption = COption::Some("hey".to_owned()); - /// assert_eq!(x.as_deref(), COption::Some("hey")); - /// - /// let x: COption = COption::None; - /// assert_eq!(x.as_deref(), COption::None); - /// ``` - pub fn as_deref(&self) -> COption<&T::Target> { - self.as_ref().map(|t| t.deref()) - } -} - -impl COption { - /// Converts from `COption` (or `&mut COption`) to `COption<&mut T::Target>`. - /// - /// Leaves the original `COption` in-place, creating a new one containing a mutable reference to - /// the inner type's `Deref::Target` type. - /// - /// # Examples - /// - /// ```ignore - /// #![feature(inner_deref)] - /// - /// let mut x: COption = COption::Some("hey".to_owned()); - /// assert_eq!(x.as_deref_mut().map(|x| { - /// x.make_ascii_uppercase(); - /// x - /// }), COption::Some("HEY".to_owned().as_mut_str())); - /// ``` - pub fn as_deref_mut(&mut self) -> COption<&mut T::Target> { - self.as_mut().map(|t| t.deref_mut()) - } -} - -impl COption> { - /// Transposes an `COption` of a [`Result`] into a [`Result`] of an `COption`. - /// - /// [`COption::None`] will be mapped to [`Ok`]`(`[`COption::None`]`)`. - /// [`COption::Some`]`(`[`Ok`]`(_))` and [`COption::Some`]`(`[`Err`]`(_))` will be mapped to - /// [`Ok`]`(`[`COption::Some`]`(_))` and [`Err`]`(_)`. - /// - /// [`COption::None`]: #variant.COption::None - /// [`Ok`]: ../../std/result/enum.Result.html#variant.Ok - /// [`COption::Some`]: #variant.COption::Some - /// [`Err`]: ../../std/result/enum.Result.html#variant.Err - /// - /// # Examples - /// - /// ```ignore - /// #[derive(Debug, Eq, PartialEq)] - /// struct COption::SomeErr; - /// - /// let x: Result, COption::SomeErr> = Ok(COption::Some(5)); - /// let y: COption> = COption::Some(Ok(5)); - /// assert_eq!(x, y.transpose()); - /// ``` - #[inline] - pub fn transpose(self) -> Result, E> { - match self { - COption::Some(Ok(x)) => Ok(COption::Some(x)), - COption::Some(Err(e)) => Err(e), - COption::None => Ok(COption::None), - } - } -} - -// This is a separate function to reduce the code size of .expect() itself. -#[inline(never)] -#[cold] -fn expect_failed(msg: &str) -> ! { - panic!("{}", msg) -} - -// // This is a separate function to reduce the code size of .expect_none() itself. -// #[inline(never)] -// #[cold] -// fn expect_none_failed(msg: &str, value: &dyn fmt::Debug) -> ! { -// panic!("{}: {:?}", msg, value) -// } - -///////////////////////////////////////////////////////////////////////////// -// Trait implementations -///////////////////////////////////////////////////////////////////////////// - -impl Clone for COption { - #[inline] - fn clone(&self) -> Self { - match self { - COption::Some(x) => COption::Some(x.clone()), - COption::None => COption::None, - } - } - - #[inline] - fn clone_from(&mut self, source: &Self) { - match (self, source) { - (COption::Some(to), COption::Some(from)) => to.clone_from(from), - (to, from) => *to = from.clone(), - } - } -} - -impl Default for COption { - /// Returns [`COption::None`][COption::COption::None]. - /// - /// # Examples - /// - /// ```ignore - /// let opt: COption = COption::default(); - /// assert!(opt.is_none()); - /// ``` - #[inline] - fn default() -> COption { - COption::None - } -} - -impl From for COption { - fn from(val: T) -> COption { - COption::Some(val) - } -} - -impl<'a, T> From<&'a COption> for COption<&'a T> { - fn from(o: &'a COption) -> COption<&'a T> { - o.as_ref() - } -} - -impl<'a, T> From<&'a mut COption> for COption<&'a mut T> { - fn from(o: &'a mut COption) -> COption<&'a mut T> { - o.as_mut() - } -} - -impl COption> { - /// Converts from `COption>` to `COption` - /// - /// # Examples - /// Basic usage: - /// ```ignore - /// #![feature(option_flattening)] - /// let x: COption> = COption::Some(COption::Some(6)); - /// assert_eq!(COption::Some(6), x.flatten()); - /// - /// let x: COption> = COption::Some(COption::None); - /// assert_eq!(COption::None, x.flatten()); - /// - /// let x: COption> = COption::None; - /// assert_eq!(COption::None, x.flatten()); - /// ``` - /// Flattening once only removes one level of nesting: - /// ```ignore - /// #![feature(option_flattening)] - /// let x: COption>> = COption::Some(COption::Some(COption::Some(6))); - /// assert_eq!(COption::Some(COption::Some(6)), x.flatten()); - /// assert_eq!(COption::Some(6), x.flatten().flatten()); - /// ``` - #[inline] - pub fn flatten(self) -> COption { - self.and_then(convert::identity) - } -} diff --git a/program/src/processor.rs b/program/src/processor.rs deleted file mode 100644 index 238c0d1..0000000 --- a/program/src/processor.rs +++ /dev/null @@ -1,2349 +0,0 @@ -//! Program state processor - -#![cfg(feature = "program")] - -use crate::{ - error::TokenError, - instruction::{is_valid_signer_index, TokenInstruction}, - option::COption, - state::{self, Account, Mint, Multisig}, -}; -use num_traits::FromPrimitive; -use solana_sdk::{ - account_info::{next_account_info, AccountInfo}, - decode_error::DecodeError, - entrypoint::ProgramResult, - info, - program_error::{PrintProgramError, ProgramError}, - pubkey::Pubkey, -}; -use std::mem::size_of; - -/// Program state handler. -pub struct Processor {} -impl Processor { - /// Processes an [InitializeMint](enum.TokenInstruction.html) instruction. - pub fn process_initialize_mint( - accounts: &[AccountInfo], - amount: u64, - decimals: u8, - ) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - let mint_info = next_account_info(account_info_iter)?; - - let mut mint_info_data = mint_info.data.borrow_mut(); - let mut mint: &mut Mint = state::unpack_unchecked(&mut mint_info_data)?; - if mint.is_initialized { - return Err(TokenError::AlreadyInUse.into()); - } - - let owner = if amount != 0 { - let dest_account_info = next_account_info(account_info_iter)?; - let mut dest_account_data = dest_account_info.data.borrow_mut(); - let mut dest_account: &mut Account = state::unpack(&mut dest_account_data)?; - - if mint_info.key != &dest_account.mint { - return Err(TokenError::MintMismatch.into()); - } - - dest_account.amount = amount; - - if let Ok(owner_info) = next_account_info(account_info_iter) { - COption::Some(*owner_info.key) - } else { - COption::None - } - } else if let Ok(owner_info) = next_account_info(account_info_iter) { - COption::Some(*owner_info.key) - } else { - return Err(TokenError::OwnerRequiredIfNoInitialSupply.into()); - }; - - mint.owner = owner; - mint.decimals = decimals; - mint.is_initialized = true; - - Ok(()) - } - - /// Processes an [InitializeAccount](enum.TokenInstruction.html) instruction. - pub fn process_initialize_account(accounts: &[AccountInfo]) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - let new_account_info = next_account_info(account_info_iter)?; - let mint_info = next_account_info(account_info_iter)?; - let owner_info = next_account_info(account_info_iter)?; - - let mut new_account_data = new_account_info.data.borrow_mut(); - let mut account: &mut Account = state::unpack_unchecked(&mut new_account_data)?; - if account.is_initialized { - return Err(TokenError::AlreadyInUse.into()); - } - - account.mint = *mint_info.key; - account.owner = *owner_info.key; - account.delegate = COption::None; - account.delegated_amount = 0; - account.is_initialized = true; - if *mint_info.key == crate::native_mint::id() { - account.is_native = true; - account.amount = new_account_info.lamports(); - } else { - account.is_native = false; - account.amount = 0; - }; - - Ok(()) - } - - /// Processes a [InitializeMultisig](enum.TokenInstruction.html) instruction. - pub fn process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - let multisig_info = next_account_info(account_info_iter)?; - let mut multisig_account_data = multisig_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack_unchecked(&mut multisig_account_data)?; - if multisig.is_initialized { - return Err(TokenError::AlreadyInUse.into()); - } - - let signer_infos = account_info_iter.as_slice(); - multisig.m = m; - multisig.n = signer_infos.len() as u8; - if !is_valid_signer_index(multisig.n as usize) { - return Err(TokenError::InvalidNumberOfProvidedSigners.into()); - } - if !is_valid_signer_index(multisig.m as usize) { - return Err(TokenError::InvalidNumberOfRequiredSigners.into()); - } - for (i, signer_info) in signer_infos.iter().enumerate() { - multisig.signers[i] = *signer_info.key; - } - multisig.is_initialized = true; - - Ok(()) - } - - /// Processes a [Transfer](enum.TokenInstruction.html) instruction. - pub fn process_transfer( - program_id: &Pubkey, - accounts: &[AccountInfo], - amount: u64, - ) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - let source_account_info = next_account_info(account_info_iter)?; - let dest_account_info = next_account_info(account_info_iter)?; - let authority_info = next_account_info(account_info_iter)?; - - let mut source_data = source_account_info.data.borrow_mut(); - let mut source_account: &mut Account = state::unpack(&mut source_data)?; - let mut dest_data = dest_account_info.data.borrow_mut(); - let mut dest_account: &mut Account = state::unpack(&mut dest_data)?; - - if source_account.amount < amount { - return Err(TokenError::InsufficientFunds.into()); - } - if source_account.mint != dest_account.mint { - return Err(TokenError::MintMismatch.into()); - } - - match source_account.delegate { - COption::Some(ref delegate) if authority_info.key == delegate => { - Self::validate_owner( - program_id, - delegate, - authority_info, - account_info_iter.as_slice(), - )?; - if source_account.delegated_amount < amount { - return Err(TokenError::InsufficientFunds.into()); - } - source_account.delegated_amount -= amount; - if source_account.delegated_amount == 0 { - source_account.delegate = COption::None; - } - } - _ => Self::validate_owner( - program_id, - &source_account.owner, - authority_info, - account_info_iter.as_slice(), - )?, - }; - - source_account.amount -= amount; - dest_account.amount = dest_account - .amount - .checked_add(amount) - .ok_or(TokenError::Overflow)?; - - if source_account.is_native { - **source_account_info.lamports.borrow_mut() -= amount; - **dest_account_info.lamports.borrow_mut() += amount; - } - - Ok(()) - } - - /// Processes an [Approve](enum.TokenInstruction.html) instruction. - pub fn process_approve( - program_id: &Pubkey, - accounts: &[AccountInfo], - amount: u64, - ) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - let source_account_info = next_account_info(account_info_iter)?; - - let mut source_data = source_account_info.data.borrow_mut(); - let mut source_account: &mut Account = state::unpack(&mut source_data)?; - let delegate_info = next_account_info(account_info_iter)?; - let owner_info = next_account_info(account_info_iter)?; - - Self::validate_owner( - program_id, - &source_account.owner, - owner_info, - account_info_iter.as_slice(), - )?; - - source_account.delegate = COption::Some(*delegate_info.key); - source_account.delegated_amount = amount; - - Ok(()) - } - - /// Processes an [Revoke](enum.TokenInstruction.html) instruction. - pub fn process_revoke(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - let source_account_info = next_account_info(account_info_iter)?; - - let mut source_data = source_account_info.data.borrow_mut(); - let mut source_account: &mut Account = state::unpack(&mut source_data)?; - let owner_info = next_account_info(account_info_iter)?; - - Self::validate_owner( - program_id, - &source_account.owner, - owner_info, - account_info_iter.as_slice(), - )?; - - source_account.delegate = COption::None; - source_account.delegated_amount = 0; - - Ok(()) - } - - /// Processes a [SetOwner](enum.TokenInstruction.html) instruction. - pub fn process_set_owner(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - let account_info = next_account_info(account_info_iter)?; - let new_owner_info = next_account_info(account_info_iter)?; - let authority_info = next_account_info(account_info_iter)?; - - if account_info.data_len() == size_of::() { - let mut account_data = account_info.data.borrow_mut(); - let mut account: &mut Account = state::unpack(&mut account_data)?; - - Self::validate_owner( - program_id, - &account.owner, - authority_info, - account_info_iter.as_slice(), - )?; - - account.owner = *new_owner_info.key; - } else if account_info.data_len() == size_of::() { - let mut account_data = account_info.data.borrow_mut(); - let mut mint: &mut Mint = state::unpack(&mut account_data)?; - - match mint.owner { - COption::Some(ref owner) => { - Self::validate_owner( - program_id, - owner, - authority_info, - account_info_iter.as_slice(), - )?; - } - COption::None => return Err(TokenError::FixedSupply.into()), - } - mint.owner = COption::Some(*new_owner_info.key); - } else { - return Err(ProgramError::InvalidArgument); - } - - Ok(()) - } - - /// Processes a [MintTo](enum.TokenInstruction.html) instruction. - pub fn process_mint_to( - program_id: &Pubkey, - accounts: &[AccountInfo], - amount: u64, - ) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - let mint_info = next_account_info(account_info_iter)?; - let dest_account_info = next_account_info(account_info_iter)?; - let owner_info = next_account_info(account_info_iter)?; - - let mut dest_account_data = dest_account_info.data.borrow_mut(); - let mut dest_account: &mut Account = state::unpack(&mut dest_account_data)?; - - if dest_account.is_native { - return Err(TokenError::NativeNotSupported.into()); - } - if mint_info.key != &dest_account.mint { - return Err(TokenError::MintMismatch.into()); - } - - let mut mint_info_data = mint_info.data.borrow_mut(); - let mint: &mut Mint = state::unpack(&mut mint_info_data)?; - - match mint.owner { - COption::Some(owner) => { - Self::validate_owner(program_id, &owner, owner_info, account_info_iter.as_slice())?; - } - COption::None => { - return Err(TokenError::FixedSupply.into()); - } - } - - dest_account.amount = dest_account - .amount - .checked_add(amount) - .ok_or(TokenError::Overflow)?; - - Ok(()) - } - - /// Processes a [Burn](enum.TokenInstruction.html) instruction. - pub fn process_burn( - program_id: &Pubkey, - accounts: &[AccountInfo], - amount: u64, - ) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - let source_account_info = next_account_info(account_info_iter)?; - let authority_info = next_account_info(account_info_iter)?; - - let mut source_data = source_account_info.data.borrow_mut(); - let source_account: &mut Account = state::unpack(&mut source_data)?; - - if source_account.is_native { - return Err(TokenError::NativeNotSupported.into()); - } - if source_account.amount < amount { - return Err(TokenError::InsufficientFunds.into()); - } - - match source_account.delegate { - COption::Some(ref delegate) if authority_info.key == delegate => { - Self::validate_owner( - program_id, - delegate, - authority_info, - account_info_iter.as_slice(), - )?; - - if source_account.delegated_amount < amount { - return Err(TokenError::InsufficientFunds.into()); - } - source_account.delegated_amount -= amount; - if source_account.delegated_amount == 0 { - source_account.delegate = COption::None; - } - } - _ => Self::validate_owner( - program_id, - &source_account.owner, - authority_info, - account_info_iter.as_slice(), - )?, - } - - source_account.amount -= amount; - - Ok(()) - } - - /// Processes a [CloseAccount](enum.TokenInstruction.html) instruction. - pub fn process_close_account(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - let source_account_info = next_account_info(account_info_iter)?; - let dest_account_info = next_account_info(account_info_iter)?; - let authority_info = next_account_info(account_info_iter)?; - - let mut source_data = source_account_info.data.borrow_mut(); - let source_account: &mut Account = state::unpack(&mut source_data)?; - - if !source_account.is_native { - return Err(TokenError::NonNativeNotSupported.into()); - } - - Self::validate_owner( - program_id, - &source_account.owner, - authority_info, - account_info_iter.as_slice(), - )?; - - **dest_account_info.lamports.borrow_mut() += source_account_info.lamports(); - **source_account_info.lamports.borrow_mut() = 0; - source_account.amount = 0; - - Ok(()) - } - - /// Processes an [Instruction](enum.Instruction.html). - pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult { - let instruction = TokenInstruction::unpack(input)?; - - match instruction { - TokenInstruction::InitializeMint { amount, decimals } => { - info!("Instruction: InitializeMint"); - Self::process_initialize_mint(accounts, amount, decimals) - } - TokenInstruction::InitializeAccount => { - info!("Instruction: InitializeAccount"); - Self::process_initialize_account(accounts) - } - TokenInstruction::InitializeMultisig { m } => { - info!("Instruction: InitializeMultisig"); - Self::process_initialize_multisig(accounts, m) - } - TokenInstruction::Transfer { amount } => { - info!("Instruction: Transfer"); - Self::process_transfer(program_id, accounts, amount) - } - TokenInstruction::Approve { amount } => { - info!("Instruction: Approve"); - Self::process_approve(program_id, accounts, amount) - } - TokenInstruction::Revoke => { - info!("Instruction: Revoke"); - Self::process_revoke(program_id, accounts) - } - TokenInstruction::SetOwner => { - info!("Instruction: SetOwner"); - Self::process_set_owner(program_id, accounts) - } - TokenInstruction::MintTo { amount } => { - info!("Instruction: MintTo"); - Self::process_mint_to(program_id, accounts, amount) - } - TokenInstruction::Burn { amount } => { - info!("Instruction: Burn"); - Self::process_burn(program_id, accounts, amount) - } - TokenInstruction::CloseAccount => { - info!("Instruction: CloseAccount"); - Self::process_close_account(program_id, accounts) - } - } - } - - /// Validates owner(s) are present - pub fn validate_owner( - program_id: &Pubkey, - expected_owner: &Pubkey, - owner_account_info: &AccountInfo, - signers: &[AccountInfo], - ) -> ProgramResult { - if expected_owner != owner_account_info.key { - return Err(TokenError::OwnerMismatch.into()); - } - if program_id == owner_account_info.owner - && owner_account_info.data_len() == std::mem::size_of::() - { - let mut owner_data = owner_account_info.data.borrow_mut(); - let multisig: &mut Multisig = state::unpack(&mut owner_data)?; - let mut num_signers = 0; - for signer in signers.iter() { - if multisig.signers[0..multisig.n as usize].contains(signer.key) { - if !signer.is_signer { - return Err(ProgramError::MissingRequiredSignature); - } - num_signers += 1; - } - } - if num_signers < multisig.m { - return Err(ProgramError::MissingRequiredSignature); - } - } else if !owner_account_info.is_signer { - return Err(ProgramError::MissingRequiredSignature); - } - Ok(()) - } -} - -impl PrintProgramError for TokenError { - fn print(&self) - where - E: 'static + std::error::Error + DecodeError + PrintProgramError + FromPrimitive, - { - match self { - TokenError::InsufficientFunds => info!("Error: insufficient funds"), - TokenError::MintMismatch => info!("Error: Account not associated with this Mint"), - TokenError::OwnerMismatch => info!("Error: owner does not match"), - TokenError::FixedSupply => info!("Error: the total supply of this token is fixed"), - TokenError::AlreadyInUse => info!("Error: account or token already in use"), - TokenError::OwnerRequiredIfNoInitialSupply => { - info!("Error: An owner is required if supply is zero") - } - TokenError::InvalidNumberOfProvidedSigners => { - info!("Error: Invalid number of provided signers") - } - TokenError::InvalidNumberOfRequiredSigners => { - info!("Error: Invalid number of required signers") - } - TokenError::UninitializedState => info!("Error: State is uninitialized"), - TokenError::NativeNotSupported => { - info!("Error: Instruction does not support native tokens") - } - TokenError::NonNativeNotSupported => { - info!("Error: Instruction does not support non-native tokens") - } - TokenError::InvalidInstruction => info!("Error: Invalid instruction"), - TokenError::Overflow => info!("Error: Operation overflowed"), - } - } -} - -// Pull in syscall stubs when building for non-BPF targets -#[cfg(not(target_arch = "bpf"))] -solana_sdk::program_stubs!(); - -#[cfg(test)] -mod tests { - use super::*; - use crate::instruction::{ - approve, burn, close_account, initialize_account, initialize_mint, initialize_multisig, - mint_to, revoke, set_owner, transfer, MAX_SIGNERS, - }; - use solana_sdk::{ - account::Account as SolanaAccount, account_info::create_is_signer_account_infos, - clock::Epoch, instruction::Instruction, - }; - - fn pubkey_rand() -> Pubkey { - Pubkey::new(&rand::random::<[u8; 32]>()) - } - - fn do_process_instruction( - instruction: Instruction, - accounts: Vec<&mut SolanaAccount>, - ) -> ProgramResult { - let mut meta = instruction - .accounts - .iter() - .zip(accounts) - .map(|(account_meta, account)| (&account_meta.pubkey, account_meta.is_signer, account)) - .collect::>(); - - let account_infos = create_is_signer_account_infos(&mut meta); - Processor::process(&instruction.program_id, &account_infos, &instruction.data) - } - - fn return_token_error_as_program_error() -> ProgramError { - TokenError::MintMismatch.into() - } - - #[test] - fn test_print_error() { - let error = return_token_error_as_program_error(); - error.print::(); - } - - #[test] - #[should_panic(expected = "Custom(1)")] - fn test_error_unwrap() { - Err::<(), ProgramError>(return_token_error_as_program_error()).unwrap(); - } - - #[test] - fn test_unique_account_sizes() { - assert_ne!(size_of::(), 0); - assert_ne!(size_of::(), size_of::()); - assert_ne!(size_of::(), size_of::()); - assert_ne!(size_of::(), 0); - assert_ne!(size_of::(), size_of::()); - assert_ne!(size_of::(), 0); - } - - #[test] - fn test_initialize_mint() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); - let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - let mint2_key = pubkey_rand(); - let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); - - // account not created - assert_eq!( - Err(TokenError::UninitializedState.into()), - do_process_instruction( - initialize_mint(&program_id, &mint_key, Some(&account_key), None, 1000, 2).unwrap(), - vec![&mut mint_account, &mut account_account] - ) - ); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut owner_account, &mut mint_account], - ) - .unwrap(); - - // create new mint - do_process_instruction( - initialize_mint(&program_id, &mint_key, Some(&account_key), None, 1000, 2).unwrap(), - vec![&mut mint_account, &mut account_account], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![&mut account2_account, &mut owner_account, &mut mint_account], - ) - .unwrap(); - - // mismatch account - assert_eq!( - Err(TokenError::MintMismatch.into()), - do_process_instruction( - initialize_mint(&program_id, &mint2_key, Some(&account2_key), None, 1000, 2) - .unwrap(), - vec![&mut mint2_account, &mut account2_account] - ) - ); - - // create twice - assert_eq!( - Err(TokenError::AlreadyInUse.into()), - do_process_instruction( - initialize_mint(&program_id, &mint_key, Some(&account_key), None, 1000, 2).unwrap(), - vec![&mut mint_account, &mut account_account] - ) - ); - } - - #[test] - fn test_initialize_mint_account() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create twice - assert_eq!( - Err(TokenError::AlreadyInUse.into()), - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - ); - } - - #[test] - fn test_transfer() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); - let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); - let account3_key = pubkey_rand(); - let mut account3_account = SolanaAccount::new(0, size_of::(), &program_id); - let delegate_key = pubkey_rand(); - let mut delegate_account = SolanaAccount::default(); - let mismatch_key = pubkey_rand(); - let mut mismatch_account = SolanaAccount::new(0, size_of::(), &program_id); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = pubkey_rand(); - let mut owner2_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - let mint2_key = pubkey_rand(); - let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![&mut account2_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), - vec![&mut account3_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create mismatch account - do_process_instruction( - initialize_account(&program_id, &mismatch_key, &mint2_key, &owner_key).unwrap(), - vec![ - &mut mismatch_account, - &mut mint2_account, - &mut owner_account, - ], - ) - .unwrap(); - - // create new mint - do_process_instruction( - initialize_mint(&program_id, &mint_key, Some(&account_key), None, 1000, 2).unwrap(), - vec![&mut mint_account, &mut account_account], - ) - .unwrap(); - - // missing signer - let mut instruction = transfer( - &program_id, - &account_key, - &account2_key, - &owner_key, - &[], - 1000, - ) - .unwrap(); - instruction.accounts[2].is_signer = false; - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - do_process_instruction( - instruction, - vec![ - &mut account_account, - &mut account2_account, - &mut owner_account, - ], - ) - ); - - // mismatch mint - assert_eq!( - Err(TokenError::MintMismatch.into()), - do_process_instruction( - transfer( - &program_id, - &account_key, - &mismatch_key, - &owner_key, - &[], - 1000 - ) - .unwrap(), - vec![ - &mut account_account, - &mut mismatch_account, - &mut owner_account, - ], - ) - ); - - // missing owner - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &owner2_key, - &[], - 1000 - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut owner2_account, - ], - ) - ); - - // transfer - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &owner_key, - &[], - 1000, - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut owner_account, - ], - ) - .unwrap(); - - // insufficient funds - assert_eq!( - Err(TokenError::InsufficientFunds.into()), - do_process_instruction( - transfer(&program_id, &account_key, &account2_key, &owner_key, &[], 1).unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut owner_account, - ], - ) - ); - - // transfer half back - do_process_instruction( - transfer( - &program_id, - &account2_key, - &account_key, - &owner_key, - &[], - 500, - ) - .unwrap(), - vec![ - &mut account2_account, - &mut account_account, - &mut owner_account, - ], - ) - .unwrap(); - - // transfer rest - do_process_instruction( - transfer( - &program_id, - &account2_key, - &account_key, - &owner_key, - &[], - 500, - ) - .unwrap(), - vec![ - &mut account2_account, - &mut account_account, - &mut owner_account, - ], - ) - .unwrap(); - - // insufficient funds - assert_eq!( - Err(TokenError::InsufficientFunds.into()), - do_process_instruction( - transfer(&program_id, &account2_key, &account_key, &owner_key, &[], 1).unwrap(), - vec![ - &mut account2_account, - &mut account_account, - &mut owner_account, - ], - ) - ); - - // approve delegate - do_process_instruction( - approve( - &program_id, - &account_key, - &delegate_key, - &owner_key, - &[], - 100, - ) - .unwrap(), - vec![ - &mut account_account, - &mut delegate_account, - &mut owner_account, - ], - ) - .unwrap(); - - // transfer via delegate - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &delegate_key, - &[], - 100, - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut delegate_account, - ], - ) - .unwrap(); - - // insufficient funds approved via delegate - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &delegate_key, - &[], - 100 - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut delegate_account, - ], - ) - ); - - // transfer rest - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &owner_key, - &[], - 900, - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut owner_account, - ], - ) - .unwrap(); - - // approve delegate - do_process_instruction( - approve( - &program_id, - &account_key, - &delegate_key, - &owner_key, - &[], - 100, - ) - .unwrap(), - vec![ - &mut account_account, - &mut delegate_account, - &mut owner_account, - ], - ) - .unwrap(); - - // insufficient funds in source account via delegate - assert_eq!( - Err(TokenError::InsufficientFunds.into()), - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &delegate_key, - &[], - 100 - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut delegate_account, - ], - ) - ); - } - - #[test] - fn test_mintable_token_with_zero_supply() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut owner_account, &mut mint_account], - ) - .unwrap(); - - // create mint-able token without owner - let mut instruction = - initialize_mint(&program_id, &mint_key, None, Some(&owner_key), 0, 2).unwrap(); - instruction.accounts.pop(); - assert_eq!( - Err(TokenError::OwnerRequiredIfNoInitialSupply.into()), - do_process_instruction(instruction, vec![&mut mint_account]) - ); - - // create mint-able token with zero supply - let amount = 0; - let decimals = 2; - do_process_instruction( - initialize_mint( - &program_id, - &mint_key, - None, - Some(&owner_key), - amount, - decimals, - ) - .unwrap(), - vec![&mut mint_account, &mut account_account], - ) - .unwrap(); - let mint: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); - assert_eq!( - *mint, - Mint { - owner: COption::Some(owner_key), - decimals, - is_initialized: true, - } - ); - - // mint to - do_process_instruction( - mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 42).unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], - ) - .unwrap(); - - let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); - let dest_account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(dest_account.amount, 42); - } - - #[test] - fn test_approve() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); - let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); - let delegate_key = pubkey_rand(); - let mut delegate_account = SolanaAccount::default(); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = pubkey_rand(); - let mut owner2_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut owner_account, &mut mint_account], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![&mut account2_account, &mut owner_account, &mut mint_account], - ) - .unwrap(); - - // create new mint - do_process_instruction( - initialize_mint(&program_id, &mint_key, Some(&account_key), None, 1000, 2).unwrap(), - vec![&mut mint_account, &mut account_account], - ) - .unwrap(); - - // missing signer - let mut instruction = approve( - &program_id, - &account_key, - &delegate_key, - &owner_key, - &[], - 100, - ) - .unwrap(); - instruction.accounts[2].is_signer = false; - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - do_process_instruction( - instruction, - vec![ - &mut account_account, - &mut delegate_account, - &mut owner_account, - ], - ) - ); - - // no owner - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - approve( - &program_id, - &account_key, - &delegate_key, - &owner2_key, - &[], - 100 - ) - .unwrap(), - vec![ - &mut account_account, - &mut delegate_account, - &mut owner2_account, - ], - ) - ); - - // approve delegate - do_process_instruction( - approve( - &program_id, - &account_key, - &delegate_key, - &owner_key, - &[], - 100, - ) - .unwrap(), - vec![ - &mut account_account, - &mut delegate_account, - &mut owner_account, - ], - ) - .unwrap(); - - // revoke delegate - do_process_instruction( - revoke(&program_id, &account_key, &owner_key, &[]).unwrap(), - vec![&mut account_account, &mut owner_account], - ) - .unwrap(); - } - - #[test] - fn test_set_owner() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); - let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = pubkey_rand(); - let mut owner2_account = SolanaAccount::default(); - let owner3_key = pubkey_rand(); - let mut owner3_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - let mint2_key = pubkey_rand(); - let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); - - // invalid account - assert_eq!( - Err(TokenError::UninitializedState.into()), - do_process_instruction( - set_owner(&program_id, &account_key, &owner2_key, &owner_key, &[]).unwrap(), - vec![ - &mut account_account, - &mut owner2_account, - &mut owner_account, - ], - ) - ); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account2_key, &mint2_key, &owner_key).unwrap(), - vec![ - &mut account2_account, - &mut mint2_account, - &mut owner_account, - ], - ) - .unwrap(); - - // missing owner - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - set_owner(&program_id, &account_key, &owner_key, &owner2_key, &[]).unwrap(), - vec![ - &mut account_account, - &mut owner_account, - &mut owner2_account, - ], - ) - ); - - // owner did not sign - let mut instruction = - set_owner(&program_id, &account_key, &owner2_key, &owner_key, &[]).unwrap(); - instruction.accounts[2].is_signer = false; - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - do_process_instruction( - instruction, - vec![ - &mut account_account, - &mut owner2_account, - &mut owner_account, - ], - ) - ); - - // set owner - do_process_instruction( - set_owner(&program_id, &account_key, &owner2_key, &owner_key, &[]).unwrap(), - vec![ - &mut account_account, - &mut owner2_account, - &mut owner_account, - ], - ) - .unwrap(); - - // create new mint with owner - do_process_instruction( - initialize_mint( - &program_id, - &mint_key, - Some(&account_key), - Some(&owner_key), - 1000, - 2, - ) - .unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], - ) - .unwrap(); - - // wrong account - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - set_owner(&program_id, &mint_key, &owner3_key, &owner2_key, &[]).unwrap(), - vec![&mut mint_account, &mut owner3_account, &mut owner2_account], - ) - ); - - // owner did not sign - let mut instruction = - set_owner(&program_id, &mint_key, &owner2_key, &owner_key, &[]).unwrap(); - instruction.accounts[2].is_signer = false; - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - do_process_instruction( - instruction, - vec![&mut mint_account, &mut owner2_account, &mut owner_account], - ) - ); - - // set owner - do_process_instruction( - set_owner(&program_id, &mint_key, &owner2_key, &owner_key, &[]).unwrap(), - vec![&mut mint_account, &mut owner2_account, &mut owner_account], - ) - .unwrap(); - - // create new mint without owner - do_process_instruction( - initialize_mint(&program_id, &mint2_key, Some(&account2_key), None, 1000, 2).unwrap(), - vec![&mut mint2_account, &mut account2_account], - ) - .unwrap(); - - // set owner for non-mint-able token - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - set_owner(&program_id, &mint2_key, &owner2_key, &owner_key, &[]).unwrap(), - vec![&mut mint_account, &mut owner2_account, &mut owner_account], - ) - ); - } - - #[test] - fn test_mint_to() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); - let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); - let account3_key = pubkey_rand(); - let mut account3_account = SolanaAccount::new(0, size_of::(), &program_id); - let mismatch_key = pubkey_rand(); - let mut mismatch_account = SolanaAccount::new(0, size_of::(), &program_id); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = pubkey_rand(); - let mut owner2_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - let mint2_key = pubkey_rand(); - let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); - let uninitialized_key = pubkey_rand(); - let mut uninitialized_account = SolanaAccount::new(0, size_of::(), &program_id); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![&mut account2_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), - vec![&mut account3_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create mismatch account - do_process_instruction( - initialize_account(&program_id, &mismatch_key, &mint2_key, &owner_key).unwrap(), - vec![ - &mut mismatch_account, - &mut mint2_account, - &mut owner_account, - ], - ) - .unwrap(); - - // create new mint with owner - do_process_instruction( - initialize_mint( - &program_id, - &mint_key, - Some(&account_key), - Some(&owner_key), - 1000, - 2, - ) - .unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], - ) - .unwrap(); - - // mint to - do_process_instruction( - mint_to(&program_id, &mint_key, &account2_key, &owner_key, &[], 42).unwrap(), - vec![&mut mint_account, &mut account2_account, &mut owner_account], - ) - .unwrap(); - - let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); - let dest_account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); - assert_eq!(dest_account.amount, 42); - - // missing signer - let mut instruction = - mint_to(&program_id, &mint_key, &account2_key, &owner_key, &[], 42).unwrap(); - instruction.accounts[2].is_signer = false; - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - do_process_instruction( - instruction, - vec![&mut mint_account, &mut account2_account, &mut owner_account], - ) - ); - - // mismatch account - assert_eq!( - Err(TokenError::MintMismatch.into()), - do_process_instruction( - mint_to(&program_id, &mint_key, &mismatch_key, &owner_key, &[], 42).unwrap(), - vec![&mut mint_account, &mut mismatch_account, &mut owner_account], - ) - ); - - // missing owner - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - mint_to(&program_id, &mint_key, &account2_key, &owner2_key, &[], 42).unwrap(), - vec![ - &mut mint_account, - &mut account2_account, - &mut owner2_account, - ], - ) - ); - - // uninitialized destination account - assert_eq!( - Err(TokenError::UninitializedState.into()), - do_process_instruction( - mint_to( - &program_id, - &mint_key, - &uninitialized_key, - &owner_key, - &[], - 42 - ) - .unwrap(), - vec![ - &mut mint_account, - &mut uninitialized_account, - &mut owner_account, - ], - ) - ); - } - - #[test] - fn test_burn() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); - let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); - let account3_key = pubkey_rand(); - let mut account3_account = SolanaAccount::new(0, size_of::(), &program_id); - let delegate_key = pubkey_rand(); - let mut delegate_account = SolanaAccount::default(); - let mismatch_key = pubkey_rand(); - let mut mismatch_account = SolanaAccount::new(0, size_of::(), &program_id); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = pubkey_rand(); - let mut owner2_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - let mint2_key = pubkey_rand(); - let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); - - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![&mut account2_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), - vec![&mut account3_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create mismatch account - do_process_instruction( - initialize_account(&program_id, &mismatch_key, &mint2_key, &owner_key).unwrap(), - vec![ - &mut mismatch_account, - &mut mint2_account, - &mut owner_account, - ], - ) - .unwrap(); - - // create new mint - do_process_instruction( - initialize_mint(&program_id, &mint_key, Some(&account_key), None, 1000, 2).unwrap(), - vec![&mut mint_account, &mut account_account], - ) - .unwrap(); - - // missing signer - let mut instruction = burn(&program_id, &account_key, &delegate_key, &[], 42).unwrap(); - instruction.accounts[1].is_signer = false; - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - instruction, - vec![&mut account_account, &mut delegate_account], - ) - ); - - // missing owner - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - burn(&program_id, &account_key, &owner2_key, &[], 42).unwrap(), - vec![&mut account_account, &mut owner2_account], - ) - ); - - // burn - do_process_instruction( - burn(&program_id, &account_key, &owner_key, &[], 42).unwrap(), - vec![&mut account_account, &mut owner_account], - ) - .unwrap(); - - let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(account.amount, 1000 - 42); - - // insufficient funds - assert_eq!( - Err(TokenError::InsufficientFunds.into()), - do_process_instruction( - burn(&program_id, &account_key, &owner_key, &[], 100_000_000).unwrap(), - vec![&mut account_account, &mut owner_account], - ) - ); - - // approve delegate - do_process_instruction( - approve( - &program_id, - &account_key, - &delegate_key, - &owner_key, - &[], - 84, - ) - .unwrap(), - vec![ - &mut account_account, - &mut delegate_account, - &mut owner_account, - ], - ) - .unwrap(); - - // not a delegate of source account - assert_eq!( - Err(TokenError::InsufficientFunds.into()), - do_process_instruction( - burn(&program_id, &account_key, &owner_key, &[], 100_000_000).unwrap(), - vec![&mut account_account, &mut owner_account], - ) - ); - - // burn via delegate - do_process_instruction( - burn(&program_id, &account_key, &delegate_key, &[], 84).unwrap(), - vec![&mut account_account, &mut delegate_account], - ) - .unwrap(); - - // match - let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(account.amount, 1000 - 42 - 84); - - // insufficient funds approved via delegate - assert_eq!( - Err(TokenError::OwnerMismatch.into()), - do_process_instruction( - burn(&program_id, &account_key, &delegate_key, &[], 100).unwrap(), - vec![&mut account_account, &mut delegate_account], - ) - ); - } - - #[test] - fn test_multisig() { - let program_id = pubkey_rand(); - let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - let account_key = pubkey_rand(); - let mut account = SolanaAccount::new(0, size_of::(), &program_id); - let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - let multisig_key = pubkey_rand(); - let mut multisig_account = SolanaAccount::new(0, size_of::(), &program_id); - let multisig_delegate_key = pubkey_rand(); - let mut multisig_delegate_account = - SolanaAccount::new(0, size_of::(), &program_id); - let signer_keys = vec![pubkey_rand(); MAX_SIGNERS]; - let signer_key_refs: Vec<&Pubkey> = signer_keys.iter().map(|key| key).collect(); - let mut signer_accounts = vec![SolanaAccount::new(0, 0, &program_id); MAX_SIGNERS]; - - // single signer - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(), - vec![ - &mut multisig_account, - &mut account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // multiple signer - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - initialize_multisig( - &program_id, - &multisig_delegate_key, - &signer_key_refs, - MAX_SIGNERS as u8, - ) - .unwrap(), - vec![ - &mut multisig_delegate_account, - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // create account with multisig owner - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &multisig_key).unwrap(), - vec![&mut account, &mut mint_account, &mut multisig_account], - ) - .unwrap(); - - // create another account with multisig owner - do_process_instruction( - initialize_account( - &program_id, - &account2_key, - &mint_key, - &multisig_delegate_key, - ) - .unwrap(), - vec![ - &mut account2_account, - &mut mint_account, - &mut multisig_account, - ], - ) - .unwrap(); - - // create new m int with multisig owner - do_process_instruction( - initialize_mint( - &program_id, - &mint_key, - Some(&account_key), - Some(&multisig_key), - 1000, - 2, - ) - .unwrap(), - vec![&mut mint_account, &mut account, &mut multisig_account], - ) - .unwrap(); - - // approve - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - approve( - &program_id, - &account_key, - &multisig_delegate_key, - &multisig_key, - &[&signer_keys[0]], - 100, - ) - .unwrap(), - vec![ - &mut account, - &mut multisig_delegate_account, - &mut multisig_account, - &mut account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // transfer - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &multisig_key, - &[&signer_keys[0]], - 42, - ) - .unwrap(), - vec![ - &mut account, - &mut account2_account, - &mut multisig_account, - &mut account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // transfer via delegate - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &multisig_delegate_key, - &signer_key_refs, - 42, - ) - .unwrap(), - vec![ - &mut account, - &mut account2_account, - &mut multisig_delegate_account, - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // mint to - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - mint_to( - &program_id, - &mint_key, - &account2_key, - &multisig_key, - &[&signer_keys[0]], - 42, - ) - .unwrap(), - vec![ - &mut mint_account, - &mut account2_account, - &mut multisig_account, - &mut account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // burn - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - burn( - &program_id, - &account_key, - &multisig_key, - &[&signer_keys[0]], - 42, - ) - .unwrap(), - vec![ - &mut account, - &mut multisig_account, - &mut account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // burn via delegate - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - burn( - &program_id, - &account_key, - &multisig_delegate_key, - &signer_key_refs, - 42, - ) - .unwrap(), - vec![ - &mut account, - &mut multisig_delegate_account, - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // do SetOwner on mint - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - set_owner( - &program_id, - &mint_key, - &owner_key, - &multisig_key, - &[&signer_keys[0]], - ) - .unwrap(), - vec![ - &mut mint_account, - &mut owner_account, - &mut multisig_account, - &mut account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - - // do SetOwner on account - let account_info_iter = &mut signer_accounts.iter_mut(); - do_process_instruction( - set_owner( - &program_id, - &account_key, - &owner_key, - &multisig_key, - &[&signer_keys[0]], - ) - .unwrap(), - vec![ - &mut account, - &mut owner_account, - &mut multisig_account, - &mut account_info_iter.next().unwrap(), - ], - ) - .unwrap(); - } - - #[test] - fn test_validate_owner() { - let program_id = pubkey_rand(); - let owner_key = pubkey_rand(); - let mut signer_keys = [Pubkey::default(); MAX_SIGNERS]; - for signer_key in signer_keys.iter_mut().take(MAX_SIGNERS) { - *signer_key = pubkey_rand(); - } - let mut signer_lamports = 0; - let mut signer_data = vec![]; - let mut signers = vec![ - AccountInfo::new( - &owner_key, - true, - false, - &mut signer_lamports, - &mut signer_data, - &program_id, - false, - Epoch::default(), - ); - MAX_SIGNERS + 1 - ]; - for (signer, key) in signers.iter_mut().zip(&signer_keys) { - signer.key = key; - } - let mut lamports = 0; - let mut data = vec![0; size_of::()]; - let mut multisig: &mut Multisig = state::unpack_unchecked(&mut data).unwrap(); - multisig.m = MAX_SIGNERS as u8; - multisig.n = MAX_SIGNERS as u8; - multisig.signers = signer_keys; - multisig.is_initialized = true; - let owner_account_info = AccountInfo::new( - &owner_key, - false, - false, - &mut lamports, - &mut data, - &program_id, - false, - Epoch::default(), - ); - - // full 11 of 11 - Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap(); - - // 1 of 11 - { - let mut data_ref_mut = owner_account_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); - multisig.m = 1; - } - Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap(); - - // 2:1 - { - let mut data_ref_mut = owner_account_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); - multisig.m = 2; - multisig.n = 1; - } - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers) - ); - - // 0:11 - { - let mut data_ref_mut = owner_account_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); - multisig.m = 0; - multisig.n = 11; - } - Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap(); - - // 2:11 but 0 provided - { - let mut data_ref_mut = owner_account_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); - multisig.m = 2; - multisig.n = 11; - } - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &[]) - ); - // 2:11 but 1 provided - { - let mut data_ref_mut = owner_account_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); - multisig.m = 2; - multisig.n = 11; - } - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers[0..1]) - ); - - // 2:11, 2 from middle provided - { - let mut data_ref_mut = owner_account_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); - multisig.m = 2; - multisig.n = 11; - } - Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers[5..7]) - .unwrap(); - - // 11:11, one is not a signer - { - let mut data_ref_mut = owner_account_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); - multisig.m = 2; - multisig.n = 11; - } - signers[5].is_signer = false; - assert_eq!( - Err(ProgramError::MissingRequiredSignature), - Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers) - ); - signers[5].is_signer = true; - } - - #[test] - fn test_close_account() { - let program_id = pubkey_rand(); - let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(42, size_of::(), &program_id); - let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(2, size_of::(), &program_id); - let account3_key = pubkey_rand(); - let mut account3_account = SolanaAccount::new(2, size_of::(), &program_id); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = pubkey_rand(); - let mut owner2_account = SolanaAccount::default(); - - // uninitialized - assert_eq!( - Err(TokenError::UninitializedState.into()), - do_process_instruction( - close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(), - vec![ - &mut account_account, - &mut account3_account, - &mut owner2_account, - ], - ) - ); - - // initialize non-native account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // initialize native account - do_process_instruction( - initialize_account( - &program_id, - &account2_key, - &crate::native_mint::id(), - &owner_key, - ) - .unwrap(), - vec![&mut account2_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); - assert!(account.is_native); - assert_eq!(account.amount, 2); - - // close non-native account - assert_eq!( - Err(TokenError::NonNativeNotSupported.into()), - do_process_instruction( - close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(), - vec![ - &mut account_account, - &mut account3_account, - &mut owner_account, - ], - ) - ); - assert_eq!(account_account.lamports, 42); - - // close native account - do_process_instruction( - close_account(&program_id, &account2_key, &account3_key, &owner_key, &[]).unwrap(), - vec![ - &mut account2_account, - &mut account3_account, - &mut owner_account, - ], - ) - .unwrap(); - let account: &mut Account = state::unpack_unchecked(&mut account2_account.data).unwrap(); - assert!(account.is_native); - assert_eq!(account.amount, 0); - assert_eq!(account3_account.lamports, 4); - } - - #[test] - fn test_native_token() { - let program_id = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(42, size_of::(), &program_id); - let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(2, size_of::(), &program_id); - let account3_key = pubkey_rand(); - let mut account3_account = SolanaAccount::new(2, 0, &program_id); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - - // initialize native account - do_process_instruction( - initialize_account( - &program_id, - &account_key, - &crate::native_mint::id(), - &owner_key, - ) - .unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert!(account.is_native); - assert_eq!(account.amount, 42); - - // initialize native account - do_process_instruction( - initialize_account( - &program_id, - &account2_key, - &crate::native_mint::id(), - &owner_key, - ) - .unwrap(), - vec![&mut account2_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); - assert!(account.is_native); - assert_eq!(account.amount, 2); - - // mint_to unsupported - assert_eq!( - Err(TokenError::NativeNotSupported.into()), - do_process_instruction( - mint_to( - &program_id, - &crate::native_mint::id(), - &account_key, - &owner_key, - &[], - 42 - ) - .unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], - ) - ); - - // burn unsupported - assert_eq!( - Err(TokenError::NativeNotSupported.into()), - do_process_instruction( - burn(&program_id, &account_key, &owner_key, &[], 42).unwrap(), - vec![&mut account_account, &mut owner_account], - ) - ); - - // initialize native account - do_process_instruction( - transfer( - &program_id, - &account_key, - &account2_key, - &owner_key, - &[], - 40, - ) - .unwrap(), - vec![ - &mut account_account, - &mut account2_account, - &mut owner_account, - ], - ) - .unwrap(); - - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert!(account.is_native); - assert_eq!(account_account.lamports, 2); - assert_eq!(account.amount, 2); - let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); - assert!(account.is_native); - assert_eq!(account2_account.lamports, 42); - assert_eq!(account.amount, 42); - - // close native account - do_process_instruction( - close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(), - vec![ - &mut account_account, - &mut account3_account, - &mut owner_account, - ], - ) - .unwrap(); - let account: &mut Account = state::unpack_unchecked(&mut account_account.data).unwrap(); - assert!(account.is_native); - assert_eq!(account_account.lamports, 0); - assert_eq!(account.amount, 0); - assert_eq!(account3_account.lamports, 4); - } - - #[test] - fn test_overflow() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); - let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); - let owner_key = pubkey_rand(); - let mut owner_account = SolanaAccount::default(); - let owner2_key = pubkey_rand(); - let mut owner2_account = SolanaAccount::default(); - let mint_owner_key = pubkey_rand(); - let mut mint_owner_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); - - // create victim account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], - ) - .unwrap(); - - // create another account - do_process_instruction( - initialize_account(&program_id, &account2_key, &mint_key, &owner2_key).unwrap(), - vec![ - &mut account2_account, - &mut mint_account, - &mut owner2_account, - ], - ) - .unwrap(); - - // create new mint with owner - do_process_instruction( - initialize_mint(&program_id, &mint_key, None, Some(&mint_owner_key), 0, 2).unwrap(), - vec![&mut mint_account, &mut mint_owner_account], - ) - .unwrap(); - - // mint the max to attacker - do_process_instruction( - mint_to( - &program_id, - &mint_key, - &account2_key, - &mint_owner_key, - &[], - 42, - ) - .unwrap(), - vec![ - &mut mint_account, - &mut account2_account, - &mut mint_owner_account, - ], - ) - .unwrap(); - let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); - assert_eq!(account.amount, 42); - - // mint the max to victum - do_process_instruction( - mint_to( - &program_id, - &mint_key, - &account_key, - &mint_owner_key, - &[], - u64::MAX, - ) - .unwrap(), - vec![ - &mut mint_account, - &mut account_account, - &mut mint_owner_account, - ], - ) - .unwrap(); - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(account.amount, u64::MAX); - - // mint one more - assert_eq!( - Err(TokenError::Overflow.into()), - do_process_instruction( - mint_to( - &program_id, - &mint_key, - &account_key, - &mint_owner_key, - &[], - 1, - ) - .unwrap(), - vec![ - &mut mint_account, - &mut account_account, - &mut mint_owner_account, - ], - ) - ); - - // mint back to large amount - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - account.amount = 0; - do_process_instruction( - mint_to( - &program_id, - &mint_key, - &account_key, - &mint_owner_key, - &[], - u64::MAX, - ) - .unwrap(), - vec![ - &mut mint_account, - &mut account_account, - &mut mint_owner_account, - ], - ) - .unwrap(); - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(account.amount, u64::MAX); - - // transfer to burn victim - assert_eq!( - Err(TokenError::Overflow.into()), - do_process_instruction( - transfer( - &program_id, - &account2_key, - &account_key, - &owner2_key, - &[], - 1, - ) - .unwrap(), - vec![ - &mut account2_account, - &mut account_account, - &mut owner2_account, - ], - ) - ); - } -} diff --git a/program/src/state.rs b/program/src/state.rs deleted file mode 100644 index 3b63f4f..0000000 --- a/program/src/state.rs +++ /dev/null @@ -1,92 +0,0 @@ -//! State transition types - -use crate::{error::TokenError, instruction::MAX_SIGNERS, option::COption}; -use solana_sdk::{program_error::ProgramError, pubkey::Pubkey}; -use std::mem::size_of; - -/// Mint data. -#[repr(C)] -#[derive(Clone, Copy, Debug, Default, PartialEq)] -pub struct Mint { - /// Optional owner, used to mint new tokens. The owner may only - /// be provided during mint creation. If no owner is present then the mint - /// has a fixed supply and no further tokens may be minted. - pub owner: COption, - /// Number of base 10 digits to the right of the decimal place. - pub decimals: u8, - /// Is `true` if this structure has been initialized - pub is_initialized: bool, -} -impl IsInitialized for Mint { - fn is_initialized(&self) -> bool { - self.is_initialized - } -} - -/// Account data. -#[repr(C)] -#[derive(Clone, Copy, Debug, Default, PartialEq)] -pub struct Account { - /// The mint associated with this account - pub mint: Pubkey, - /// The owner of this account. - pub owner: Pubkey, - /// The amount of tokens this account holds. - pub amount: u64, - /// If `delegate` is `Some` then `delegated_amount` represents - /// the amount authorized by the delegate - pub delegate: COption, - /// Is `true` if this structure has been initialized - pub is_initialized: bool, - /// Is this a native token - pub is_native: bool, - /// The amount delegated - pub delegated_amount: u64, -} -impl IsInitialized for Account { - fn is_initialized(&self) -> bool { - self.is_initialized - } -} - -/// Multisignature data. -#[repr(C)] -#[derive(Clone, Copy, Debug, Default, PartialEq)] -pub struct Multisig { - /// Number of signers required - pub m: u8, - /// Number of valid signers - pub n: u8, - /// Is `true` if this structure has been initialized - pub is_initialized: bool, - /// Signer public keys - pub signers: [Pubkey; MAX_SIGNERS], -} -impl IsInitialized for Multisig { - fn is_initialized(&self) -> bool { - self.is_initialized - } -} - -/// Check is a token state is initialized -pub trait IsInitialized { - /// Is initialized - fn is_initialized(&self) -> bool; -} - -/// Unpacks a token state from a bytes buffer while assuring that the state is initialized. -pub fn unpack(input: &mut [u8]) -> Result<&mut T, ProgramError> { - let mut_ref: &mut T = unpack_unchecked(input)?; - if !mut_ref.is_initialized() { - return Err(TokenError::UninitializedState.into()); - } - Ok(mut_ref) -} -/// Unpacks a token state from a bytes buffer without checking that the state is initialized. -pub fn unpack_unchecked(input: &mut [u8]) -> Result<&mut T, ProgramError> { - if input.len() != size_of::() { - return Err(ProgramError::InvalidAccountData); - } - #[allow(clippy::cast_ptr_alignment)] - Ok(unsafe { &mut *(&mut input[0] as *mut u8 as *mut T) }) -} From 1f722cb1bda7425baa8dc724e1f9a28fa8bdb964 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Wed, 26 Aug 2020 22:08:40 -0700 Subject: [PATCH 021/248] Relocate program2 into program --- program/Cargo.toml | 34 + program/Xargo.toml | 2 + program/build.rs | 65 + program/inc/token.h | 359 +++++ program/program-id.md | 1 + program/src/entrypoint.rs | 24 + program/src/error.rs | 71 + program/src/instruction.rs | 915 +++++++++++ program/src/lib.rs | 28 + program/src/native_mint.rs | 24 + program/src/option.rs | 1112 +++++++++++++ program/src/processor.rs | 3128 ++++++++++++++++++++++++++++++++++++ program/src/state.rs | 122 ++ 13 files changed, 5885 insertions(+) create mode 100644 program/Cargo.toml create mode 100644 program/Xargo.toml create mode 100644 program/build.rs create mode 100644 program/inc/token.h create mode 100644 program/program-id.md create mode 100644 program/src/entrypoint.rs create mode 100644 program/src/error.rs create mode 100644 program/src/instruction.rs create mode 100644 program/src/lib.rs create mode 100644 program/src/native_mint.rs create mode 100644 program/src/option.rs create mode 100644 program/src/processor.rs create mode 100644 program/src/state.rs diff --git a/program/Cargo.toml b/program/Cargo.toml new file mode 100644 index 0000000..e112088 --- /dev/null +++ b/program/Cargo.toml @@ -0,0 +1,34 @@ + +# Note: This crate must be built using do.sh + +[package] +name = "spl-token" +version = "1.1.0" +description = "Solana Program Library Token" +authors = ["Solana Maintainers "] +repository = "https://github.com/solana-labs/solana-program-library" +license = "Apache-2.0" +edition = "2018" +exclude = ["js/**"] + +[features] +no-entrypoint = [] +skip-no-mangle = ["solana-sdk/skip-no-mangle"] +program = ["solana-sdk/program"] +default = ["solana-sdk/default"] + +[dependencies] +num-derive = "0.3" +num-traits = "0.2" +remove_dir_all = "=0.5.0" +solana-sdk = { version = "1.3.4", default-features = false, optional = true } +thiserror = "1.0" + +[dev-dependencies] +rand = { version = "0.7.0"} + +[build-dependencies] +cbindgen = "=0.14.2" + +[lib] +crate-type = ["cdylib", "lib"] diff --git a/program/Xargo.toml b/program/Xargo.toml new file mode 100644 index 0000000..1744f09 --- /dev/null +++ b/program/Xargo.toml @@ -0,0 +1,2 @@ +[target.bpfel-unknown-unknown.dependencies.std] +features = [] \ No newline at end of file diff --git a/program/build.rs b/program/build.rs new file mode 100644 index 0000000..4862075 --- /dev/null +++ b/program/build.rs @@ -0,0 +1,65 @@ +extern crate cbindgen; + +use std::env; + +fn main() { + println!("cargo:rerun-if-env-changed=SPL_CBINDGEN"); + println!("cargo:rerun-if-changed=inc/token.h"); + if std::path::Path::new("inc/token.h").exists() && env::var("SPL_CBINDGEN").is_err() { + return; + } + + println!("cargo:warning=Generating inc/token.h"); + let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let config = cbindgen::Config { + header: Some("/* Autogenerated SPL Token program C Bindings */".to_string()), + after_includes: Some(format!( + "{}{}{}", + format!( + "\n#define TOKEN_MAJOR_VERSION {}", + env!("CARGO_PKG_VERSION_MAJOR") + ), + format!( + "\n#define TOKEN_MINOR_VERSION {}", + env!("CARGO_PKG_VERSION_MINOR") + ), + format!( + "\n#define TOKEN_PATCH_VERSION {}", + env!("CARGO_PKG_VERSION_PATCH") + ) + )), + language: cbindgen::Language::C, + line_length: 80, + style: cbindgen::Style::Both, + tab_width: 4, + cpp_compat: true, + pragma_once: true, + enumeration: cbindgen::EnumConfig { + prefix_with_name: true, + ..cbindgen::EnumConfig::default() + }, + export: cbindgen::ExportConfig { + prefix: Some("Token_".to_string()), + include: vec![ + "TokenInstruction".to_string(), + "Mint".to_string(), + "Account".to_string(), + "Multisig".to_string(), + ], + exclude: vec!["DECIMALS".to_string()], + ..cbindgen::ExportConfig::default() + }, + parse: cbindgen::ParseConfig { + parse_deps: true, + include: Some(vec!["solana-sdk".to_string()]), + ..cbindgen::ParseConfig::default() + }, + ..cbindgen::Config::default() + }; + cbindgen::Builder::new() + .with_crate(crate_dir) + .with_config(config) + .generate() + .unwrap() + .write_to_file("inc/token.h"); +} diff --git a/program/inc/token.h b/program/inc/token.h new file mode 100644 index 0000000..38222c0 --- /dev/null +++ b/program/inc/token.h @@ -0,0 +1,359 @@ +/* Autogenerated SPL Token program C Bindings */ + +#pragma once + +#include +#include +#include +#include + +#define TOKEN_MAJOR_VERSION 1 +#define TOKEN_MINOR_VERSION 1 +#define TOKEN_PATCH_VERSION 0 + +/** + * Maximum number of multisignature signers (max N) + */ +#define Token_MAX_SIGNERS 11 + +/** + * Minimum number of multisignature signers (min N) + */ +#define Token_MIN_SIGNERS 1 + +/** + * Instructions supported by the token program. + */ +typedef enum Token_TokenInstruction_Tag { + /** + * Initializes a new mint and optionally deposits all the newly minted tokens in an account. + * + * The `InitializeMint` instruction requires no signers and MUST be included within + * the same Transaction as the system program's `CreateInstruction` that creates the account + * being initialized. Otherwise another party can acquire ownership of the uninitialized account. + * + * Accounts expected by this instruction: + * + * 0. `[writable]` The mint to initialize. + * 1. + * * If supply is non-zero: `[writable]` The account to hold all the newly minted tokens. + * * If supply is zero: `[]` The owner/multisignature of the mint. + * 2. `[]` (optional) The owner/multisignature of the mint if supply is non-zero, if + * present then further minting is supported. + * + */ + Token_TokenInstruction_InitializeMint, + /** + * Initializes a new account to hold tokens. If this account is associated with the native mint + * then the token balance of the initialized account will be equal to the amount of SOL in the account. + * + * The `InitializeAccount` instruction requires no signers and MUST be included within + * the same Transaction as the system program's `CreateInstruction` that creates the account + * being initialized. Otherwise another party can acquire ownership of the uninitialized account. + * + * Accounts expected by this instruction: + * + * 0. `[writable]` The account to initialize. + * 1. `[]` The mint this account will be associated with. + * 2. `[]` The new account's owner/multisignature. + */ + Token_TokenInstruction_InitializeAccount, + /** + * Initializes a multisignature account with N provided signers. + * + * Multisignature accounts can used in place of any single owner/delegate accounts in any + * token instruction that require an owner/delegate to be present. The variant field represents the + * number of signers (M) required to validate this multisignature account. + * + * The `InitializeMultisig` instruction requires no signers and MUST be included within + * the same Transaction as the system program's `CreateInstruction` that creates the account + * being initialized. Otherwise another party can acquire ownership of the uninitialized account. + * + * Accounts expected by this instruction: + * + * 0. `[writable]` The multisignature account to initialize. + * 1. ..1+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. + */ + Token_TokenInstruction_InitializeMultisig, + /** + * Transfers tokens from one account to another either directly or via a delegate. If this + * account is associated with the native mint then equal amounts of SOL and Tokens will be + * transferred to the destination account. + * + * Accounts expected by this instruction: + * + * * Single owner/delegate + * 0. `[writable]` The source account. + * 1. `[writable]` The destination account. + * 2. '[signer]' The source account's owner/delegate. + * + * * Multisignature owner/delegate + * 0. `[writable]` The source account. + * 1. `[writable]` The destination account. + * 2. '[]' The source account's multisignature owner/delegate. + * 3. ..3+M '[signer]' M signer accounts. + */ + Token_TokenInstruction_Transfer, + /** + * Approves a delegate. A delegate is given the authority over + * tokens on behalf of the source account's owner. + * Accounts expected by this instruction: + * + * * Single owner + * 0. `[writable]` The source account. + * 1. `[]` The delegate. + * 2. `[signer]` The source account owner. + * + * * Multisignature owner + * 0. `[writable]` The source account. + * 1. `[]` The delegate. + * 2. '[]' The source account's multisignature owner. + * 3. ..3+M '[signer]' M signer accounts + */ + Token_TokenInstruction_Approve, + /** + * Revokes the delegate's authority. + * + * Accounts expected by this instruction: + * + * * Single owner + * 0. `[writable]` The source account. + * 1. `[signer]` The source account owner. + * + * * Multisignature owner + * 0. `[writable]` The source account. + * 1. '[]' The source account's multisignature owner. + * 2. ..2+M '[signer]' M signer accounts + */ + Token_TokenInstruction_Revoke, + /** + * Sets a new owner of a mint or account. + * + * Accounts expected by this instruction: + * + * * Single owner + * 0. `[writable]` The mint or account to change the owner of. + * 1. `[]` The new owner/delegate/multisignature. + * 2. `[signer]` The owner of the mint or account. + * + * * Multisignature owner + * 0. `[writable]` The mint or account to change the owner of. + * 1. `[]` The new owner/delegate/multisignature. + * 2. `[]` The mint's or account's multisignature owner. + * 3. ..3+M '[signer]' M signer accounts + */ + Token_TokenInstruction_SetOwner, + /** + * Mints new tokens to an account. The native mint does not support minting. + * + * Accounts expected by this instruction: + * + * * Single owner + * 0. `[writable]` The mint. + * 1. `[writable]` The account to mint tokens to. + * 2. `[signer]` The mint's owner. + * + * * Multisignature owner + * 0. `[writable]` The mint. + * 1. `[writable]` The account to mint tokens to. + * 2. `[]` The mint's multisignature owner. + * 3. ..3+M '[signer]' M signer accounts. + */ + Token_TokenInstruction_MintTo, + /** + * Burns tokens by removing them from an account. `Burn` does not support accounts + * associated with the native mint, use `CloseAccount` instead. + * + * Accounts expected by this instruction: + * + * * Single owner/delegate + * 0. `[writable]` The account to burn from. + * 1. `[signer]` The account's owner/delegate. + * + * * Multisignature owner/delegate + * 0. `[writable]` The account to burn from. + * 1. `[]` The account's multisignature owner/delegate. + * 2. ..2+M '[signer]' M signer accounts. + */ + Token_TokenInstruction_Burn, + /** + * Close an account by transferring all its SOL to the destination account. + * Non-native accounts may only be closed if its token amount is zero. + * + * Accounts expected by this instruction: + * + * * Single owner + * 0. `[writable]` The account to close. + * 1. '[writable]' The destination account. + * 2. `[signer]` The account's owner. + * + * * Multisignature owner + * 0. `[writable]` The account to close. + * 1. '[writable]' The destination account. + * 2. `[]` The account's multisignature owner. + * 3. ..3+M '[signer]' M signer accounts. + */ + Token_TokenInstruction_CloseAccount, +} Token_TokenInstruction_Tag; + +typedef struct Token_TokenInstruction_Token_InitializeMint_Body { + /** + * Initial amount of tokens to mint. + */ + uint64_t amount; + /** + * Number of base 10 digits to the right of the decimal place. + */ + uint8_t decimals; +} Token_TokenInstruction_Token_InitializeMint_Body; + +typedef struct Token_TokenInstruction_Token_InitializeMultisig_Body { + /** + * The number of signers (M) required to validate this multisignature account. + */ + uint8_t m; +} Token_TokenInstruction_Token_InitializeMultisig_Body; + +typedef struct Token_TokenInstruction_Token_Transfer_Body { + /** + * The amount of tokens to transfer. + */ + uint64_t amount; +} Token_TokenInstruction_Token_Transfer_Body; + +typedef struct Token_TokenInstruction_Token_Approve_Body { + /** + * The amount of tokens the delegate is approved for. + */ + uint64_t amount; +} Token_TokenInstruction_Token_Approve_Body; + +typedef struct Token_TokenInstruction_Token_MintTo_Body { + /** + * The amount of new tokens to mint. + */ + uint64_t amount; +} Token_TokenInstruction_Token_MintTo_Body; + +typedef struct Token_TokenInstruction_Token_Burn_Body { + /** + * The amount of tokens to burn. + */ + uint64_t amount; +} Token_TokenInstruction_Token_Burn_Body; + +typedef struct Token_TokenInstruction { + Token_TokenInstruction_Tag tag; + union { + Token_TokenInstruction_Token_InitializeMint_Body initialize_mint; + Token_TokenInstruction_Token_InitializeMultisig_Body initialize_multisig; + Token_TokenInstruction_Token_Transfer_Body transfer; + Token_TokenInstruction_Token_Approve_Body approve; + Token_TokenInstruction_Token_MintTo_Body mint_to; + Token_TokenInstruction_Token_Burn_Body burn; + }; +} Token_TokenInstruction; + +typedef uint8_t Token_Pubkey[32]; + +/** + * A C representation of Rust's `std::option::Option` + */ +typedef enum Token_COption_Pubkey_Tag { + /** + * No value + */ + Token_COption_Pubkey_None_Pubkey, + /** + * Some value `T` + */ + Token_COption_Pubkey_Some_Pubkey, +} Token_COption_Pubkey_Tag; + +typedef struct Token_COption_Pubkey_Token_Some_Body_Pubkey { + Token_Pubkey _0; +} Token_COption_Pubkey_Token_Some_Body_Pubkey; + +typedef struct Token_COption_Pubkey { + Token_COption_Pubkey_Tag tag; + union { + Token_COption_Pubkey_Token_Some_Body_Pubkey some; + }; +} Token_COption_Pubkey; + +/** + * Mint data. + */ +typedef struct Token_Mint { + /** + * Optional owner, used to mint new tokens. The owner may only + * be provided during mint creation. If no owner is present then the mint + * has a fixed supply and no further tokens may be minted. + */ + Token_COption_Pubkey owner; + /** + * Number of base 10 digits to the right of the decimal place. + */ + uint8_t decimals; + /** + * Is `true` if this structure has been initialized + */ + bool is_initialized; +} Token_Mint; + +/** + * Account data. + */ +typedef struct Token_Account { + /** + * The mint associated with this account + */ + Token_Pubkey mint; + /** + * The owner of this account. + */ + Token_Pubkey owner; + /** + * The amount of tokens this account holds. + */ + uint64_t amount; + /** + * If `delegate` is `Some` then `delegated_amount` represents + * the amount authorized by the delegate + */ + Token_COption_Pubkey delegate; + /** + * Is `true` if this structure has been initialized + */ + bool is_initialized; + /** + * Is this a native token + */ + bool is_native; + /** + * The amount delegated + */ + uint64_t delegated_amount; +} Token_Account; + +/** + * Multisignature data. + */ +typedef struct Token_Multisig { + /** + * Number of signers required + */ + uint8_t m; + /** + * Number of valid signers + */ + uint8_t n; + /** + * Is `true` if this structure has been initialized + */ + bool is_initialized; + /** + * Signer public keys + */ + Token_Pubkey signers[Token_MAX_SIGNERS]; +} Token_Multisig; diff --git a/program/program-id.md b/program/program-id.md new file mode 100644 index 0000000..0e3c586 --- /dev/null +++ b/program/program-id.md @@ -0,0 +1 @@ +TokensVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o diff --git a/program/src/entrypoint.rs b/program/src/entrypoint.rs new file mode 100644 index 0000000..b5594ca --- /dev/null +++ b/program/src/entrypoint.rs @@ -0,0 +1,24 @@ +//! Program entrypoint + +#![cfg(feature = "program")] +#![cfg(not(feature = "no-entrypoint"))] + +use crate::{error::TokenError, processor::Processor}; +use solana_sdk::{ + account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, + program_error::PrintProgramError, pubkey::Pubkey, +}; + +entrypoint!(process_instruction); +fn process_instruction<'a>( + program_id: &Pubkey, + accounts: &'a [AccountInfo<'a>], + instruction_data: &[u8], +) -> ProgramResult { + if let Err(error) = Processor::process(program_id, accounts, instruction_data) { + // catch the error so we can print it + error.print::(); + return Err(error); + } + Ok(()) +} diff --git a/program/src/error.rs b/program/src/error.rs new file mode 100644 index 0000000..cc7da55 --- /dev/null +++ b/program/src/error.rs @@ -0,0 +1,71 @@ +//! Error types + +use num_derive::FromPrimitive; +use solana_sdk::{decode_error::DecodeError, program_error::ProgramError}; +use thiserror::Error; + +/// Errors that may be returned by the Token program. +#[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)] +pub enum TokenError { + /// Insufficient funds for the operation requested. + #[error("Insufficient funds")] + InsufficientFunds, + /// Account not associated with this Mint. + #[error("Account not associated with this Mint")] + MintMismatch, + /// Owner does not match. + #[error("Owner does not match")] + OwnerMismatch, + /// This token's supply is fixed and new tokens cannot be minted. + #[error("Fixed supply")] + FixedSupply, + /// The account cannot be initialized because it is already being used. + #[error("AlreadyInUse")] + AlreadyInUse, + /// An owner is required if initial supply is zero. + #[error("An owner is required if supply is zero")] + OwnerRequiredIfNoInitialSupply, + /// Invalid number of provided signers. + #[error("Invalid number of provided signers")] + InvalidNumberOfProvidedSigners, + /// Invalid number of required signers. + #[error("Invalid number of required signers")] + InvalidNumberOfRequiredSigners, + /// State is uninitialized. + #[error("State is unititialized")] + UninitializedState, + /// Instruction does not support native tokens + #[error("Instruction does not support native tokens")] + NativeNotSupported, + /// Non-native account can only be closed if its balance is zero + #[error("Non-native account can only be closed if its balance is zero")] + NonNativeHasBalance, + /// Invalid instruction + #[error("Invalid instruction")] + InvalidInstruction, + /// State is invalid for requested operation. + #[error("State is invalid for requested operation")] + InvalidState, + /// Operation overflowed + #[error("Operation overflowed")] + Overflow, + /// Account does not support specified authority type. + #[error("Account does not support specified authority type")] + AuthorityTypeNotSupported, + /// This token mint cannot freeze accounts. + #[error("This token mint cannot freeze accounts")] + MintCannotFreeze, + /// Account is frozen; all account operations will fail + #[error("Account is frozen")] + AccountFrozen, +} +impl From for ProgramError { + fn from(e: TokenError) -> Self { + ProgramError::Custom(e as u32) + } +} +impl DecodeError for TokenError { + fn type_of() -> &'static str { + "TokenError" + } +} diff --git a/program/src/instruction.rs b/program/src/instruction.rs new file mode 100644 index 0000000..96acf76 --- /dev/null +++ b/program/src/instruction.rs @@ -0,0 +1,915 @@ +//! Instruction types + +use crate::{error::TokenError, option::COption}; +use solana_sdk::{ + instruction::{AccountMeta, Instruction}, + program_error::ProgramError, + pubkey::Pubkey, +}; +use std::mem::size_of; + +/// Minimum number of multisignature signers (min N) +pub const MIN_SIGNERS: usize = 1; +/// Maximum number of multisignature signers (max N) +pub const MAX_SIGNERS: usize = 11; + +/// Instructions supported by the token program. +#[repr(C)] +#[derive(Clone, Debug, PartialEq)] +pub enum TokenInstruction { + /// Initializes a new mint and optionally deposits all the newly minted tokens in an account. + /// + /// The `InitializeMint` instruction requires no signers and MUST be included within + /// the same Transaction as the system program's `CreateInstruction` that creates the account + /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. + /// + /// Accounts expected by this instruction: + /// + /// 0. `[writable]` The mint to initialize. + /// + InitializeMint { + /// Number of base 10 digits to the right of the decimal place. + decimals: u8, + /// The authority/multisignature to mint tokens. If present, further minting is supported. + mint_authority: COption, + /// The freeze authority/multisignature of the mint. + freeze_authority: COption, + }, + /// Initializes a new account to hold tokens. If this account is associated with the native mint + /// then the token balance of the initialized account will be equal to the amount of SOL in the account. + /// + /// The `InitializeAccount` instruction requires no signers and MUST be included within + /// the same Transaction as the system program's `CreateInstruction` that creates the account + /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. + /// + /// Accounts expected by this instruction: + /// + /// 0. `[writable]` The account to initialize. + /// 1. `[]` The mint this account will be associated with. + /// 2. `[]` The new account's owner/multisignature. + InitializeAccount, + /// Initializes a multisignature account with N provided signers. + /// + /// Multisignature accounts can used in place of any single owner/delegate accounts in any + /// token instruction that require an owner/delegate to be present. The variant field represents the + /// number of signers (M) required to validate this multisignature account. + /// + /// The `InitializeMultisig` instruction requires no signers and MUST be included within + /// the same Transaction as the system program's `CreateInstruction` that creates the account + /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. + /// + /// Accounts expected by this instruction: + /// + /// 0. `[writable]` The multisignature account to initialize. + /// 1. ..1+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. + InitializeMultisig { + /// The number of signers (M) required to validate this multisignature account. + m: u8, + }, + /// Transfers tokens from one account to another either directly or via a delegate. If this + /// account is associated with the native mint then equal amounts of SOL and Tokens will be + /// transferred to the destination account. + /// + /// Accounts expected by this instruction: + /// + /// * Single owner/delegate + /// 0. `[writable]` The source account. + /// 1. `[writable]` The destination account. + /// 2. '[signer]' The source account's owner/delegate. + /// + /// * Multisignature owner/delegate + /// 0. `[writable]` The source account. + /// 1. `[writable]` The destination account. + /// 2. '[]' The source account's multisignature owner/delegate. + /// 3. ..3+M '[signer]' M signer accounts. + Transfer { + /// The amount of tokens to transfer. + amount: u64, + }, + /// Approves a delegate. A delegate is given the authority over + /// tokens on behalf of the source account's owner. + + /// Accounts expected by this instruction: + /// + /// * Single owner + /// 0. `[writable]` The source account. + /// 1. `[]` The delegate. + /// 2. `[signer]` The source account owner. + /// + /// * Multisignature owner + /// 0. `[writable]` The source account. + /// 1. `[]` The delegate. + /// 2. '[]' The source account's multisignature owner. + /// 3. ..3+M '[signer]' M signer accounts + Approve { + /// The amount of tokens the delegate is approved for. + amount: u64, + }, + /// Revokes the delegate's authority. + /// + /// Accounts expected by this instruction: + /// + /// * Single owner + /// 0. `[writable]` The source account. + /// 1. `[signer]` The source account owner. + /// + /// * Multisignature owner + /// 0. `[writable]` The source account. + /// 1. '[]' The source account's multisignature owner. + /// 2. ..2+M '[signer]' M signer accounts + Revoke, + /// Sets a new authority of a mint or account. + /// + /// Accounts expected by this instruction: + /// + /// * Single authority + /// 0. `[writable]` The mint or account to change the authority of. + /// 1. `[signer]` The current authority of the mint or account. + /// + /// * Multisignature authority + /// 0. `[writable]` The mint or account to change the authority of. + /// 1. `[]` The mint's or account's multisignature authority. + /// 3. ..3+M '[signer]' M signer accounts + SetAuthority { + /// The type of authority to update. + authority_type: AuthorityType, + /// The new authority + new_authority: COption, + }, + /// Mints new tokens to an account. The native mint does not support minting. + /// + /// Accounts expected by this instruction: + /// + /// * Single authority + /// 0. `[writable]` The mint. + /// 1. `[writable]` The account to mint tokens to. + /// 2. `[signer]` The mint's minting authority. + /// + /// * Multisignature authority + /// 0. `[writable]` The mint. + /// 1. `[writable]` The account to mint tokens to. + /// 2. `[]` The mint's multisignature mint-tokens authority. + /// 3. ..3+M '[signer]' M signer accounts. + MintTo { + /// The amount of new tokens to mint. + amount: u64, + }, + /// Burns tokens by removing them from an account. `Burn` does not support accounts + /// associated with the native mint, use `CloseAccount` instead. + /// + /// Accounts expected by this instruction: + /// + /// * Single owner/delegate + /// 0. `[writable]` The account to burn from. + /// 1. `[signer]` The account's owner/delegate. + /// + /// * Multisignature owner/delegate + /// 0. `[writable]` The account to burn from. + /// 1. `[]` The account's multisignature owner/delegate. + /// 2. ..2+M '[signer]' M signer accounts. + Burn { + /// The amount of tokens to burn. + amount: u64, + }, + /// Close an account by transferring all its SOL to the destination account. + /// Non-native accounts may only be closed if its token amount is zero. + /// + /// Accounts expected by this instruction: + /// + /// * Single owner + /// 0. `[writable]` The account to close. + /// 1. '[writable]' The destination account. + /// 2. `[signer]` The account's owner. + /// + /// * Multisignature owner + /// 0. `[writable]` The account to close. + /// 1. '[writable]' The destination account. + /// 2. `[]` The account's multisignature owner. + /// 3. ..3+M '[signer]' M signer accounts. + CloseAccount, + /// Freeze an Initialized account using the Mint's freeze_authority (if set). + /// + /// Accounts expected by this instruction: + /// + /// * Single owner + /// 0. `[writable]` The account to freeze. + /// 1. '[]' The token mint. + /// 2. `[signer]` The mint freeze authority. + /// + /// * Multisignature owner + /// 0. `[writable]` The account to freeze. + /// 1. '[]' The token mint. + /// 2. `[]` The mint's multisignature freeze authority. + /// 3. ..3+M '[signer]' M signer accounts. + FreezeAccount, + /// Thaw a Frozen account using the Mint's freeze_authority (if set). + /// + /// Accounts expected by this instruction: + /// + /// * Single owner + /// 0. `[writable]` The account to freeze. + /// 1. '[]' The token mint. + /// 2. `[signer]` The mint freeze authority. + /// + /// * Multisignature owner + /// 0. `[writable]` The account to freeze. + /// 1. '[]' The token mint. + /// 2. `[]` The mint's multisignature freeze authority. + /// 3. ..3+M '[signer]' M signer accounts. + ThawAccount, +} +impl TokenInstruction { + /// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html). + pub fn unpack(input: &[u8]) -> Result { + if input.len() < size_of::() { + return Err(TokenError::InvalidInstruction.into()); + } + Ok(match input[0] { + 0 => { + if input.len() < size_of::() + size_of::() + size_of::() { + return Err(TokenError::InvalidInstruction.into()); + } + let mut input_len = 0; + input_len += size_of::(); + + let decimals = unsafe { *(&input[input_len] as *const u8) }; + input_len += size_of::(); + + let mint_authority = COption::unpack_or( + input, + &mut input_len, + Into::::into(TokenError::InvalidInstruction), + )?; + let freeze_authority = COption::unpack_or( + input, + &mut input_len, + Into::::into(TokenError::InvalidInstruction), + )?; + + Self::InitializeMint { + mint_authority, + freeze_authority, + decimals, + } + } + 1 => Self::InitializeAccount, + 2 => { + if input.len() < size_of::() + size_of::() { + return Err(TokenError::InvalidInstruction.into()); + } + #[allow(clippy::cast_ptr_alignment)] + let m = unsafe { *(&input[1] as *const u8) }; + Self::InitializeMultisig { m } + } + 3 => { + if input.len() < size_of::() + size_of::() { + return Err(TokenError::InvalidInstruction.into()); + } + #[allow(clippy::cast_ptr_alignment)] + let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; + Self::Transfer { amount } + } + 4 => { + if input.len() < size_of::() + size_of::() { + return Err(TokenError::InvalidInstruction.into()); + } + #[allow(clippy::cast_ptr_alignment)] + let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; + Self::Approve { amount } + } + 5 => Self::Revoke, + 6 => { + if input.len() < size_of::() + size_of::() { + return Err(TokenError::InvalidInstruction.into()); + } + let mut input_len = 0; + input_len += size_of::(); + let authority_type = AuthorityType::from(input[1])?; + input_len += size_of::(); + + let new_authority = COption::unpack_or( + input, + &mut input_len, + Into::::into(TokenError::InvalidInstruction), + )?; + + Self::SetAuthority { + authority_type, + new_authority, + } + } + 7 => { + if input.len() < size_of::() + size_of::() { + return Err(TokenError::InvalidInstruction.into()); + } + #[allow(clippy::cast_ptr_alignment)] + let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; + Self::MintTo { amount } + } + 8 => { + if input.len() < size_of::() + size_of::() { + return Err(TokenError::InvalidInstruction.into()); + } + #[allow(clippy::cast_ptr_alignment)] + let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; + Self::Burn { amount } + } + 9 => Self::CloseAccount, + 10 => Self::FreezeAccount, + 11 => Self::ThawAccount, + _ => return Err(TokenError::InvalidInstruction.into()), + }) + } + + /// Packs a [TokenInstruction](enum.TokenInstruction.html) into a byte buffer. + pub fn pack(&self) -> Result, ProgramError> { + let mut output = vec![0u8; size_of::()]; + let mut output_len = 0; + match self { + Self::InitializeMint { + mint_authority, + freeze_authority, + decimals, + } => { + output[output_len] = 0; + output_len += size_of::(); + + let value = unsafe { &mut *(&mut output[output_len] as *mut u8) }; + *value = *decimals; + output_len += size_of::(); + + mint_authority.pack(&mut output, &mut output_len); + freeze_authority.pack(&mut output, &mut output_len); + } + Self::InitializeAccount => { + output[output_len] = 1; + output_len += size_of::(); + } + Self::InitializeMultisig { m } => { + output[output_len] = 2; + output_len += size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u8) }; + *value = *m; + output_len += size_of::(); + } + Self::Transfer { amount } => { + output[output_len] = 3; + output_len += size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; + *value = *amount; + output_len += size_of::(); + } + Self::Approve { amount } => { + output[output_len] = 4; + output_len += size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; + *value = *amount; + output_len += size_of::(); + } + Self::Revoke => { + output[output_len] = 5; + output_len += size_of::(); + } + Self::SetAuthority { + authority_type, + new_authority, + } => { + output[output_len] = 6; + output_len += size_of::(); + + output[output_len] = authority_type.into(); + output_len += size_of::(); + + new_authority.pack(&mut output, &mut output_len); + } + Self::MintTo { amount } => { + output[output_len] = 7; + output_len += size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; + *value = *amount; + output_len += size_of::(); + } + Self::Burn { amount } => { + output[output_len] = 8; + output_len += size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; + *value = *amount; + output_len += size_of::(); + } + Self::CloseAccount => { + output[output_len] = 9; + output_len += size_of::(); + } + Self::FreezeAccount => { + output[output_len] = 10; + output_len += size_of::(); + } + Self::ThawAccount => { + output[output_len] = 11; + output_len += size_of::(); + } + } + + output.truncate(output_len); + Ok(output) + } +} + +/// Specifies the authority type for SetAuthority instructions +#[repr(u8)] +#[derive(Clone, Debug, PartialEq)] +pub enum AuthorityType { + /// Authority to mint new tokens + MintTokens, + /// Authority to freeze any account associated with the Mint + FreezeAccount, + /// Holder of a given token account + AccountHolder, + /// Authority to close a token account + CloseAccount, +} + +impl AuthorityType { + fn into(&self) -> u8 { + match self { + AuthorityType::MintTokens => 0, + AuthorityType::FreezeAccount => 1, + AuthorityType::AccountHolder => 2, + AuthorityType::CloseAccount => 3, + } + } + + fn from(index: u8) -> Result { + match index { + 0 => Ok(AuthorityType::MintTokens), + 1 => Ok(AuthorityType::FreezeAccount), + 2 => Ok(AuthorityType::AccountHolder), + 3 => Ok(AuthorityType::CloseAccount), + _ => Err(TokenError::InvalidInstruction.into()), + } + } +} + +/// Creates a 'InitializeMint' instruction. +pub fn initialize_mint( + token_program_id: &Pubkey, + mint_pubkey: &Pubkey, + mint_authority_pubkey: &Pubkey, + freeze_authority_pubkey: Option<&Pubkey>, + decimals: u8, +) -> Result { + let mint_authority = COption::Some(*mint_authority_pubkey); + let freeze_authority = freeze_authority_pubkey.cloned().into(); + let data = TokenInstruction::InitializeMint { + mint_authority, + freeze_authority, + decimals, + } + .pack()?; + + let accounts = vec![AccountMeta::new(*mint_pubkey, false)]; + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates a `InitializeAccount` instruction. +pub fn initialize_account( + token_program_id: &Pubkey, + account_pubkey: &Pubkey, + mint_pubkey: &Pubkey, + owner_pubkey: &Pubkey, +) -> Result { + let data = TokenInstruction::InitializeAccount.pack()?; + + let accounts = vec![ + AccountMeta::new(*account_pubkey, false), + AccountMeta::new_readonly(*mint_pubkey, false), + AccountMeta::new_readonly(*owner_pubkey, false), + ]; + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates a `InitializeMultisig` instruction. +pub fn initialize_multisig( + token_program_id: &Pubkey, + multisig_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], + m: u8, +) -> Result { + if !is_valid_signer_index(m as usize) + || !is_valid_signer_index(signer_pubkeys.len()) + || m as usize > signer_pubkeys.len() + { + return Err(ProgramError::MissingRequiredSignature); + } + let data = TokenInstruction::InitializeMultisig { m }.pack()?; + + let mut accounts = Vec::with_capacity(1 + signer_pubkeys.len()); + accounts.push(AccountMeta::new(*multisig_pubkey, false)); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new_readonly(**signer_pubkey, false)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates a `Transfer` instruction. +pub fn transfer( + token_program_id: &Pubkey, + source_pubkey: &Pubkey, + destination_pubkey: &Pubkey, + authority_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], + amount: u64, +) -> Result { + let data = TokenInstruction::Transfer { amount }.pack()?; + + let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); + accounts.push(AccountMeta::new(*source_pubkey, false)); + accounts.push(AccountMeta::new(*destination_pubkey, false)); + accounts.push(AccountMeta::new_readonly( + *authority_pubkey, + signer_pubkeys.is_empty(), + )); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new(**signer_pubkey, true)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates an `Approve` instruction. +pub fn approve( + token_program_id: &Pubkey, + source_pubkey: &Pubkey, + delegate_pubkey: &Pubkey, + owner_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], + amount: u64, +) -> Result { + let data = TokenInstruction::Approve { amount }.pack()?; + + let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); + accounts.push(AccountMeta::new(*source_pubkey, false)); + accounts.push(AccountMeta::new_readonly(*delegate_pubkey, false)); + accounts.push(AccountMeta::new_readonly( + *owner_pubkey, + signer_pubkeys.is_empty(), + )); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new(**signer_pubkey, true)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates a `Revoke` instruction. +pub fn revoke( + token_program_id: &Pubkey, + source_pubkey: &Pubkey, + owner_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], +) -> Result { + let data = TokenInstruction::Revoke.pack()?; + + let mut accounts = Vec::with_capacity(2 + signer_pubkeys.len()); + accounts.push(AccountMeta::new_readonly(*source_pubkey, false)); + accounts.push(AccountMeta::new_readonly( + *owner_pubkey, + signer_pubkeys.is_empty(), + )); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new(**signer_pubkey, true)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates a `SetAuthority` instruction. +pub fn set_authority( + token_program_id: &Pubkey, + owned_pubkey: &Pubkey, + new_authority_pubkey: Option<&Pubkey>, + authority_type: AuthorityType, + owner_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], +) -> Result { + let new_authority = new_authority_pubkey.cloned().into(); + let data = TokenInstruction::SetAuthority { + authority_type, + new_authority, + } + .pack()?; + + let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); + accounts.push(AccountMeta::new(*owned_pubkey, false)); + accounts.push(AccountMeta::new_readonly( + *owner_pubkey, + signer_pubkeys.is_empty(), + )); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new(**signer_pubkey, true)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates a `MintTo` instruction. +pub fn mint_to( + token_program_id: &Pubkey, + mint_pubkey: &Pubkey, + account_pubkey: &Pubkey, + owner_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], + amount: u64, +) -> Result { + let data = TokenInstruction::MintTo { amount }.pack()?; + + let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); + accounts.push(AccountMeta::new(*mint_pubkey, false)); + accounts.push(AccountMeta::new(*account_pubkey, false)); + accounts.push(AccountMeta::new_readonly( + *owner_pubkey, + signer_pubkeys.is_empty(), + )); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new(**signer_pubkey, true)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates a `Burn` instruction. +pub fn burn( + token_program_id: &Pubkey, + account_pubkey: &Pubkey, + authority_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], + amount: u64, +) -> Result { + let data = TokenInstruction::Burn { amount }.pack()?; + + let mut accounts = Vec::with_capacity(2 + signer_pubkeys.len()); + accounts.push(AccountMeta::new(*account_pubkey, false)); + accounts.push(AccountMeta::new_readonly( + *authority_pubkey, + signer_pubkeys.is_empty(), + )); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new(**signer_pubkey, true)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates a `CloseAccount` instruction. +pub fn close_account( + token_program_id: &Pubkey, + account_pubkey: &Pubkey, + destination_pubkey: &Pubkey, + owner_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], +) -> Result { + let data = TokenInstruction::CloseAccount.pack()?; + + let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); + accounts.push(AccountMeta::new(*account_pubkey, false)); + accounts.push(AccountMeta::new(*destination_pubkey, false)); + accounts.push(AccountMeta::new_readonly( + *owner_pubkey, + signer_pubkeys.is_empty(), + )); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new(**signer_pubkey, true)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates a `FreezeAccount` instruction. +pub fn freeze_account( + token_program_id: &Pubkey, + account_pubkey: &Pubkey, + mint_pubkey: &Pubkey, + owner_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], +) -> Result { + let data = TokenInstruction::FreezeAccount.pack()?; + + let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); + accounts.push(AccountMeta::new(*account_pubkey, false)); + accounts.push(AccountMeta::new_readonly(*mint_pubkey, false)); + accounts.push(AccountMeta::new_readonly( + *owner_pubkey, + signer_pubkeys.is_empty(), + )); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new(**signer_pubkey, true)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates a `ThawAccount` instruction. +pub fn thaw_account( + token_program_id: &Pubkey, + account_pubkey: &Pubkey, + mint_pubkey: &Pubkey, + owner_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], +) -> Result { + let data = TokenInstruction::ThawAccount.pack()?; + + let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); + accounts.push(AccountMeta::new(*account_pubkey, false)); + accounts.push(AccountMeta::new_readonly(*mint_pubkey, false)); + accounts.push(AccountMeta::new_readonly( + *owner_pubkey, + signer_pubkeys.is_empty(), + )); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new(**signer_pubkey, true)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Utility function that checks index is between MIN_SIGNERS and MAX_SIGNERS +pub fn is_valid_signer_index(index: usize) -> bool { + !(index < MIN_SIGNERS || index > MAX_SIGNERS) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_instruction_packing() { + let check = TokenInstruction::InitializeMint { + decimals: 2, + mint_authority: COption::None, + freeze_authority: COption::None, + }; + let packed = check.pack().unwrap(); + let expect = Vec::from([0u8, 2, 0, 0]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::InitializeMint { + decimals: 2, + mint_authority: COption::Some(Pubkey::new(&[2u8; 32])), + freeze_authority: COption::Some(Pubkey::new(&[3u8; 32])), + }; + let packed = check.pack().unwrap(); + let mut expect = vec![0u8, 2]; + expect.extend_from_slice(&[1]); + expect.extend_from_slice(&[2u8; 32]); + expect.extend_from_slice(&[1]); + expect.extend_from_slice(&[3u8; 32]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::InitializeAccount; + let packed = check.pack().unwrap(); + let expect = Vec::from([1u8]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::InitializeMultisig { m: 1 }; + let packed = check.pack().unwrap(); + let expect = Vec::from([2u8, 1]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::Transfer { amount: 1 }; + let packed = check.pack().unwrap(); + let expect = Vec::from([3u8, 1, 0, 0, 0, 0, 0, 0, 0]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::Approve { amount: 1 }; + let packed = check.pack().unwrap(); + let expect = Vec::from([4u8, 1, 0, 0, 0, 0, 0, 0, 0]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::Revoke; + let packed = check.pack().unwrap(); + let expect = Vec::from([5u8]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::SetAuthority { + authority_type: AuthorityType::FreezeAccount, + new_authority: COption::Some(Pubkey::new(&[4u8; 32])), + }; + let packed = check.pack().unwrap(); + let mut expect = Vec::from([6u8, 1]); + expect.extend_from_slice(&[1]); + expect.extend_from_slice(&[4u8; 32]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::MintTo { amount: 1 }; + let packed = check.pack().unwrap(); + let expect = Vec::from([7u8, 1, 0, 0, 0, 0, 0, 0, 0]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::Burn { amount: 1 }; + let packed = check.pack().unwrap(); + let expect = Vec::from([8u8, 1, 0, 0, 0, 0, 0, 0, 0]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::CloseAccount; + let packed = check.pack().unwrap(); + let expect = Vec::from([9u8]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::FreezeAccount; + let packed = check.pack().unwrap(); + let expect = Vec::from([10u8]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::ThawAccount; + let packed = check.pack().unwrap(); + let expect = Vec::from([11u8]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + } +} diff --git a/program/src/lib.rs b/program/src/lib.rs new file mode 100644 index 0000000..c6096a5 --- /dev/null +++ b/program/src/lib.rs @@ -0,0 +1,28 @@ +#![deny(missing_docs)] + +//! An ERC20-like Token program for the Solana blockchain + +pub mod entrypoint; +pub mod error; +pub mod instruction; +pub mod native_mint; +pub mod option; +pub mod processor; +pub mod state; + +// Export current solana-sdk types for downstream users who may also be building with a different +// solana-sdk version +pub use solana_sdk; + +/// Convert the UI representation of a token amount (using the decimals field defined in its mint) +/// to the raw amount +pub fn ui_amount_to_amount(ui_amount: f64, decimals: u8) -> u64 { + (ui_amount * 10_usize.pow(decimals as u32) as f64) as u64 +} + +/// Convert a raw amount to its UI representation (using the decimals field defined in its mint) +pub fn amount_to_ui_amount(amount: u64, decimals: u8) -> f64 { + amount as f64 / 10_usize.pow(decimals as u32) as f64 +} + +solana_sdk::declare_id!("TokensVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o"); diff --git a/program/src/native_mint.rs b/program/src/native_mint.rs new file mode 100644 index 0000000..e502128 --- /dev/null +++ b/program/src/native_mint.rs @@ -0,0 +1,24 @@ +//! The Mint that represents the native token + +/// There are 10^9 lamports in one SOL +pub const DECIMALS: u8 = 9; + +// The Mint for native SOL Token accounts +solana_sdk::declare_id!("So12111111111111111111111111111111111111111"); + +#[cfg(test)] +mod tests { + use super::*; + use solana_sdk::native_token::*; + + #[test] + fn test_decimals() { + assert!( + lamports_to_sol(42) - crate::amount_to_ui_amount(42, DECIMALS).abs() < f64::EPSILON + ); + assert_eq!( + sol_to_lamports(42.), + crate::ui_amount_to_amount(42., DECIMALS) + ); + } +} diff --git a/program/src/option.rs b/program/src/option.rs new file mode 100644 index 0000000..9e6f16d --- /dev/null +++ b/program/src/option.rs @@ -0,0 +1,1112 @@ +//! A C representation of Rust's `std::option::Option` used accross the FFI +//! boundary for Solana program interfaces +//! +//! This implementation mostly matches `std::option` except iterators since the iteration +//! trait requires returning `std::option::Option` + +use std::pin::Pin; +use std::{ + convert, hint, mem, + ops::{Deref, DerefMut}, +}; + +/// A C representation of Rust's `std::option::Option` +#[repr(C)] +#[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] +pub enum COption { + /// No value + None, + /// Some value `T` + Some(T), +} + +///////////////////////////////////////////////////////////////////////////// +// Type implementation +///////////////////////////////////////////////////////////////////////////// + +impl COption { + ///////////////////////////////////////////////////////////////////////// + // Querying the contained values + ///////////////////////////////////////////////////////////////////////// + + /// Returns `true` if the option is a [`COption::Some`] value. + /// + /// # Examples + /// + /// ```ignore + /// let x: COption = COption::Some(2); + /// assert_eq!(x.is_some(), true); + /// + /// let x: COption = COption::None; + /// assert_eq!(x.is_some(), false); + /// ``` + /// + /// [`COption::Some`]: #variant.COption::Some + #[must_use = "if you intended to assert that this has a value, consider `.unwrap()` instead"] + #[inline] + pub fn is_some(&self) -> bool { + match *self { + COption::Some(_) => true, + COption::None => false, + } + } + + /// Returns `true` if the option is a [`COption::None`] value. + /// + /// # Examples + /// + /// ```ignore + /// let x: COption = COption::Some(2); + /// assert_eq!(x.is_none(), false); + /// + /// let x: COption = COption::None; + /// assert_eq!(x.is_none(), true); + /// ``` + /// + /// [`COption::None`]: #variant.COption::None + #[must_use = "if you intended to assert that this doesn't have a value, consider \ + `.and_then(|| panic!(\"`COption` had a value when expected `COption::None`\"))` instead"] + #[inline] + pub fn is_none(&self) -> bool { + !self.is_some() + } + + /// Returns `true` if the option is a [`COption::Some`] value containing the given value. + /// + /// # Examples + /// + /// ```ignore + /// #![feature(option_result_contains)] + /// + /// let x: COption = COption::Some(2); + /// assert_eq!(x.contains(&2), true); + /// + /// let x: COption = COption::Some(3); + /// assert_eq!(x.contains(&2), false); + /// + /// let x: COption = COption::None; + /// assert_eq!(x.contains(&2), false); + /// ``` + #[must_use] + #[inline] + pub fn contains(&self, x: &U) -> bool + where + U: PartialEq, + { + match self { + COption::Some(y) => x == y, + COption::None => false, + } + } + + ///////////////////////////////////////////////////////////////////////// + // Adapter for working with references + ///////////////////////////////////////////////////////////////////////// + + /// Converts from `&COption` to `COption<&T>`. + /// + /// # Examples + /// + /// Converts an `COption<`[`String`]`>` into an `COption<`[`usize`]`>`, preserving the original. + /// The [`map`] method takes the `self` argument by value, consuming the original, + /// so this technique uses `as_ref` to first take an `COption` to a reference + /// to the value inside the original. + /// + /// [`map`]: enum.COption.html#method.map + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ```ignore + /// let text: COption = COption::Some("Hello, world!".to_string()); + /// // First, cast `COption` to `COption<&String>` with `as_ref`, + /// // then consume *that* with `map`, leaving `text` on the stack. + /// let text_length: COption = text.as_ref().map(|s| s.len()); + /// println!("still can print text: {:?}", text); + /// ``` + #[inline] + pub fn as_ref(&self) -> COption<&T> { + match *self { + COption::Some(ref x) => COption::Some(x), + COption::None => COption::None, + } + } + + /// Converts from `&mut COption` to `COption<&mut T>`. + /// + /// # Examples + /// + /// ```ignore + /// let mut x = COption::Some(2); + /// match x.as_mut() { + /// COption::Some(v) => *v = 42, + /// COption::None => {}, + /// } + /// assert_eq!(x, COption::Some(42)); + /// ``` + #[inline] + pub fn as_mut(&mut self) -> COption<&mut T> { + match *self { + COption::Some(ref mut x) => COption::Some(x), + COption::None => COption::None, + } + } + + /// Converts from [`Pin`]`<&COption>` to `COption<`[`Pin`]`<&T>>`. + /// + /// [`Pin`]: ../pin/struct.Pin.html + #[inline] + #[allow(clippy::wrong_self_convention)] + pub fn as_pin_ref(self: Pin<&Self>) -> COption> { + unsafe { Pin::get_ref(self).as_ref().map(|x| Pin::new_unchecked(x)) } + } + + /// Converts from [`Pin`]`<&mut COption>` to `COption<`[`Pin`]`<&mut T>>`. + /// + /// [`Pin`]: ../pin/struct.Pin.html + #[inline] + #[allow(clippy::wrong_self_convention)] + pub fn as_pin_mut(self: Pin<&mut Self>) -> COption> { + unsafe { + Pin::get_unchecked_mut(self) + .as_mut() + .map(|x| Pin::new_unchecked(x)) + } + } + + ///////////////////////////////////////////////////////////////////////// + // Getting to contained values + ///////////////////////////////////////////////////////////////////////// + + /// Unwraps an option, yielding the content of a [`COption::Some`]. + /// + /// # Panics + /// + /// Panics if the value is a [`COption::None`] with a custom panic message provided by + /// `msg`. + /// + /// [`COption::Some`]: #variant.COption::Some + /// [`COption::None`]: #variant.COption::None + /// + /// # Examples + /// + /// ```ignore + /// let x = COption::Some("value"); + /// assert_eq!(x.expect("the world is ending"), "value"); + /// ``` + /// + /// ```ignore{.should_panic} + /// let x: COption<&str> = COption::None; + /// x.expect("the world is ending"); // panics with `the world is ending` + /// ``` + #[inline] + pub fn expect(self, msg: &str) -> T { + match self { + COption::Some(val) => val, + COption::None => expect_failed(msg), + } + } + + /// Moves the value `v` out of the `COption` if it is [`COption::Some(v)`]. + /// + /// In general, because this function may panic, its use is discouraged. + /// Instead, prefer to use pattern matching and handle the [`COption::None`] + /// case explicitly. + /// + /// # Panics + /// + /// Panics if the self value equals [`COption::None`]. + /// + /// [`COption::Some(v)`]: #variant.COption::Some + /// [`COption::None`]: #variant.COption::None + /// + /// # Examples + /// + /// ```ignore + /// let x = COption::Some("air"); + /// assert_eq!(x.unwrap(), "air"); + /// ``` + /// + /// ```ignore{.should_panic} + /// let x: COption<&str> = COption::None; + /// assert_eq!(x.unwrap(), "air"); // fails + /// ``` + #[inline] + pub fn unwrap(self) -> T { + match self { + COption::Some(val) => val, + COption::None => panic!("called `COption::unwrap()` on a `COption::None` value"), + } + } + + /// Returns the contained value or a default. + /// + /// Arguments passed to `unwrap_or` are eagerly evaluated; if you are passing + /// the result of a function call, it is recommended to use [`unwrap_or_else`], + /// which is lazily evaluated. + /// + /// [`unwrap_or_else`]: #method.unwrap_or_else + /// + /// # Examples + /// + /// ```ignore + /// assert_eq!(COption::Some("car").unwrap_or("bike"), "car"); + /// assert_eq!(COption::None.unwrap_or("bike"), "bike"); + /// ``` + #[inline] + pub fn unwrap_or(self, def: T) -> T { + match self { + COption::Some(x) => x, + COption::None => def, + } + } + + /// Returns the contained value or computes it from a closure. + /// + /// # Examples + /// + /// ```ignore + /// let k = 10; + /// assert_eq!(COption::Some(4).unwrap_or_else(|| 2 * k), 4); + /// assert_eq!(COption::None.unwrap_or_else(|| 2 * k), 20); + /// ``` + #[inline] + pub fn unwrap_or_else T>(self, f: F) -> T { + match self { + COption::Some(x) => x, + COption::None => f(), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Transforming contained values + ///////////////////////////////////////////////////////////////////////// + + /// Maps an `COption` to `COption` by applying a function to a contained value. + /// + /// # Examples + /// + /// Converts an `COption<`[`String`]`>` into an `COption<`[`usize`]`>`, consuming the original: + /// + /// [`String`]: ../../std/string/struct.String.html + /// [`usize`]: ../../std/primitive.usize.html + /// + /// ```ignore + /// let maybe_some_string = COption::Some(String::from("Hello, World!")); + /// // `COption::map` takes self *by value*, consuming `maybe_some_string` + /// let maybe_some_len = maybe_some_string.map(|s| s.len()); + /// + /// assert_eq!(maybe_some_len, COption::Some(13)); + /// ``` + #[inline] + pub fn map U>(self, f: F) -> COption { + match self { + COption::Some(x) => COption::Some(f(x)), + COption::None => COption::None, + } + } + + /// Applies a function to the contained value (if any), + /// or returns the provided default (if not). + /// + /// # Examples + /// + /// ```ignore + /// let x = COption::Some("foo"); + /// assert_eq!(x.map_or(42, |v| v.len()), 3); + /// + /// let x: COption<&str> = COption::None; + /// assert_eq!(x.map_or(42, |v| v.len()), 42); + /// ``` + #[inline] + pub fn map_or U>(self, default: U, f: F) -> U { + match self { + COption::Some(t) => f(t), + COption::None => default, + } + } + + /// Applies a function to the contained value (if any), + /// or computes a default (if not). + /// + /// # Examples + /// + /// ```ignore + /// let k = 21; + /// + /// let x = COption::Some("foo"); + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3); + /// + /// let x: COption<&str> = COption::None; + /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42); + /// ``` + #[inline] + pub fn map_or_else U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U { + match self { + COption::Some(t) => f(t), + COption::None => default(), + } + } + + /// Transforms the `COption` into a [`Result`], mapping [`COption::Some(v)`] to + /// [`Ok(v)`] and [`COption::None`] to [`Err(err)`]. + /// + /// Arguments passed to `ok_or` are eagerly evaluated; if you are passing the + /// result of a function call, it is recommended to use [`ok_or_else`], which is + /// lazily evaluated. + /// + /// [`Result`]: ../../std/result/enum.Result.html + /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok + /// [`Err(err)`]: ../../std/result/enum.Result.html#variant.Err + /// [`COption::None`]: #variant.COption::None + /// [`COption::Some(v)`]: #variant.COption::Some + /// [`ok_or_else`]: #method.ok_or_else + /// + /// # Examples + /// + /// ```ignore + /// let x = COption::Some("foo"); + /// assert_eq!(x.ok_or(0), Ok("foo")); + /// + /// let x: COption<&str> = COption::None; + /// assert_eq!(x.ok_or(0), Err(0)); + /// ``` + #[inline] + pub fn ok_or(self, err: E) -> Result { + match self { + COption::Some(v) => Ok(v), + COption::None => Err(err), + } + } + + /// Transforms the `COption` into a [`Result`], mapping [`COption::Some(v)`] to + /// [`Ok(v)`] and [`COption::None`] to [`Err(err())`]. + /// + /// [`Result`]: ../../std/result/enum.Result.html + /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok + /// [`Err(err())`]: ../../std/result/enum.Result.html#variant.Err + /// [`COption::None`]: #variant.COption::None + /// [`COption::Some(v)`]: #variant.COption::Some + /// + /// # Examples + /// + /// ```ignore + /// let x = COption::Some("foo"); + /// assert_eq!(x.ok_or_else(|| 0), Ok("foo")); + /// + /// let x: COption<&str> = COption::None; + /// assert_eq!(x.ok_or_else(|| 0), Err(0)); + /// ``` + #[inline] + pub fn ok_or_else E>(self, err: F) -> Result { + match self { + COption::Some(v) => Ok(v), + COption::None => Err(err()), + } + } + + ///////////////////////////////////////////////////////////////////////// + // Boolean operations on the values, eager and lazy + ///////////////////////////////////////////////////////////////////////// + + /// Returns [`COption::None`] if the option is [`COption::None`], otherwise returns `optb`. + /// + /// [`COption::None`]: #variant.COption::None + /// + /// # Examples + /// + /// ```ignore + /// let x = COption::Some(2); + /// let y: COption<&str> = COption::None; + /// assert_eq!(x.and(y), COption::None); + /// + /// let x: COption = COption::None; + /// let y = COption::Some("foo"); + /// assert_eq!(x.and(y), COption::None); + /// + /// let x = COption::Some(2); + /// let y = COption::Some("foo"); + /// assert_eq!(x.and(y), COption::Some("foo")); + /// + /// let x: COption = COption::None; + /// let y: COption<&str> = COption::None; + /// assert_eq!(x.and(y), COption::None); + /// ``` + #[inline] + pub fn and(self, optb: COption) -> COption { + match self { + COption::Some(_) => optb, + COption::None => COption::None, + } + } + + /// Returns [`COption::None`] if the option is [`COption::None`], otherwise calls `f` with the + /// wrapped value and returns the result. + /// + /// COption::Some languages call this operation flatmap. + /// + /// [`COption::None`]: #variant.COption::None + /// + /// # Examples + /// + /// ```ignore + /// fn sq(x: u32) -> COption { COption::Some(x * x) } + /// fn nope(_: u32) -> COption { COption::None } + /// + /// assert_eq!(COption::Some(2).and_then(sq).and_then(sq), COption::Some(16)); + /// assert_eq!(COption::Some(2).and_then(sq).and_then(nope), COption::None); + /// assert_eq!(COption::Some(2).and_then(nope).and_then(sq), COption::None); + /// assert_eq!(COption::None.and_then(sq).and_then(sq), COption::None); + /// ``` + #[inline] + pub fn and_then COption>(self, f: F) -> COption { + match self { + COption::Some(x) => f(x), + COption::None => COption::None, + } + } + + /// Returns [`COption::None`] if the option is [`COption::None`], otherwise calls `predicate` + /// with the wrapped value and returns: + /// + /// - [`COption::Some(t)`] if `predicate` returns `true` (where `t` is the wrapped + /// value), and + /// - [`COption::None`] if `predicate` returns `false`. + /// + /// This function works similar to [`Iterator::filter()`]. You can imagine + /// the `COption` being an iterator over one or zero elements. `filter()` + /// lets you decide which elements to keep. + /// + /// # Examples + /// + /// ```ignore + /// fn is_even(n: &i32) -> bool { + /// n % 2 == 0 + /// } + /// + /// assert_eq!(COption::None.filter(is_even), COption::None); + /// assert_eq!(COption::Some(3).filter(is_even), COption::None); + /// assert_eq!(COption::Some(4).filter(is_even), COption::Some(4)); + /// ``` + /// + /// [`COption::None`]: #variant.COption::None + /// [`COption::Some(t)`]: #variant.COption::Some + /// [`Iterator::filter()`]: ../../std/iter/trait.Iterator.html#method.filter + #[inline] + pub fn filter bool>(self, predicate: P) -> Self { + if let COption::Some(x) = self { + if predicate(&x) { + return COption::Some(x); + } + } + COption::None + } + + /// Returns the option if it contains a value, otherwise returns `optb`. + /// + /// Arguments passed to `or` are eagerly evaluated; if you are passing the + /// result of a function call, it is recommended to use [`or_else`], which is + /// lazily evaluated. + /// + /// [`or_else`]: #method.or_else + /// + /// # Examples + /// + /// ```ignore + /// let x = COption::Some(2); + /// let y = COption::None; + /// assert_eq!(x.or(y), COption::Some(2)); + /// + /// let x = COption::None; + /// let y = COption::Some(100); + /// assert_eq!(x.or(y), COption::Some(100)); + /// + /// let x = COption::Some(2); + /// let y = COption::Some(100); + /// assert_eq!(x.or(y), COption::Some(2)); + /// + /// let x: COption = COption::None; + /// let y = COption::None; + /// assert_eq!(x.or(y), COption::None); + /// ``` + #[inline] + pub fn or(self, optb: COption) -> COption { + match self { + COption::Some(_) => self, + COption::None => optb, + } + } + + /// Returns the option if it contains a value, otherwise calls `f` and + /// returns the result. + /// + /// # Examples + /// + /// ```ignore + /// fn nobody() -> COption<&'static str> { COption::None } + /// fn vikings() -> COption<&'static str> { COption::Some("vikings") } + /// + /// assert_eq!(COption::Some("barbarians").or_else(vikings), COption::Some("barbarians")); + /// assert_eq!(COption::None.or_else(vikings), COption::Some("vikings")); + /// assert_eq!(COption::None.or_else(nobody), COption::None); + /// ``` + #[inline] + pub fn or_else COption>(self, f: F) -> COption { + match self { + COption::Some(_) => self, + COption::None => f(), + } + } + + /// Returns [`COption::Some`] if exactly one of `self`, `optb` is [`COption::Some`], otherwise returns [`COption::None`]. + /// + /// [`COption::Some`]: #variant.COption::Some + /// [`COption::None`]: #variant.COption::None + /// + /// # Examples + /// + /// ```ignore + /// let x = COption::Some(2); + /// let y: COption = COption::None; + /// assert_eq!(x.xor(y), COption::Some(2)); + /// + /// let x: COption = COption::None; + /// let y = COption::Some(2); + /// assert_eq!(x.xor(y), COption::Some(2)); + /// + /// let x = COption::Some(2); + /// let y = COption::Some(2); + /// assert_eq!(x.xor(y), COption::None); + /// + /// let x: COption = COption::None; + /// let y: COption = COption::None; + /// assert_eq!(x.xor(y), COption::None); + /// ``` + #[inline] + pub fn xor(self, optb: COption) -> COption { + match (self, optb) { + (COption::Some(a), COption::None) => COption::Some(a), + (COption::None, COption::Some(b)) => COption::Some(b), + _ => COption::None, + } + } + + ///////////////////////////////////////////////////////////////////////// + // Entry-like operations to insert if COption::None and return a reference + ///////////////////////////////////////////////////////////////////////// + + /// Inserts `v` into the option if it is [`COption::None`], then + /// returns a mutable reference to the contained value. + /// + /// [`COption::None`]: #variant.COption::None + /// + /// # Examples + /// + /// ```ignore + /// let mut x = COption::None; + /// + /// { + /// let y: &mut u32 = x.get_or_insert(5); + /// assert_eq!(y, &5); + /// + /// *y = 7; + /// } + /// + /// assert_eq!(x, COption::Some(7)); + /// ``` + #[inline] + pub fn get_or_insert(&mut self, v: T) -> &mut T { + self.get_or_insert_with(|| v) + } + + /// Inserts a value computed from `f` into the option if it is [`COption::None`], then + /// returns a mutable reference to the contained value. + /// + /// [`COption::None`]: #variant.COption::None + /// + /// # Examples + /// + /// ```ignore + /// let mut x = COption::None; + /// + /// { + /// let y: &mut u32 = x.get_or_insert_with(|| 5); + /// assert_eq!(y, &5); + /// + /// *y = 7; + /// } + /// + /// assert_eq!(x, COption::Some(7)); + /// ``` + #[inline] + pub fn get_or_insert_with T>(&mut self, f: F) -> &mut T { + if let COption::None = *self { + *self = COption::Some(f()) + } + + match *self { + COption::Some(ref mut v) => v, + COption::None => unsafe { hint::unreachable_unchecked() }, + } + } + + ///////////////////////////////////////////////////////////////////////// + // Misc + ///////////////////////////////////////////////////////////////////////// + + /// Replaces the actual value in the option by the value given in parameter, + /// returning the old value if present, + /// leaving a [`COption::Some`] in its place without deinitializing either one. + /// + /// [`COption::Some`]: #variant.COption::Some + /// + /// # Examples + /// + /// ```ignore + /// let mut x = COption::Some(2); + /// let old = x.replace(5); + /// assert_eq!(x, COption::Some(5)); + /// assert_eq!(old, COption::Some(2)); + /// + /// let mut x = COption::None; + /// let old = x.replace(3); + /// assert_eq!(x, COption::Some(3)); + /// assert_eq!(old, COption::None); + /// ``` + #[inline] + pub fn replace(&mut self, value: T) -> COption { + mem::replace(self, COption::Some(value)) + } + + ///////////////////////////////////////////////////////////////////////// + // SPL Token-Specific Methods + ///////////////////////////////////////////////////////////////////////// + + /// Packs a COption into a mutable slice as compactly as possible + #[inline] + pub fn pack(&self, output: &mut [u8], cursor: &mut usize) + where + T: Copy, + { + match self { + COption::Some(some_value) => { + output[*cursor] = 1; + *cursor += mem::size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let value = unsafe { &mut *(&mut output[*cursor] as *mut u8 as *mut T) }; + *value = *some_value; + *cursor += mem::size_of::(); + } + COption::None => { + output[*cursor] = 0; + *cursor += mem::size_of::(); + } + } + } + + /// Unpacks a COption from a compact slice + #[inline] + pub fn unpack_or(input: &[u8], cursor: &mut usize, error: E) -> Result, E> + where + T: Copy, + { + match input[*cursor] { + 0 => { + *cursor += mem::size_of::(); + Ok(COption::None) + } + 1 => { + *cursor += mem::size_of::(); + #[allow(clippy::cast_ptr_alignment)] + let result = unsafe { *(&input[*cursor] as *const u8 as *const T) }; + *cursor += mem::size_of::(); + Ok(COption::Some(result)) + } + _ => Err(error), + } + } +} + +impl COption<&T> { + /// Maps an `COption<&T>` to an `COption` by copying the contents of the + /// option. + /// + /// # Examples + /// + /// ```ignore + /// let x = 12; + /// let opt_x = COption::Some(&x); + /// assert_eq!(opt_x, COption::Some(&12)); + /// let copied = opt_x.copied(); + /// assert_eq!(copied, COption::Some(12)); + /// ``` + pub fn copied(self) -> COption { + self.map(|&t| t) + } +} + +impl COption<&mut T> { + /// Maps an `COption<&mut T>` to an `COption` by copying the contents of the + /// option. + /// + /// # Examples + /// + /// ```ignore + /// let mut x = 12; + /// let opt_x = COption::Some(&mut x); + /// assert_eq!(opt_x, COption::Some(&mut 12)); + /// let copied = opt_x.copied(); + /// assert_eq!(copied, COption::Some(12)); + /// ``` + pub fn copied(self) -> COption { + self.map(|&mut t| t) + } +} + +impl COption<&T> { + /// Maps an `COption<&T>` to an `COption` by cloning the contents of the + /// option. + /// + /// # Examples + /// + /// ```ignore + /// let x = 12; + /// let opt_x = COption::Some(&x); + /// assert_eq!(opt_x, COption::Some(&12)); + /// let cloned = opt_x.cloned(); + /// assert_eq!(cloned, COption::Some(12)); + /// ``` + pub fn cloned(self) -> COption { + self.map(|t| t.clone()) + } +} + +impl COption<&mut T> { + /// Maps an `COption<&mut T>` to an `COption` by cloning the contents of the + /// option. + /// + /// # Examples + /// + /// ```ignore + /// let mut x = 12; + /// let opt_x = COption::Some(&mut x); + /// assert_eq!(opt_x, COption::Some(&mut 12)); + /// let cloned = opt_x.cloned(); + /// assert_eq!(cloned, COption::Some(12)); + /// ``` + pub fn cloned(self) -> COption { + self.map(|t| t.clone()) + } +} + +impl COption { + /// Returns the contained value or a default + /// + /// Consumes the `self` argument then, if [`COption::Some`], returns the contained + /// value, otherwise if [`COption::None`], returns the [default value] for that + /// type. + /// + /// # Examples + /// + /// Converts a string to an integer, turning poorly-formed strings + /// into 0 (the default value for integers). [`parse`] converts + /// a string to any other type that implements [`FromStr`], returning + /// [`COption::None`] on error. + /// + /// ```ignore + /// let good_year_from_input = "1909"; + /// let bad_year_from_input = "190blarg"; + /// let good_year = good_year_from_input.parse().ok().unwrap_or_default(); + /// let bad_year = bad_year_from_input.parse().ok().unwrap_or_default(); + /// + /// assert_eq!(1909, good_year); + /// assert_eq!(0, bad_year); + /// ``` + /// + /// [`COption::Some`]: #variant.COption::Some + /// [`COption::None`]: #variant.COption::None + /// [default value]: ../default/trait.Default.html#tymethod.default + /// [`parse`]: ../../std/primitive.str.html#method.parse + /// [`FromStr`]: ../../std/str/trait.FromStr.html + #[inline] + pub fn unwrap_or_default(self) -> T { + match self { + COption::Some(x) => x, + COption::None => Default::default(), + } + } +} + +impl COption { + /// Converts from `COption` (or `&COption`) to `COption<&T::Target>`. + /// + /// Leaves the original COption in-place, creating a new one with a reference + /// to the original one, additionally coercing the contents via [`Deref`]. + /// + /// [`Deref`]: ../../std/ops/trait.Deref.html + /// + /// # Examples + /// + /// ```ignore + /// #![feature(inner_deref)] + /// + /// let x: COption = COption::Some("hey".to_owned()); + /// assert_eq!(x.as_deref(), COption::Some("hey")); + /// + /// let x: COption = COption::None; + /// assert_eq!(x.as_deref(), COption::None); + /// ``` + pub fn as_deref(&self) -> COption<&T::Target> { + self.as_ref().map(|t| t.deref()) + } +} + +impl COption { + /// Converts from `COption` (or `&mut COption`) to `COption<&mut T::Target>`. + /// + /// Leaves the original `COption` in-place, creating a new one containing a mutable reference to + /// the inner type's `Deref::Target` type. + /// + /// # Examples + /// + /// ```ignore + /// #![feature(inner_deref)] + /// + /// let mut x: COption = COption::Some("hey".to_owned()); + /// assert_eq!(x.as_deref_mut().map(|x| { + /// x.make_ascii_uppercase(); + /// x + /// }), COption::Some("HEY".to_owned().as_mut_str())); + /// ``` + pub fn as_deref_mut(&mut self) -> COption<&mut T::Target> { + self.as_mut().map(|t| t.deref_mut()) + } +} + +impl COption> { + /// Transposes an `COption` of a [`Result`] into a [`Result`] of an `COption`. + /// + /// [`COption::None`] will be mapped to [`Ok`]`(`[`COption::None`]`)`. + /// [`COption::Some`]`(`[`Ok`]`(_))` and [`COption::Some`]`(`[`Err`]`(_))` will be mapped to + /// [`Ok`]`(`[`COption::Some`]`(_))` and [`Err`]`(_)`. + /// + /// [`COption::None`]: #variant.COption::None + /// [`Ok`]: ../../std/result/enum.Result.html#variant.Ok + /// [`COption::Some`]: #variant.COption::Some + /// [`Err`]: ../../std/result/enum.Result.html#variant.Err + /// + /// # Examples + /// + /// ```ignore + /// #[derive(Debug, Eq, PartialEq)] + /// struct COption::SomeErr; + /// + /// let x: Result, COption::SomeErr> = Ok(COption::Some(5)); + /// let y: COption> = COption::Some(Ok(5)); + /// assert_eq!(x, y.transpose()); + /// ``` + #[inline] + pub fn transpose(self) -> Result, E> { + match self { + COption::Some(Ok(x)) => Ok(COption::Some(x)), + COption::Some(Err(e)) => Err(e), + COption::None => Ok(COption::None), + } + } +} + +// This is a separate function to reduce the code size of .expect() itself. +#[inline(never)] +#[cold] +fn expect_failed(msg: &str) -> ! { + panic!("{}", msg) +} + +// // This is a separate function to reduce the code size of .expect_none() itself. +// #[inline(never)] +// #[cold] +// fn expect_none_failed(msg: &str, value: &dyn fmt::Debug) -> ! { +// panic!("{}: {:?}", msg, value) +// } + +///////////////////////////////////////////////////////////////////////////// +// Trait implementations +///////////////////////////////////////////////////////////////////////////// + +impl Clone for COption { + #[inline] + fn clone(&self) -> Self { + match self { + COption::Some(x) => COption::Some(x.clone()), + COption::None => COption::None, + } + } + + #[inline] + fn clone_from(&mut self, source: &Self) { + match (self, source) { + (COption::Some(to), COption::Some(from)) => to.clone_from(from), + (to, from) => *to = from.clone(), + } + } +} + +impl Default for COption { + /// Returns [`COption::None`][COption::COption::None]. + /// + /// # Examples + /// + /// ```ignore + /// let opt: COption = COption::default(); + /// assert!(opt.is_none()); + /// ``` + #[inline] + fn default() -> COption { + COption::None + } +} + +impl From for COption { + fn from(val: T) -> COption { + COption::Some(val) + } +} + +impl<'a, T> From<&'a COption> for COption<&'a T> { + fn from(o: &'a COption) -> COption<&'a T> { + o.as_ref() + } +} + +impl<'a, T> From<&'a mut COption> for COption<&'a mut T> { + fn from(o: &'a mut COption) -> COption<&'a mut T> { + o.as_mut() + } +} + +impl COption> { + /// Converts from `COption>` to `COption` + /// + /// # Examples + /// Basic usage: + /// ```ignore + /// #![feature(option_flattening)] + /// let x: COption> = COption::Some(COption::Some(6)); + /// assert_eq!(COption::Some(6), x.flatten()); + /// + /// let x: COption> = COption::Some(COption::None); + /// assert_eq!(COption::None, x.flatten()); + /// + /// let x: COption> = COption::None; + /// assert_eq!(COption::None, x.flatten()); + /// ``` + /// Flattening once only removes one level of nesting: + /// ```ignore + /// #![feature(option_flattening)] + /// let x: COption>> = COption::Some(COption::Some(COption::Some(6))); + /// assert_eq!(COption::Some(COption::Some(6)), x.flatten()); + /// assert_eq!(COption::Some(6), x.flatten().flatten()); + /// ``` + #[inline] + pub fn flatten(self) -> COption { + self.and_then(convert::identity) + } +} + +impl From> for COption { + fn from(option: Option) -> Self { + match option { + Some(value) => COption::Some(value), + None => COption::None, + } + } +} + +impl Into> for COption { + fn into(self) -> Option { + match self { + COption::Some(value) => Some(value), + COption::None => None, + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use solana_sdk::pubkey::Pubkey; + + #[test] + fn test_from_rust_option() { + let option = Some(99u64); + let c_option: COption = option.into(); + assert_eq!(c_option, COption::Some(99u64)); + let expected = c_option.into(); + assert_eq!(option, expected); + + let option = None; + let c_option: COption = option.into(); + assert_eq!(c_option, COption::None); + let expected = c_option.into(); + assert_eq!(option, expected); + } + + #[test] + fn test_coption_packing() { + // Solana Pubkey + let option_pubkey = COption::Some(Pubkey::new(&[2u8; 32])); + let expected_size = mem::size_of::() + mem::size_of::(); + let mut output = vec![0u8; expected_size]; + let mut cursor = 0; + option_pubkey.pack(&mut output, &mut cursor); + + let mut expected = vec![1u8]; + expected.extend_from_slice(&[2u8; 32]); + assert_eq!(output, expected); + + let mut cursor = 0; + let unpacked = COption::unpack_or(&expected, &mut cursor, "Error".to_string()).unwrap(); + assert_eq!(unpacked, option_pubkey); + + let option_pubkey: COption = COption::None; + let expected_size = mem::size_of::(); + let mut output = vec![0u8; expected_size]; + let mut cursor = 0; + option_pubkey.pack(&mut output, &mut cursor); + + let expected = vec![0u8]; + assert_eq!(output, expected); + + let mut cursor = 0; + let unpacked = COption::unpack_or(&expected, &mut cursor, "Error".to_string()).unwrap(); + assert_eq!(unpacked, option_pubkey); + + // u64 + let option_pubkey = COption::Some(99u64); + let expected_size = mem::size_of::() + mem::size_of::(); + let mut output = vec![0u8; expected_size]; + let mut cursor = 0; + option_pubkey.pack(&mut output, &mut cursor); + + let mut expected = vec![1u8]; + expected.extend_from_slice(&[99, 0, 0, 0, 0, 0, 0, 0]); + assert_eq!(output, expected); + + let mut cursor = 0; + let unpacked = COption::unpack_or(&expected, &mut cursor, "Error".to_string()).unwrap(); + assert_eq!(unpacked, option_pubkey); + + let option_pubkey: COption = COption::None; + let expected_size = mem::size_of::(); + let mut output = vec![0u8; expected_size]; + let mut cursor = 0; + option_pubkey.pack(&mut output, &mut cursor); + + let expected = vec![0u8]; + assert_eq!(output, expected); + + let mut cursor = 0; + let unpacked = COption::unpack_or(&expected, &mut cursor, "Error".to_string()).unwrap(); + assert_eq!(unpacked, option_pubkey); + } +} diff --git a/program/src/processor.rs b/program/src/processor.rs new file mode 100644 index 0000000..d8baa68 --- /dev/null +++ b/program/src/processor.rs @@ -0,0 +1,3128 @@ +//! Program state processor + +#![cfg(feature = "program")] + +use crate::{ + error::TokenError, + instruction::{is_valid_signer_index, AuthorityType, TokenInstruction}, + option::COption, + state::{self, Account, AccountState, IsInitialized, Mint, Multisig}, +}; +use num_traits::FromPrimitive; +use solana_sdk::{ + account_info::{next_account_info, AccountInfo}, + decode_error::DecodeError, + entrypoint::ProgramResult, + info, + program_error::{PrintProgramError, ProgramError}, + pubkey::Pubkey, +}; +use std::mem::size_of; + +/// Program state handler. +pub struct Processor {} +impl Processor { + /// Processes an [InitializeMint](enum.TokenInstruction.html) instruction. + pub fn process_initialize_mint( + accounts: &[AccountInfo], + decimals: u8, + mint_authority: COption, + freeze_authority: COption, + ) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let mint_info = next_account_info(account_info_iter)?; + + let mut mint_info_data = mint_info.data.borrow_mut(); + let mut mint: &mut Mint = state::unpack_unchecked(&mut mint_info_data)?; + if mint.is_initialized { + return Err(TokenError::AlreadyInUse.into()); + } + + if mint_authority.is_none() { + return Err(TokenError::OwnerRequiredIfNoInitialSupply.into()); + } + + mint.mint_authority = mint_authority; + mint.decimals = decimals; + mint.is_initialized = true; + mint.freeze_authority = freeze_authority; + + Ok(()) + } + + /// Processes an [InitializeAccount](enum.TokenInstruction.html) instruction. + pub fn process_initialize_account(accounts: &[AccountInfo]) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let new_account_info = next_account_info(account_info_iter)?; + let mint_info = next_account_info(account_info_iter)?; + let owner_info = next_account_info(account_info_iter)?; + + let mut new_account_data = new_account_info.data.borrow_mut(); + let mut account: &mut Account = state::unpack_unchecked(&mut new_account_data)?; + if account.is_initialized() { + return Err(TokenError::AlreadyInUse.into()); + } + + account.mint = *mint_info.key; + account.owner = *owner_info.key; + account.delegate = COption::None; + account.delegated_amount = 0; + account.state = AccountState::Initialized; + if *mint_info.key == crate::native_mint::id() { + account.is_native = true; + account.amount = new_account_info.lamports(); + } else { + account.is_native = false; + account.amount = 0; + }; + + Ok(()) + } + + /// Processes a [InitializeMultisig](enum.TokenInstruction.html) instruction. + pub fn process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let multisig_info = next_account_info(account_info_iter)?; + let mut multisig_account_data = multisig_info.data.borrow_mut(); + let mut multisig: &mut Multisig = state::unpack_unchecked(&mut multisig_account_data)?; + if multisig.is_initialized { + return Err(TokenError::AlreadyInUse.into()); + } + + let signer_infos = account_info_iter.as_slice(); + multisig.m = m; + multisig.n = signer_infos.len() as u8; + if !is_valid_signer_index(multisig.n as usize) { + return Err(TokenError::InvalidNumberOfProvidedSigners.into()); + } + if !is_valid_signer_index(multisig.m as usize) { + return Err(TokenError::InvalidNumberOfRequiredSigners.into()); + } + for (i, signer_info) in signer_infos.iter().enumerate() { + multisig.signers[i] = *signer_info.key; + } + multisig.is_initialized = true; + + Ok(()) + } + + /// Processes a [Transfer](enum.TokenInstruction.html) instruction. + pub fn process_transfer( + program_id: &Pubkey, + accounts: &[AccountInfo], + amount: u64, + ) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let source_account_info = next_account_info(account_info_iter)?; + let dest_account_info = next_account_info(account_info_iter)?; + let authority_info = next_account_info(account_info_iter)?; + + if source_account_info.key == dest_account_info.key { + return Ok(()); + } + + let mut source_data = source_account_info.data.borrow_mut(); + let mut source_account: &mut Account = state::unpack(&mut source_data)?; + let mut dest_data = dest_account_info.data.borrow_mut(); + let mut dest_account: &mut Account = state::unpack(&mut dest_data)?; + + if source_account.amount < amount { + return Err(TokenError::InsufficientFunds.into()); + } + if source_account.mint != dest_account.mint { + return Err(TokenError::MintMismatch.into()); + } + if source_account.is_frozen() || dest_account.is_frozen() { + return Err(TokenError::AccountFrozen.into()); + } + + match source_account.delegate { + COption::Some(ref delegate) if authority_info.key == delegate => { + Self::validate_owner( + program_id, + delegate, + authority_info, + account_info_iter.as_slice(), + )?; + if source_account.delegated_amount < amount { + return Err(TokenError::InsufficientFunds.into()); + } + source_account.delegated_amount -= amount; + if source_account.delegated_amount == 0 { + source_account.delegate = COption::None; + } + } + _ => Self::validate_owner( + program_id, + &source_account.owner, + authority_info, + account_info_iter.as_slice(), + )?, + }; + + source_account.amount -= amount; + dest_account.amount = dest_account + .amount + .checked_add(amount) + .ok_or(TokenError::Overflow)?; + + if source_account.is_native { + **source_account_info.lamports.borrow_mut() -= amount; + **dest_account_info.lamports.borrow_mut() += amount; + } + + Ok(()) + } + + /// Processes an [Approve](enum.TokenInstruction.html) instruction. + pub fn process_approve( + program_id: &Pubkey, + accounts: &[AccountInfo], + amount: u64, + ) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let source_account_info = next_account_info(account_info_iter)?; + + let mut source_data = source_account_info.data.borrow_mut(); + let mut source_account: &mut Account = state::unpack(&mut source_data)?; + let delegate_info = next_account_info(account_info_iter)?; + let owner_info = next_account_info(account_info_iter)?; + + if source_account.is_frozen() { + return Err(TokenError::AccountFrozen.into()); + } + + Self::validate_owner( + program_id, + &source_account.owner, + owner_info, + account_info_iter.as_slice(), + )?; + + source_account.delegate = COption::Some(*delegate_info.key); + source_account.delegated_amount = amount; + + Ok(()) + } + + /// Processes an [Revoke](enum.TokenInstruction.html) instruction. + pub fn process_revoke(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let source_account_info = next_account_info(account_info_iter)?; + + let mut source_data = source_account_info.data.borrow_mut(); + let mut source_account: &mut Account = state::unpack(&mut source_data)?; + let owner_info = next_account_info(account_info_iter)?; + + if source_account.is_frozen() { + return Err(TokenError::AccountFrozen.into()); + } + + Self::validate_owner( + program_id, + &source_account.owner, + owner_info, + account_info_iter.as_slice(), + )?; + + source_account.delegate = COption::None; + source_account.delegated_amount = 0; + + Ok(()) + } + + /// Processes a [SetAuthority](enum.TokenInstruction.html) instruction. + pub fn process_set_authority( + program_id: &Pubkey, + accounts: &[AccountInfo], + authority_type: AuthorityType, + new_authority: COption, + ) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let account_info = next_account_info(account_info_iter)?; + let authority_info = next_account_info(account_info_iter)?; + + if account_info.data_len() == size_of::() { + let mut account_data = account_info.data.borrow_mut(); + let mut account: &mut Account = state::unpack(&mut account_data)?; + + if account.is_frozen() { + return Err(TokenError::AccountFrozen.into()); + } + + match authority_type { + AuthorityType::AccountHolder => { + Self::validate_owner( + program_id, + &account.owner, + authority_info, + account_info_iter.as_slice(), + )?; + + if let COption::Some(authority) = new_authority { + account.owner = authority; + } else { + return Err(TokenError::InvalidInstruction.into()); + } + } + AuthorityType::CloseAccount => { + let authority = account.close_authority.unwrap_or(account.owner); + Self::validate_owner( + program_id, + &authority, + authority_info, + account_info_iter.as_slice(), + )?; + account.close_authority = new_authority; + } + _ => { + return Err(TokenError::AuthorityTypeNotSupported.into()); + } + } + } else if account_info.data_len() == size_of::() { + let mut account_data = account_info.data.borrow_mut(); + let mut mint: &mut Mint = state::unpack(&mut account_data)?; + + match authority_type { + AuthorityType::MintTokens => { + // Once a mint's supply is fixed, it cannot be undone by setting a new + // mint_authority + let mint_authority = mint + .mint_authority + .ok_or(Into::::into(TokenError::FixedSupply))?; + Self::validate_owner( + program_id, + &mint_authority, + authority_info, + account_info_iter.as_slice(), + )?; + mint.mint_authority = new_authority; + } + AuthorityType::FreezeAccount => { + // Once a mint's freeze authority is disabled, it cannot be re-enabled by + // setting a new freeze_authority + let freeze_authority = mint + .freeze_authority + .ok_or(Into::::into(TokenError::MintCannotFreeze))?; + Self::validate_owner( + program_id, + &freeze_authority, + authority_info, + account_info_iter.as_slice(), + )?; + mint.freeze_authority = new_authority; + } + _ => { + return Err(TokenError::AuthorityTypeNotSupported.into()); + } + } + } else { + return Err(ProgramError::InvalidArgument); + } + + Ok(()) + } + + /// Processes a [MintTo](enum.TokenInstruction.html) instruction. + pub fn process_mint_to( + program_id: &Pubkey, + accounts: &[AccountInfo], + amount: u64, + ) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let mint_info = next_account_info(account_info_iter)?; + let dest_account_info = next_account_info(account_info_iter)?; + let owner_info = next_account_info(account_info_iter)?; + + let mut dest_account_data = dest_account_info.data.borrow_mut(); + let mut dest_account: &mut Account = state::unpack(&mut dest_account_data)?; + + if dest_account.is_frozen() { + return Err(TokenError::AccountFrozen.into()); + } + + if dest_account.is_native { + return Err(TokenError::NativeNotSupported.into()); + } + if mint_info.key != &dest_account.mint { + return Err(TokenError::MintMismatch.into()); + } + + let mut mint_info_data = mint_info.data.borrow_mut(); + let mint: &mut Mint = state::unpack(&mut mint_info_data)?; + + match mint.mint_authority { + COption::Some(mint_authority) => { + Self::validate_owner( + program_id, + &mint_authority, + owner_info, + account_info_iter.as_slice(), + )?; + } + COption::None => { + return Err(TokenError::FixedSupply.into()); + } + } + + dest_account.amount = dest_account + .amount + .checked_add(amount) + .ok_or(TokenError::Overflow)?; + + Ok(()) + } + + /// Processes a [Burn](enum.TokenInstruction.html) instruction. + pub fn process_burn( + program_id: &Pubkey, + accounts: &[AccountInfo], + amount: u64, + ) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let source_account_info = next_account_info(account_info_iter)?; + let authority_info = next_account_info(account_info_iter)?; + + let mut source_data = source_account_info.data.borrow_mut(); + let source_account: &mut Account = state::unpack(&mut source_data)?; + + if source_account.is_native { + return Err(TokenError::NativeNotSupported.into()); + } + if source_account.amount < amount { + return Err(TokenError::InsufficientFunds.into()); + } + if source_account.is_frozen() { + return Err(TokenError::AccountFrozen.into()); + } + + match source_account.delegate { + COption::Some(ref delegate) if authority_info.key == delegate => { + Self::validate_owner( + program_id, + delegate, + authority_info, + account_info_iter.as_slice(), + )?; + + if source_account.delegated_amount < amount { + return Err(TokenError::InsufficientFunds.into()); + } + source_account.delegated_amount -= amount; + if source_account.delegated_amount == 0 { + source_account.delegate = COption::None; + } + } + _ => Self::validate_owner( + program_id, + &source_account.owner, + authority_info, + account_info_iter.as_slice(), + )?, + } + + source_account.amount -= amount; + + Ok(()) + } + + /// Processes a [CloseAccount](enum.TokenInstruction.html) instruction. + pub fn process_close_account(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let source_account_info = next_account_info(account_info_iter)?; + let dest_account_info = next_account_info(account_info_iter)?; + let authority_info = next_account_info(account_info_iter)?; + + let mut source_data = source_account_info.data.borrow_mut(); + let source_account: &mut Account = state::unpack(&mut source_data)?; + + if !source_account.is_native && source_account.amount != 0 { + return Err(TokenError::NonNativeHasBalance.into()); + } + + let authority = source_account + .close_authority + .unwrap_or(source_account.owner); + Self::validate_owner( + program_id, + &authority, + authority_info, + account_info_iter.as_slice(), + )?; + + **dest_account_info.lamports.borrow_mut() += source_account_info.lamports(); + **source_account_info.lamports.borrow_mut() = 0; + source_account.amount = 0; + + Ok(()) + } + + /// Processes a [FreezeAccount](enum.TokenInstruction.html) or a + /// [ThawAccount](enum.TokenInstruction.html) instruction. + pub fn process_toggle_freeze_account( + program_id: &Pubkey, + accounts: &[AccountInfo], + freeze: bool, + ) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let source_account_info = next_account_info(account_info_iter)?; + let mint_info = next_account_info(account_info_iter)?; + let authority_info = next_account_info(account_info_iter)?; + + let mut source_data = source_account_info.data.borrow_mut(); + let source_account: &mut Account = state::unpack(&mut source_data)?; + + if source_account.is_native { + return Err(TokenError::NativeNotSupported.into()); + } + if mint_info.key != &source_account.mint { + return Err(TokenError::MintMismatch.into()); + } + if freeze && source_account.is_frozen() || !freeze && !source_account.is_frozen() { + return Err(TokenError::InvalidState.into()); + } + + let mut mint_info_data = mint_info.data.borrow_mut(); + let mint: &mut Mint = state::unpack(&mut mint_info_data)?; + + match mint.freeze_authority { + COption::Some(authority) => { + Self::validate_owner( + program_id, + &authority, + authority_info, + account_info_iter.as_slice(), + )?; + } + COption::None => { + return Err(TokenError::MintCannotFreeze.into()); + } + } + + source_account.state = if freeze { + AccountState::Frozen + } else { + AccountState::Initialized + }; + + Ok(()) + } + + /// Processes an [Instruction](enum.Instruction.html). + pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult { + let instruction = TokenInstruction::unpack(input)?; + + match instruction { + TokenInstruction::InitializeMint { + decimals, + mint_authority, + freeze_authority, + } => { + info!("Instruction: InitializeMint"); + Self::process_initialize_mint(accounts, decimals, mint_authority, freeze_authority) + } + TokenInstruction::InitializeAccount => { + info!("Instruction: InitializeAccount"); + Self::process_initialize_account(accounts) + } + TokenInstruction::InitializeMultisig { m } => { + info!("Instruction: InitializeMultisig"); + Self::process_initialize_multisig(accounts, m) + } + TokenInstruction::Transfer { amount } => { + info!("Instruction: Transfer"); + Self::process_transfer(program_id, accounts, amount) + } + TokenInstruction::Approve { amount } => { + info!("Instruction: Approve"); + Self::process_approve(program_id, accounts, amount) + } + TokenInstruction::Revoke => { + info!("Instruction: Revoke"); + Self::process_revoke(program_id, accounts) + } + TokenInstruction::SetAuthority { + authority_type, + new_authority, + } => { + info!("Instruction: SetAuthority"); + Self::process_set_authority(program_id, accounts, authority_type, new_authority) + } + TokenInstruction::MintTo { amount } => { + info!("Instruction: MintTo"); + Self::process_mint_to(program_id, accounts, amount) + } + TokenInstruction::Burn { amount } => { + info!("Instruction: Burn"); + Self::process_burn(program_id, accounts, amount) + } + TokenInstruction::CloseAccount => { + info!("Instruction: CloseAccount"); + Self::process_close_account(program_id, accounts) + } + TokenInstruction::FreezeAccount => { + info!("Instruction: FreezeAccount"); + Self::process_toggle_freeze_account(program_id, accounts, true) + } + TokenInstruction::ThawAccount => { + info!("Instruction: FreezeAccount"); + Self::process_toggle_freeze_account(program_id, accounts, false) + } + } + } + + /// Validates owner(s) are present + pub fn validate_owner( + program_id: &Pubkey, + expected_owner: &Pubkey, + owner_account_info: &AccountInfo, + signers: &[AccountInfo], + ) -> ProgramResult { + if expected_owner != owner_account_info.key { + return Err(TokenError::OwnerMismatch.into()); + } + if program_id == owner_account_info.owner + && owner_account_info.data_len() == std::mem::size_of::() + { + let mut owner_data = owner_account_info.data.borrow_mut(); + let multisig: &mut Multisig = state::unpack(&mut owner_data)?; + let mut num_signers = 0; + for signer in signers.iter() { + if multisig.signers[0..multisig.n as usize].contains(signer.key) { + if !signer.is_signer { + return Err(ProgramError::MissingRequiredSignature); + } + num_signers += 1; + } + } + if num_signers < multisig.m { + return Err(ProgramError::MissingRequiredSignature); + } + } else if !owner_account_info.is_signer { + return Err(ProgramError::MissingRequiredSignature); + } + Ok(()) + } +} + +impl PrintProgramError for TokenError { + fn print(&self) + where + E: 'static + std::error::Error + DecodeError + PrintProgramError + FromPrimitive, + { + match self { + TokenError::InsufficientFunds => info!("Error: insufficient funds"), + TokenError::MintMismatch => info!("Error: Account not associated with this Mint"), + TokenError::OwnerMismatch => info!("Error: owner does not match"), + TokenError::FixedSupply => info!("Error: the total supply of this token is fixed"), + TokenError::AlreadyInUse => info!("Error: account or token already in use"), + TokenError::OwnerRequiredIfNoInitialSupply => { + info!("Error: An owner is required if supply is zero") + } + TokenError::InvalidNumberOfProvidedSigners => { + info!("Error: Invalid number of provided signers") + } + TokenError::InvalidNumberOfRequiredSigners => { + info!("Error: Invalid number of required signers") + } + TokenError::UninitializedState => info!("Error: State is uninitialized"), + TokenError::NativeNotSupported => { + info!("Error: Instruction does not support native tokens") + } + TokenError::NonNativeHasBalance => { + info!("Error: Non-native account can only be closed if its balance is zero") + } + TokenError::InvalidInstruction => info!("Error: Invalid instruction"), + TokenError::InvalidState => info!("Error: Invalid account state for operation"), + TokenError::Overflow => info!("Error: Operation overflowed"), + TokenError::AuthorityTypeNotSupported => { + info!("Error: Account does not support specified authority type") + } + TokenError::MintCannotFreeze => info!("Error: This token mint cannot freeze accounts"), + TokenError::AccountFrozen => info!("Error: Account is frozen"), + } + } +} + +// Pull in syscall stubs when building for non-BPF targets +#[cfg(not(target_arch = "bpf"))] +solana_sdk::program_stubs!(); + +#[cfg(test)] +mod tests { + use super::*; + use crate::instruction::{ + approve, burn, close_account, freeze_account, initialize_account, initialize_mint, + initialize_multisig, mint_to, revoke, set_authority, thaw_account, transfer, MAX_SIGNERS, + }; + use solana_sdk::{ + account::Account as SolanaAccount, account_info::create_is_signer_account_infos, + clock::Epoch, instruction::Instruction, + }; + + fn pubkey_rand() -> Pubkey { + Pubkey::new(&rand::random::<[u8; 32]>()) + } + + fn do_process_instruction( + instruction: Instruction, + accounts: Vec<&mut SolanaAccount>, + ) -> ProgramResult { + let mut meta = instruction + .accounts + .iter() + .zip(accounts) + .map(|(account_meta, account)| (&account_meta.pubkey, account_meta.is_signer, account)) + .collect::>(); + + let account_infos = create_is_signer_account_infos(&mut meta); + Processor::process(&instruction.program_id, &account_infos, &instruction.data) + } + + fn return_token_error_as_program_error() -> ProgramError { + TokenError::MintMismatch.into() + } + + #[test] + fn test_print_error() { + let error = return_token_error_as_program_error(); + error.print::(); + } + + #[test] + #[should_panic(expected = "Custom(1)")] + fn test_error_unwrap() { + Err::<(), ProgramError>(return_token_error_as_program_error()).unwrap(); + } + + #[test] + fn test_unique_account_sizes() { + assert_ne!(size_of::(), 0); + assert_ne!(size_of::(), size_of::()); + assert_ne!(size_of::(), size_of::()); + assert_ne!(size_of::(), 0); + assert_ne!(size_of::(), size_of::()); + assert_ne!(size_of::(), 0); + } + + #[test] + fn test_initialize_mint() { + let program_id = pubkey_rand(); + let owner_key = pubkey_rand(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mint2_key = pubkey_rand(); + let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); + + // mint_authority not provided + let mut instruction = initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(); + instruction.data = TokenInstruction::InitializeMint { + mint_authority: COption::None, + freeze_authority: COption::None, + decimals: 2, + } + .pack() + .unwrap(); + assert_eq!( + Err(TokenError::OwnerRequiredIfNoInitialSupply.into()), + do_process_instruction(instruction, vec![&mut mint_account]) + ); + + // create new mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + // create twice + assert_eq!( + Err(TokenError::AlreadyInUse.into()), + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2,).unwrap(), + vec![&mut mint_account] + ) + ); + + // create another mint that can freeze + do_process_instruction( + initialize_mint(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(), + vec![&mut mint2_account], + ) + .unwrap(); + let mint2: &mut Mint = state::unpack(&mut mint2_account.data).unwrap(); + assert_eq!(mint2.freeze_authority, COption::Some(owner_key)); + } + + #[test] + fn test_initialize_mint_account() { + let program_id = pubkey_rand(); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create twice + assert_eq!( + Err(TokenError::AlreadyInUse.into()), + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + ); + } + + #[test] + fn test_transfer() { + let program_id = pubkey_rand(); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let account3_key = pubkey_rand(); + let mut account3_account = SolanaAccount::new(0, size_of::(), &program_id); + let delegate_key = pubkey_rand(); + let mut delegate_account = SolanaAccount::default(); + let mismatch_key = pubkey_rand(); + let mut mismatch_account = SolanaAccount::new(0, size_of::(), &program_id); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = pubkey_rand(); + let mut owner2_account = SolanaAccount::default(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mint2_key = pubkey_rand(); + let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), + vec![&mut account2_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), + vec![&mut account3_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create mismatch account + do_process_instruction( + initialize_account(&program_id, &mismatch_key, &mint2_key, &owner_key).unwrap(), + vec![ + &mut mismatch_account, + &mut mint2_account, + &mut owner_account, + ], + ) + .unwrap(); + + // create new mint & mint to account + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + + // missing signer + let mut instruction = transfer( + &program_id, + &account_key, + &account2_key, + &owner_key, + &[], + 1000, + ) + .unwrap(); + instruction.accounts[2].is_signer = false; + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + do_process_instruction( + instruction, + vec![ + &mut account_account, + &mut account2_account, + &mut owner_account, + ], + ) + ); + + // mismatch mint + assert_eq!( + Err(TokenError::MintMismatch.into()), + do_process_instruction( + transfer( + &program_id, + &account_key, + &mismatch_key, + &owner_key, + &[], + 1000 + ) + .unwrap(), + vec![ + &mut account_account, + &mut mismatch_account, + &mut owner_account, + ], + ) + ); + + // missing owner + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &owner2_key, + &[], + 1000 + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner2_account, + ], + ) + ); + + // transfer + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &owner_key, + &[], + 1000, + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner_account, + ], + ) + .unwrap(); + + // insufficient funds + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + do_process_instruction( + transfer(&program_id, &account_key, &account2_key, &owner_key, &[], 1).unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner_account, + ], + ) + ); + + // transfer half back + do_process_instruction( + transfer( + &program_id, + &account2_key, + &account_key, + &owner_key, + &[], + 500, + ) + .unwrap(), + vec![ + &mut account2_account, + &mut account_account, + &mut owner_account, + ], + ) + .unwrap(); + + // transfer rest + do_process_instruction( + transfer( + &program_id, + &account2_key, + &account_key, + &owner_key, + &[], + 500, + ) + .unwrap(), + vec![ + &mut account2_account, + &mut account_account, + &mut owner_account, + ], + ) + .unwrap(); + + // transfer to self + { + let instruction = transfer( + &program_id, + &account_key, + &account_key, + &owner_key, + &[], + 500, + ) + .unwrap(); + let account_account_info = AccountInfo::from(( + &instruction.accounts[0].pubkey, + instruction.accounts[0].is_signer, + &mut account_account, + )); + let owner_account_info = AccountInfo::from(( + &instruction.accounts[2].pubkey, + instruction.accounts[2].is_signer, + &mut owner_account, + )); + Processor::process( + &instruction.program_id, + &[ + account_account_info.clone(), + account_account_info, + owner_account_info, + ], + &instruction.data, + ) + .unwrap() + } + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + assert_eq!(account.amount, 1000); + + // insufficient funds + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + do_process_instruction( + transfer(&program_id, &account2_key, &account_key, &owner_key, &[], 1).unwrap(), + vec![ + &mut account2_account, + &mut account_account, + &mut owner_account, + ], + ) + ); + + // approve delegate + do_process_instruction( + approve( + &program_id, + &account_key, + &delegate_key, + &owner_key, + &[], + 100, + ) + .unwrap(), + vec![ + &mut account_account, + &mut delegate_account, + &mut owner_account, + ], + ) + .unwrap(); + + // transfer via delegate + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &delegate_key, + &[], + 100, + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut delegate_account, + ], + ) + .unwrap(); + + // insufficient funds approved via delegate + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &delegate_key, + &[], + 100 + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut delegate_account, + ], + ) + ); + + // transfer rest + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &owner_key, + &[], + 900, + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner_account, + ], + ) + .unwrap(); + + // approve delegate + do_process_instruction( + approve( + &program_id, + &account_key, + &delegate_key, + &owner_key, + &[], + 100, + ) + .unwrap(), + vec![ + &mut account_account, + &mut delegate_account, + &mut owner_account, + ], + ) + .unwrap(); + + // insufficient funds in source account via delegate + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &delegate_key, + &[], + 100 + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut delegate_account, + ], + ) + ); + } + + #[test] + fn test_mintable_token_with_zero_supply() { + let program_id = pubkey_rand(); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut owner_account, &mut mint_account], + ) + .unwrap(); + + // create mint-able token with zero supply + let decimals = 2; + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, decimals).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + let mint: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); + assert_eq!( + *mint, + Mint { + mint_authority: COption::Some(owner_key), + decimals, + is_initialized: true, + freeze_authority: COption::None, + } + ); + + // mint to + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 42).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + + let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); + let dest_account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + assert_eq!(dest_account.amount, 42); + } + + #[test] + fn test_approve() { + let program_id = pubkey_rand(); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let delegate_key = pubkey_rand(); + let mut delegate_account = SolanaAccount::default(); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = pubkey_rand(); + let mut owner2_account = SolanaAccount::default(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut owner_account, &mut mint_account], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), + vec![&mut account2_account, &mut owner_account, &mut mint_account], + ) + .unwrap(); + + // create new mint & mint to account + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut account_account], + ) + .unwrap(); + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + + // missing signer + let mut instruction = approve( + &program_id, + &account_key, + &delegate_key, + &owner_key, + &[], + 100, + ) + .unwrap(); + instruction.accounts[2].is_signer = false; + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + do_process_instruction( + instruction, + vec![ + &mut account_account, + &mut delegate_account, + &mut owner_account, + ], + ) + ); + + // no owner + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + approve( + &program_id, + &account_key, + &delegate_key, + &owner2_key, + &[], + 100 + ) + .unwrap(), + vec![ + &mut account_account, + &mut delegate_account, + &mut owner2_account, + ], + ) + ); + + // approve delegate + do_process_instruction( + approve( + &program_id, + &account_key, + &delegate_key, + &owner_key, + &[], + 100, + ) + .unwrap(), + vec![ + &mut account_account, + &mut delegate_account, + &mut owner_account, + ], + ) + .unwrap(); + + // revoke delegate + do_process_instruction( + revoke(&program_id, &account_key, &owner_key, &[]).unwrap(), + vec![&mut account_account, &mut owner_account], + ) + .unwrap(); + } + + #[test] + fn test_set_authority() { + let program_id = pubkey_rand(); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = pubkey_rand(); + let mut owner2_account = SolanaAccount::default(); + let owner3_key = pubkey_rand(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mint2_key = pubkey_rand(); + let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); + + // invalid account + assert_eq!( + Err(TokenError::UninitializedState.into()), + do_process_instruction( + set_authority( + &program_id, + &account_key, + Some(&owner2_key), + AuthorityType::AccountHolder, + &owner_key, + &[] + ) + .unwrap(), + vec![&mut account_account, &mut owner_account], + ) + ); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint2_key, &owner_key).unwrap(), + vec![ + &mut account2_account, + &mut mint2_account, + &mut owner_account, + ], + ) + .unwrap(); + + // missing owner + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + set_authority( + &program_id, + &account_key, + Some(&owner_key), + AuthorityType::AccountHolder, + &owner2_key, + &[] + ) + .unwrap(), + vec![&mut account_account, &mut owner2_account], + ) + ); + + // owner did not sign + let mut instruction = set_authority( + &program_id, + &account_key, + Some(&owner2_key), + AuthorityType::AccountHolder, + &owner_key, + &[], + ) + .unwrap(); + instruction.accounts[1].is_signer = false; + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + do_process_instruction(instruction, vec![&mut account_account, &mut owner_account,],) + ); + + // wrong authority type + assert_eq!( + Err(TokenError::AuthorityTypeNotSupported.into()), + do_process_instruction( + set_authority( + &program_id, + &account_key, + Some(&owner2_key), + AuthorityType::FreezeAccount, + &owner_key, + &[], + ) + .unwrap(), + vec![&mut account_account, &mut owner_account], + ) + ); + + // account owner may not be set to None + assert_eq!( + Err(TokenError::InvalidInstruction.into()), + do_process_instruction( + set_authority( + &program_id, + &account_key, + None, + AuthorityType::AccountHolder, + &owner_key, + &[], + ) + .unwrap(), + vec![&mut account_account, &mut owner_account], + ) + ); + + // set owner + do_process_instruction( + set_authority( + &program_id, + &account_key, + Some(&owner2_key), + AuthorityType::AccountHolder, + &owner_key, + &[], + ) + .unwrap(), + vec![&mut account_account, &mut owner_account], + ) + .unwrap(); + + // set close_authority + do_process_instruction( + set_authority( + &program_id, + &account_key, + Some(&owner2_key), + AuthorityType::CloseAccount, + &owner2_key, + &[], + ) + .unwrap(), + vec![&mut account_account, &mut owner2_account], + ) + .unwrap(); + + // close_authority may be set to None + do_process_instruction( + set_authority( + &program_id, + &account_key, + None, + AuthorityType::CloseAccount, + &owner2_key, + &[], + ) + .unwrap(), + vec![&mut account_account, &mut owner2_account], + ) + .unwrap(); + + // create new mint with owner + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + // wrong owner + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + set_authority( + &program_id, + &mint_key, + Some(&owner3_key), + AuthorityType::MintTokens, + &owner2_key, + &[] + ) + .unwrap(), + vec![&mut mint_account, &mut owner2_account], + ) + ); + + // owner did not sign + let mut instruction = set_authority( + &program_id, + &mint_key, + Some(&owner2_key), + AuthorityType::MintTokens, + &owner_key, + &[], + ) + .unwrap(); + instruction.accounts[1].is_signer = false; + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + do_process_instruction(instruction, vec![&mut mint_account, &mut owner_account],) + ); + + // cannot freeze + assert_eq!( + Err(TokenError::MintCannotFreeze.into()), + do_process_instruction( + set_authority( + &program_id, + &mint_key, + Some(&owner2_key), + AuthorityType::FreezeAccount, + &owner_key, + &[], + ) + .unwrap(), + vec![&mut mint_account, &mut owner_account], + ) + ); + + // set owner + do_process_instruction( + set_authority( + &program_id, + &mint_key, + Some(&owner2_key), + AuthorityType::MintTokens, + &owner_key, + &[], + ) + .unwrap(), + vec![&mut mint_account, &mut owner_account], + ) + .unwrap(); + + // set owner to None + do_process_instruction( + set_authority( + &program_id, + &mint_key, + None, + AuthorityType::MintTokens, + &owner2_key, + &[], + ) + .unwrap(), + vec![&mut mint_account, &mut owner2_account], + ) + .unwrap(); + + // test unsetting mint_authority is one-way operation + assert_eq!( + Err(TokenError::FixedSupply.into()), + do_process_instruction( + set_authority( + &program_id, + &mint2_key, + Some(&owner2_key), + AuthorityType::MintTokens, + &owner_key, + &[] + ) + .unwrap(), + vec![&mut mint_account, &mut owner_account], + ) + ); + + // create mint with owner and freeze_authority + do_process_instruction( + initialize_mint(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(), + vec![&mut mint2_account], + ) + .unwrap(); + + // set freeze_authority + do_process_instruction( + set_authority( + &program_id, + &mint2_key, + Some(&owner2_key), + AuthorityType::FreezeAccount, + &owner_key, + &[], + ) + .unwrap(), + vec![&mut mint2_account, &mut owner_account], + ) + .unwrap(); + + // test unsetting freeze_authority is one-way operation + do_process_instruction( + set_authority( + &program_id, + &mint2_key, + None, + AuthorityType::FreezeAccount, + &owner2_key, + &[], + ) + .unwrap(), + vec![&mut mint2_account, &mut owner2_account], + ) + .unwrap(); + + assert_eq!( + Err(TokenError::MintCannotFreeze.into()), + do_process_instruction( + set_authority( + &program_id, + &mint2_key, + Some(&owner2_key), + AuthorityType::FreezeAccount, + &owner_key, + &[], + ) + .unwrap(), + vec![&mut mint2_account, &mut owner2_account], + ) + ); + } + + #[test] + fn test_mint_to() { + let program_id = pubkey_rand(); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let account3_key = pubkey_rand(); + let mut account3_account = SolanaAccount::new(0, size_of::(), &program_id); + let mismatch_key = pubkey_rand(); + let mut mismatch_account = SolanaAccount::new(0, size_of::(), &program_id); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = pubkey_rand(); + let mut owner2_account = SolanaAccount::default(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mint2_key = pubkey_rand(); + let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); + let uninitialized_key = pubkey_rand(); + let mut uninitialized_account = SolanaAccount::new(0, size_of::(), &program_id); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), + vec![&mut account2_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), + vec![&mut account3_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create mismatch account + do_process_instruction( + initialize_account(&program_id, &mismatch_key, &mint2_key, &owner_key).unwrap(), + vec![ + &mut mismatch_account, + &mut mint2_account, + &mut owner_account, + ], + ) + .unwrap(); + + // create new mint with owner + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + // mint to + do_process_instruction( + mint_to(&program_id, &mint_key, &account2_key, &owner_key, &[], 42).unwrap(), + vec![&mut mint_account, &mut account2_account, &mut owner_account], + ) + .unwrap(); + + let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); + let dest_account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); + assert_eq!(dest_account.amount, 42); + + // missing signer + let mut instruction = + mint_to(&program_id, &mint_key, &account2_key, &owner_key, &[], 42).unwrap(); + instruction.accounts[2].is_signer = false; + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + do_process_instruction( + instruction, + vec![&mut mint_account, &mut account2_account, &mut owner_account], + ) + ); + + // mismatch account + assert_eq!( + Err(TokenError::MintMismatch.into()), + do_process_instruction( + mint_to(&program_id, &mint_key, &mismatch_key, &owner_key, &[], 42).unwrap(), + vec![&mut mint_account, &mut mismatch_account, &mut owner_account], + ) + ); + + // missing owner + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + mint_to(&program_id, &mint_key, &account2_key, &owner2_key, &[], 42).unwrap(), + vec![ + &mut mint_account, + &mut account2_account, + &mut owner2_account, + ], + ) + ); + + // uninitialized destination account + assert_eq!( + Err(TokenError::UninitializedState.into()), + do_process_instruction( + mint_to( + &program_id, + &mint_key, + &uninitialized_key, + &owner_key, + &[], + 42 + ) + .unwrap(), + vec![ + &mut mint_account, + &mut uninitialized_account, + &mut owner_account, + ], + ) + ); + + // unset mint_authority and test minting fails + do_process_instruction( + set_authority( + &program_id, + &mint_key, + None, + AuthorityType::MintTokens, + &owner_key, + &[], + ) + .unwrap(), + vec![&mut mint_account, &mut owner_account], + ) + .unwrap(); + assert_eq!( + Err(TokenError::FixedSupply.into()), + do_process_instruction( + mint_to(&program_id, &mint_key, &account2_key, &owner_key, &[], 42).unwrap(), + vec![&mut mint_account, &mut account2_account, &mut owner_account], + ) + ); + } + + #[test] + fn test_burn() { + let program_id = pubkey_rand(); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let account3_key = pubkey_rand(); + let mut account3_account = SolanaAccount::new(0, size_of::(), &program_id); + let delegate_key = pubkey_rand(); + let mut delegate_account = SolanaAccount::default(); + let mismatch_key = pubkey_rand(); + let mut mismatch_account = SolanaAccount::new(0, size_of::(), &program_id); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = pubkey_rand(); + let mut owner2_account = SolanaAccount::default(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mint2_key = pubkey_rand(); + let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), + vec![&mut account2_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), + vec![&mut account3_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create mismatch account + do_process_instruction( + initialize_account(&program_id, &mismatch_key, &mint2_key, &owner_key).unwrap(), + vec![ + &mut mismatch_account, + &mut mint2_account, + &mut owner_account, + ], + ) + .unwrap(); + + // create new mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + + // missing signer + let mut instruction = burn(&program_id, &account_key, &delegate_key, &[], 42).unwrap(); + instruction.accounts[1].is_signer = false; + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + instruction, + vec![&mut account_account, &mut delegate_account], + ) + ); + + // missing owner + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + burn(&program_id, &account_key, &owner2_key, &[], 42).unwrap(), + vec![&mut account_account, &mut owner2_account], + ) + ); + + // burn + do_process_instruction( + burn(&program_id, &account_key, &owner_key, &[], 42).unwrap(), + vec![&mut account_account, &mut owner_account], + ) + .unwrap(); + + let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + assert_eq!(account.amount, 1000 - 42); + + // insufficient funds + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + do_process_instruction( + burn(&program_id, &account_key, &owner_key, &[], 100_000_000).unwrap(), + vec![&mut account_account, &mut owner_account], + ) + ); + + // approve delegate + do_process_instruction( + approve( + &program_id, + &account_key, + &delegate_key, + &owner_key, + &[], + 84, + ) + .unwrap(), + vec![ + &mut account_account, + &mut delegate_account, + &mut owner_account, + ], + ) + .unwrap(); + + // not a delegate of source account + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + do_process_instruction( + burn(&program_id, &account_key, &owner_key, &[], 100_000_000).unwrap(), + vec![&mut account_account, &mut owner_account], + ) + ); + + // burn via delegate + do_process_instruction( + burn(&program_id, &account_key, &delegate_key, &[], 84).unwrap(), + vec![&mut account_account, &mut delegate_account], + ) + .unwrap(); + + // match + let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + assert_eq!(account.amount, 1000 - 42 - 84); + + // insufficient funds approved via delegate + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + burn(&program_id, &account_key, &delegate_key, &[], 100).unwrap(), + vec![&mut account_account, &mut delegate_account], + ) + ); + } + + #[test] + fn test_multisig() { + let program_id = pubkey_rand(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let account_key = pubkey_rand(); + let mut account = SolanaAccount::new(0, size_of::(), &program_id); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let multisig_key = pubkey_rand(); + let mut multisig_account = SolanaAccount::new(0, size_of::(), &program_id); + let multisig_delegate_key = pubkey_rand(); + let mut multisig_delegate_account = + SolanaAccount::new(0, size_of::(), &program_id); + let signer_keys = vec![pubkey_rand(); MAX_SIGNERS]; + let signer_key_refs: Vec<&Pubkey> = signer_keys.iter().map(|key| key).collect(); + let mut signer_accounts = vec![SolanaAccount::new(0, 0, &program_id); MAX_SIGNERS]; + + // single signer + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(), + vec![ + &mut multisig_account, + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // multiple signer + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + initialize_multisig( + &program_id, + &multisig_delegate_key, + &signer_key_refs, + MAX_SIGNERS as u8, + ) + .unwrap(), + vec![ + &mut multisig_delegate_account, + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // create account with multisig owner + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &multisig_key).unwrap(), + vec![&mut account, &mut mint_account, &mut multisig_account], + ) + .unwrap(); + + // create another account with multisig owner + do_process_instruction( + initialize_account( + &program_id, + &account2_key, + &mint_key, + &multisig_delegate_key, + ) + .unwrap(), + vec![ + &mut account2_account, + &mut mint_account, + &mut multisig_account, + ], + ) + .unwrap(); + + // create new mint with multisig owner + do_process_instruction( + initialize_mint(&program_id, &mint_key, &multisig_key, None, 2).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + mint_to( + &program_id, + &mint_key, + &account_key, + &multisig_key, + &[&signer_keys[0]], + 1000, + ) + .unwrap(), + vec![ + &mut mint_account, + &mut account, + &mut multisig_account, + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // approve + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + approve( + &program_id, + &account_key, + &multisig_delegate_key, + &multisig_key, + &[&signer_keys[0]], + 100, + ) + .unwrap(), + vec![ + &mut account, + &mut multisig_delegate_account, + &mut multisig_account, + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // transfer + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &multisig_key, + &[&signer_keys[0]], + 42, + ) + .unwrap(), + vec![ + &mut account, + &mut account2_account, + &mut multisig_account, + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // transfer via delegate + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &multisig_delegate_key, + &signer_key_refs, + 42, + ) + .unwrap(), + vec![ + &mut account, + &mut account2_account, + &mut multisig_delegate_account, + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // mint to + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + mint_to( + &program_id, + &mint_key, + &account2_key, + &multisig_key, + &[&signer_keys[0]], + 42, + ) + .unwrap(), + vec![ + &mut mint_account, + &mut account2_account, + &mut multisig_account, + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // burn + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + burn( + &program_id, + &account_key, + &multisig_key, + &[&signer_keys[0]], + 42, + ) + .unwrap(), + vec![ + &mut account, + &mut multisig_account, + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // burn via delegate + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + burn( + &program_id, + &account_key, + &multisig_delegate_key, + &signer_key_refs, + 42, + ) + .unwrap(), + vec![ + &mut account, + &mut multisig_delegate_account, + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // freeze account + let account3_key = pubkey_rand(); + let mut account3_account = SolanaAccount::new(0, size_of::(), &program_id); + let mint2_key = pubkey_rand(); + let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); + do_process_instruction( + initialize_account(&program_id, &account3_key, &mint2_key, &owner_key).unwrap(), + vec![ + &mut account3_account, + &mut mint2_account, + &mut owner_account, + ], + ) + .unwrap(); + do_process_instruction( + initialize_mint( + &program_id, + &mint2_key, + &multisig_key, + Some(&multisig_key), + 2, + ) + .unwrap(), + vec![&mut mint2_account], + ) + .unwrap(); + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + mint_to( + &program_id, + &mint2_key, + &account3_key, + &multisig_key, + &[&signer_keys[0]], + 1000, + ) + .unwrap(), + vec![ + &mut mint2_account, + &mut account3_account, + &mut multisig_account, + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + freeze_account( + &program_id, + &account3_key, + &mint2_key, + &multisig_key, + &[&signer_keys[0]], + ) + .unwrap(), + vec![ + &mut account3_account, + &mut mint2_account, + &mut multisig_account, + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // do SetAuthority on mint + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + set_authority( + &program_id, + &mint_key, + Some(&owner_key), + AuthorityType::MintTokens, + &multisig_key, + &[&signer_keys[0]], + ) + .unwrap(), + vec![ + &mut mint_account, + &mut multisig_account, + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + + // do SetAuthority on account + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + set_authority( + &program_id, + &account_key, + Some(&owner_key), + AuthorityType::AccountHolder, + &multisig_key, + &[&signer_keys[0]], + ) + .unwrap(), + vec![ + &mut account, + &mut multisig_account, + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + } + + #[test] + fn test_validate_owner() { + let program_id = pubkey_rand(); + let owner_key = pubkey_rand(); + let mut signer_keys = [Pubkey::default(); MAX_SIGNERS]; + for signer_key in signer_keys.iter_mut().take(MAX_SIGNERS) { + *signer_key = pubkey_rand(); + } + let mut signer_lamports = 0; + let mut signer_data = vec![]; + let mut signers = vec![ + AccountInfo::new( + &owner_key, + true, + false, + &mut signer_lamports, + &mut signer_data, + &program_id, + false, + Epoch::default(), + ); + MAX_SIGNERS + 1 + ]; + for (signer, key) in signers.iter_mut().zip(&signer_keys) { + signer.key = key; + } + let mut lamports = 0; + let mut data = vec![0; size_of::()]; + let mut multisig: &mut Multisig = state::unpack_unchecked(&mut data).unwrap(); + multisig.m = MAX_SIGNERS as u8; + multisig.n = MAX_SIGNERS as u8; + multisig.signers = signer_keys; + multisig.is_initialized = true; + let owner_account_info = AccountInfo::new( + &owner_key, + false, + false, + &mut lamports, + &mut data, + &program_id, + false, + Epoch::default(), + ); + + // full 11 of 11 + Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap(); + + // 1 of 11 + { + let mut data_ref_mut = owner_account_info.data.borrow_mut(); + let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); + multisig.m = 1; + } + Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap(); + + // 2:1 + { + let mut data_ref_mut = owner_account_info.data.borrow_mut(); + let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); + multisig.m = 2; + multisig.n = 1; + } + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers) + ); + + // 0:11 + { + let mut data_ref_mut = owner_account_info.data.borrow_mut(); + let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); + multisig.m = 0; + multisig.n = 11; + } + Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap(); + + // 2:11 but 0 provided + { + let mut data_ref_mut = owner_account_info.data.borrow_mut(); + let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); + multisig.m = 2; + multisig.n = 11; + } + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &[]) + ); + // 2:11 but 1 provided + { + let mut data_ref_mut = owner_account_info.data.borrow_mut(); + let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); + multisig.m = 2; + multisig.n = 11; + } + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers[0..1]) + ); + + // 2:11, 2 from middle provided + { + let mut data_ref_mut = owner_account_info.data.borrow_mut(); + let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); + multisig.m = 2; + multisig.n = 11; + } + Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers[5..7]) + .unwrap(); + + // 11:11, one is not a signer + { + let mut data_ref_mut = owner_account_info.data.borrow_mut(); + let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); + multisig.m = 2; + multisig.n = 11; + } + signers[5].is_signer = false; + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers) + ); + signers[5].is_signer = true; + } + + #[test] + fn test_close_account() { + let program_id = pubkey_rand(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(42, size_of::(), &program_id); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new(2, size_of::(), &program_id); + let account3_key = pubkey_rand(); + let mut account3_account = SolanaAccount::new(2, size_of::(), &program_id); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = pubkey_rand(); + let mut owner2_account = SolanaAccount::default(); + + // uninitialized + assert_eq!( + Err(TokenError::UninitializedState.into()), + do_process_instruction( + close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(), + vec![ + &mut account_account, + &mut account3_account, + &mut owner2_account, + ], + ) + ); + + // initialize and mint to non-native account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 42).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + assert_eq!(account.amount, 42); + + // initialize native account + do_process_instruction( + initialize_account( + &program_id, + &account2_key, + &crate::native_mint::id(), + &owner_key, + ) + .unwrap(), + vec![&mut account2_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); + assert!(account.is_native); + assert_eq!(account.amount, 2); + + // close non-native account with balance + assert_eq!( + Err(TokenError::NonNativeHasBalance.into()), + do_process_instruction( + close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(), + vec![ + &mut account_account, + &mut account3_account, + &mut owner_account, + ], + ) + ); + assert_eq!(account_account.lamports, 42); + + // empty account + do_process_instruction( + burn(&program_id, &account_key, &owner_key, &[], 42).unwrap(), + vec![&mut account_account, &mut owner_account], + ) + .unwrap(); + + // wrong owner + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(), + vec![ + &mut account_account, + &mut account3_account, + &mut owner2_account, + ], + ) + ); + + // close account + do_process_instruction( + close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(), + vec![ + &mut account_account, + &mut account3_account, + &mut owner_account, + ], + ) + .unwrap(); + let account: &mut Account = state::unpack_unchecked(&mut account_account.data).unwrap(); + assert_eq!(account_account.lamports, 0); + assert_eq!(account.amount, 0); + assert_eq!(account3_account.lamports, 44); + + // fund and initialize new non-native account to test close authority + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(42, size_of::(), &program_id); + let owner2_key = pubkey_rand(); + let mut owner2_account = SolanaAccount::new(42, size_of::(), &program_id); + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + account_account.lamports = 2; + + do_process_instruction( + set_authority( + &program_id, + &account_key, + Some(&owner2_key), + AuthorityType::CloseAccount, + &owner_key, + &[], + ) + .unwrap(), + vec![&mut account_account, &mut owner_account], + ) + .unwrap(); + + // account owner cannot authorize close if close_authority is set + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(), + vec![ + &mut account_account, + &mut account3_account, + &mut owner_account, + ], + ) + ); + + // close non-native account with close_authority + do_process_instruction( + close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(), + vec![ + &mut account_account, + &mut account3_account, + &mut owner2_account, + ], + ) + .unwrap(); + assert_eq!(account_account.lamports, 0); + assert_eq!(account.amount, 0); + assert_eq!(account3_account.lamports, 46); + + // close native account + do_process_instruction( + close_account(&program_id, &account2_key, &account3_key, &owner_key, &[]).unwrap(), + vec![ + &mut account2_account, + &mut account3_account, + &mut owner_account, + ], + ) + .unwrap(); + let account: &mut Account = state::unpack_unchecked(&mut account2_account.data).unwrap(); + assert!(account.is_native); + assert_eq!(account_account.lamports, 0); + assert_eq!(account.amount, 0); + assert_eq!(account3_account.lamports, 48); + } + + #[test] + fn test_native_token() { + let program_id = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(42, size_of::(), &program_id); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new(2, size_of::(), &program_id); + let account3_key = pubkey_rand(); + let mut account3_account = SolanaAccount::new(2, 0, &program_id); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + + // initialize native account + do_process_instruction( + initialize_account( + &program_id, + &account_key, + &crate::native_mint::id(), + &owner_key, + ) + .unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + assert!(account.is_native); + assert_eq!(account.amount, 42); + + // initialize native account + do_process_instruction( + initialize_account( + &program_id, + &account2_key, + &crate::native_mint::id(), + &owner_key, + ) + .unwrap(), + vec![&mut account2_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); + assert!(account.is_native); + assert_eq!(account.amount, 2); + + // mint_to unsupported + assert_eq!( + Err(TokenError::NativeNotSupported.into()), + do_process_instruction( + mint_to( + &program_id, + &crate::native_mint::id(), + &account_key, + &owner_key, + &[], + 42 + ) + .unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + ); + + // burn unsupported + assert_eq!( + Err(TokenError::NativeNotSupported.into()), + do_process_instruction( + burn(&program_id, &account_key, &owner_key, &[], 42).unwrap(), + vec![&mut account_account, &mut owner_account], + ) + ); + + // initialize native account + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &owner_key, + &[], + 40, + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner_account, + ], + ) + .unwrap(); + + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + assert!(account.is_native); + assert_eq!(account_account.lamports, 2); + assert_eq!(account.amount, 2); + let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); + assert!(account.is_native); + assert_eq!(account2_account.lamports, 42); + assert_eq!(account.amount, 42); + + // close native account + do_process_instruction( + close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(), + vec![ + &mut account_account, + &mut account3_account, + &mut owner_account, + ], + ) + .unwrap(); + let account: &mut Account = state::unpack_unchecked(&mut account_account.data).unwrap(); + assert!(account.is_native); + assert_eq!(account_account.lamports, 0); + assert_eq!(account.amount, 0); + assert_eq!(account3_account.lamports, 4); + } + + #[test] + fn test_overflow() { + let program_id = pubkey_rand(); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = pubkey_rand(); + let mut owner2_account = SolanaAccount::default(); + let mint_owner_key = pubkey_rand(); + let mut mint_owner_account = SolanaAccount::default(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + + // create victim account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint_key, &owner2_key).unwrap(), + vec![ + &mut account2_account, + &mut mint_account, + &mut owner2_account, + ], + ) + .unwrap(); + + // create new mint with owner + do_process_instruction( + initialize_mint(&program_id, &mint_key, &mint_owner_key, None, 2).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + // mint the max to attacker + do_process_instruction( + mint_to( + &program_id, + &mint_key, + &account2_key, + &mint_owner_key, + &[], + 42, + ) + .unwrap(), + vec![ + &mut mint_account, + &mut account2_account, + &mut mint_owner_account, + ], + ) + .unwrap(); + let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); + assert_eq!(account.amount, 42); + + // mint the max to victum + do_process_instruction( + mint_to( + &program_id, + &mint_key, + &account_key, + &mint_owner_key, + &[], + u64::MAX, + ) + .unwrap(), + vec![ + &mut mint_account, + &mut account_account, + &mut mint_owner_account, + ], + ) + .unwrap(); + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + assert_eq!(account.amount, u64::MAX); + + // mint one more + assert_eq!( + Err(TokenError::Overflow.into()), + do_process_instruction( + mint_to( + &program_id, + &mint_key, + &account_key, + &mint_owner_key, + &[], + 1, + ) + .unwrap(), + vec![ + &mut mint_account, + &mut account_account, + &mut mint_owner_account, + ], + ) + ); + + // mint back to large amount + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + account.amount = 0; + do_process_instruction( + mint_to( + &program_id, + &mint_key, + &account_key, + &mint_owner_key, + &[], + u64::MAX, + ) + .unwrap(), + vec![ + &mut mint_account, + &mut account_account, + &mut mint_owner_account, + ], + ) + .unwrap(); + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + assert_eq!(account.amount, u64::MAX); + + // transfer to burn victim + assert_eq!( + Err(TokenError::Overflow.into()), + do_process_instruction( + transfer( + &program_id, + &account2_key, + &account_key, + &owner2_key, + &[], + 1, + ) + .unwrap(), + vec![ + &mut account2_account, + &mut account_account, + &mut owner2_account, + ], + ) + ); + } + + #[test] + fn test_frozen() { + let program_id = pubkey_rand(); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), + vec![&mut account2_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // create new mint and fund first account + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + + // no transfer if either account is frozen + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + account.state = AccountState::Frozen; + assert_eq!( + Err(TokenError::AccountFrozen.into()), + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &owner_key, + &[], + 500, + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner_account, + ], + ) + ); + + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + account.state = AccountState::Initialized; + let account2: &mut Account = state::unpack(&mut account2_account.data).unwrap(); + account2.state = AccountState::Frozen; + assert_eq!( + Err(TokenError::AccountFrozen.into()), + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &owner_key, + &[], + 500, + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner_account, + ], + ) + ); + + // no approve if account is frozen + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + account.state = AccountState::Frozen; + let delegate_key = pubkey_rand(); + let mut delegate_account = SolanaAccount::default(); + assert_eq!( + Err(TokenError::AccountFrozen.into()), + do_process_instruction( + approve( + &program_id, + &account_key, + &delegate_key, + &owner_key, + &[], + 100 + ) + .unwrap(), + vec![ + &mut account_account, + &mut delegate_account, + &mut owner_account, + ], + ) + ); + + // no revoke if account is frozen + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + account.delegate = COption::Some(delegate_key); + account.delegated_amount = 100; + assert_eq!( + Err(TokenError::AccountFrozen.into()), + do_process_instruction( + revoke(&program_id, &account_key, &owner_key, &[]).unwrap(), + vec![&mut account_account, &mut owner_account,], + ) + ); + + // no set authority if account is frozen + let new_owner_key = pubkey_rand(); + assert_eq!( + Err(TokenError::AccountFrozen.into()), + do_process_instruction( + set_authority( + &program_id, + &account_key, + Some(&new_owner_key), + AuthorityType::AccountHolder, + &owner_key, + &[] + ) + .unwrap(), + vec![&mut account_account, &mut owner_account,], + ) + ); + + // no mint_to if destination account is frozen + assert_eq!( + Err(TokenError::AccountFrozen.into()), + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 100).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account,], + ) + ); + + // no burn if account is frozen + assert_eq!( + Err(TokenError::AccountFrozen.into()), + do_process_instruction( + burn(&program_id, &account_key, &owner_key, &[], 100).unwrap(), + vec![&mut account_account, &mut owner_account,], + ) + ); + } + + #[test] + fn test_freeze_account() { + let program_id = pubkey_rand(); + let account_key = pubkey_rand(); + let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let account_owner_key = pubkey_rand(); + let mut account_owner_account = SolanaAccount::default(); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = pubkey_rand(); + let mut owner2_account = SolanaAccount::default(); + let mint_key = pubkey_rand(); + let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &account_owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut account_owner_account, + ], + ) + .unwrap(); + + // create new mint with owner different from account owner + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + + // mint cannot freeze + assert_eq!( + Err(TokenError::MintCannotFreeze.into()), + do_process_instruction( + freeze_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + ); + + // missing freeze_authority + let mint: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); + mint.freeze_authority = COption::Some(owner_key); + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + freeze_account(&program_id, &account_key, &mint_key, &owner2_key, &[]).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner2_account], + ) + ); + + // check explicit thaw + assert_eq!( + Err(TokenError::InvalidState.into()), + do_process_instruction( + thaw_account(&program_id, &account_key, &mint_key, &owner2_key, &[]).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner2_account], + ) + ); + + // freeze + do_process_instruction( + freeze_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + assert_eq!(account.state, AccountState::Frozen); + + // check explicit freeze + assert_eq!( + Err(TokenError::InvalidState.into()), + do_process_instruction( + freeze_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + ); + + // check thaw authority + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + thaw_account(&program_id, &account_key, &mint_key, &owner2_key, &[]).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner2_account], + ) + ); + + // thaw + do_process_instruction( + thaw_account(&program_id, &account_key, &mint_key, &owner_key, &[]).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + assert_eq!(account.state, AccountState::Initialized); + } +} diff --git a/program/src/state.rs b/program/src/state.rs new file mode 100644 index 0000000..423fa64 --- /dev/null +++ b/program/src/state.rs @@ -0,0 +1,122 @@ +//! State transition types + +use crate::{error::TokenError, instruction::MAX_SIGNERS, option::COption}; +use solana_sdk::{program_error::ProgramError, pubkey::Pubkey}; +use std::mem::size_of; + +/// Mint data. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct Mint { + /// Optional authority used to mint new tokens. The mint authority may only be provided during + /// mint creation. If no mint authority is present then the mint has a fixed supply and no + /// further tokens may be minted. + pub mint_authority: COption, + /// Number of base 10 digits to the right of the decimal place. + pub decimals: u8, + /// Is `true` if this structure has been initialized + pub is_initialized: bool, + /// Optional authority to freeze token accounts. + pub freeze_authority: COption, +} +impl IsInitialized for Mint { + fn is_initialized(&self) -> bool { + self.is_initialized + } +} + +/// Account data. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct Account { + /// The mint associated with this account + pub mint: Pubkey, + /// The owner of this account. + pub owner: Pubkey, + /// The amount of tokens this account holds. + pub amount: u64, + /// If `delegate` is `Some` then `delegated_amount` represents + /// the amount authorized by the delegate + pub delegate: COption, + /// The account's state + pub state: AccountState, + /// Is this a native token + pub is_native: bool, + /// The amount delegated + pub delegated_amount: u64, + /// Optional authority to close the account. + pub close_authority: COption, +} +impl Account { + /// Checks if account is frozen + pub fn is_frozen(&self) -> bool { + self.state == AccountState::Frozen + } +} +impl IsInitialized for Account { + fn is_initialized(&self) -> bool { + self.state != AccountState::Uninitialized + } +} + +/// Account state. +#[repr(u8)] +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum AccountState { + /// Account is not yet initialized + Uninitialized, + /// Account is initialized; the account owner and/or delegate may perform permitted operations + /// on this account + Initialized, + /// Account has been frozen by the mint freeze authority. Neither the account owner nor + /// the delegate are able to perform operations on this account. + Frozen, +} + +impl Default for AccountState { + fn default() -> Self { + AccountState::Uninitialized + } +} + +/// Multisignature data. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct Multisig { + /// Number of signers required + pub m: u8, + /// Number of valid signers + pub n: u8, + /// Is `true` if this structure has been initialized + pub is_initialized: bool, + /// Signer public keys + pub signers: [Pubkey; MAX_SIGNERS], +} +impl IsInitialized for Multisig { + fn is_initialized(&self) -> bool { + self.is_initialized + } +} + +/// Check is a token state is initialized +pub trait IsInitialized { + /// Is initialized + fn is_initialized(&self) -> bool; +} + +/// Unpacks a token state from a bytes buffer while assuring that the state is initialized. +pub fn unpack(input: &mut [u8]) -> Result<&mut T, ProgramError> { + let mut_ref: &mut T = unpack_unchecked(input)?; + if !mut_ref.is_initialized() { + return Err(TokenError::UninitializedState.into()); + } + Ok(mut_ref) +} +/// Unpacks a token state from a bytes buffer without checking that the state is initialized. +pub fn unpack_unchecked(input: &mut [u8]) -> Result<&mut T, ProgramError> { + if input.len() != size_of::() { + return Err(ProgramError::InvalidAccountData); + } + #[allow(clippy::cast_ptr_alignment)] + Ok(unsafe { &mut *(&mut input[0] as *mut u8 as *mut T) }) +} From b29b86ec28cf800aef301937a2be907e788f7917 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Wed, 26 Aug 2020 22:34:43 -0700 Subject: [PATCH 022/248] Update token.h --- program/inc/token.h | 219 +++++++++++++++++++++++++++++++++----------- 1 file changed, 163 insertions(+), 56 deletions(-) diff --git a/program/inc/token.h b/program/inc/token.h index 38222c0..44f8423 100644 --- a/program/inc/token.h +++ b/program/inc/token.h @@ -21,6 +21,89 @@ */ #define Token_MIN_SIGNERS 1 +/** + * Account state. + */ +enum Token_AccountState +#ifdef __cplusplus + : uint8_t +#endif // __cplusplus + { + /** + * Account is not yet initialized + */ + Token_AccountState_Uninitialized, + /** + * Account is initialized; the account owner and/or delegate may perform permitted operations + * on this account + */ + Token_AccountState_Initialized, + /** + * Account has been frozen by the mint freeze authority. Neither the account owner nor + * the delegate are able to perform operations on this account. + */ + Token_AccountState_Frozen, +}; +#ifndef __cplusplus +typedef uint8_t Token_AccountState; +#endif // __cplusplus + +/** + * Specifies the authority type for SetAuthority instructions + */ +enum Token_AuthorityType +#ifdef __cplusplus + : uint8_t +#endif // __cplusplus + { + /** + * Authority to mint new tokens + */ + Token_AuthorityType_MintTokens, + /** + * Authority to freeze any account associated with the Mint + */ + Token_AuthorityType_FreezeAccount, + /** + * Holder of a given token account + */ + Token_AuthorityType_AccountHolder, + /** + * Authority to close a token account + */ + Token_AuthorityType_CloseAccount, +}; +#ifndef __cplusplus +typedef uint8_t Token_AuthorityType; +#endif // __cplusplus + +typedef uint8_t Token_Pubkey[32]; + +/** + * A C representation of Rust's `std::option::Option` + */ +typedef enum Token_COption_Pubkey_Tag { + /** + * No value + */ + Token_COption_Pubkey_None_Pubkey, + /** + * Some value `T` + */ + Token_COption_Pubkey_Some_Pubkey, +} Token_COption_Pubkey_Tag; + +typedef struct Token_COption_Pubkey_Token_Some_Body_Pubkey { + Token_Pubkey _0; +} Token_COption_Pubkey_Token_Some_Body_Pubkey; + +typedef struct Token_COption_Pubkey { + Token_COption_Pubkey_Tag tag; + union { + Token_COption_Pubkey_Token_Some_Body_Pubkey some; + }; +} Token_COption_Pubkey; + /** * Instructions supported by the token program. */ @@ -35,11 +118,6 @@ typedef enum Token_TokenInstruction_Tag { * Accounts expected by this instruction: * * 0. `[writable]` The mint to initialize. - * 1. - * * If supply is non-zero: `[writable]` The account to hold all the newly minted tokens. - * * If supply is zero: `[]` The owner/multisignature of the mint. - * 2. `[]` (optional) The owner/multisignature of the mint if supply is non-zero, if - * present then further minting is supported. * */ Token_TokenInstruction_InitializeMint, @@ -127,36 +205,34 @@ typedef enum Token_TokenInstruction_Tag { */ Token_TokenInstruction_Revoke, /** - * Sets a new owner of a mint or account. + * Sets a new authority of a mint or account. * * Accounts expected by this instruction: * - * * Single owner - * 0. `[writable]` The mint or account to change the owner of. - * 1. `[]` The new owner/delegate/multisignature. - * 2. `[signer]` The owner of the mint or account. + * * Single authority + * 0. `[writable]` The mint or account to change the authority of. + * 1. `[signer]` The current authority of the mint or account. * - * * Multisignature owner - * 0. `[writable]` The mint or account to change the owner of. - * 1. `[]` The new owner/delegate/multisignature. - * 2. `[]` The mint's or account's multisignature owner. + * * Multisignature authority + * 0. `[writable]` The mint or account to change the authority of. + * 1. `[]` The mint's or account's multisignature authority. * 3. ..3+M '[signer]' M signer accounts */ - Token_TokenInstruction_SetOwner, + Token_TokenInstruction_SetAuthority, /** * Mints new tokens to an account. The native mint does not support minting. * * Accounts expected by this instruction: * - * * Single owner + * * Single authority * 0. `[writable]` The mint. * 1. `[writable]` The account to mint tokens to. - * 2. `[signer]` The mint's owner. + * 2. `[signer]` The mint's minting authority. * - * * Multisignature owner + * * Multisignature authority * 0. `[writable]` The mint. * 1. `[writable]` The account to mint tokens to. - * 2. `[]` The mint's multisignature owner. + * 2. `[]` The mint's multisignature mint-tokens authority. * 3. ..3+M '[signer]' M signer accounts. */ Token_TokenInstruction_MintTo, @@ -194,17 +270,55 @@ typedef enum Token_TokenInstruction_Tag { * 3. ..3+M '[signer]' M signer accounts. */ Token_TokenInstruction_CloseAccount, + /** + * Freeze an Initialized account using the Mint's freeze_authority (if set). + * + * Accounts expected by this instruction: + * + * * Single owner + * 0. `[writable]` The account to freeze. + * 1. '[]' The token mint. + * 2. `[signer]` The mint freeze authority. + * + * * Multisignature owner + * 0. `[writable]` The account to freeze. + * 1. '[]' The token mint. + * 2. `[]` The mint's multisignature freeze authority. + * 3. ..3+M '[signer]' M signer accounts. + */ + Token_TokenInstruction_FreezeAccount, + /** + * Thaw a Frozen account using the Mint's freeze_authority (if set). + * + * Accounts expected by this instruction: + * + * * Single owner + * 0. `[writable]` The account to freeze. + * 1. '[]' The token mint. + * 2. `[signer]` The mint freeze authority. + * + * * Multisignature owner + * 0. `[writable]` The account to freeze. + * 1. '[]' The token mint. + * 2. `[]` The mint's multisignature freeze authority. + * 3. ..3+M '[signer]' M signer accounts. + */ + Token_TokenInstruction_ThawAccount, } Token_TokenInstruction_Tag; typedef struct Token_TokenInstruction_Token_InitializeMint_Body { - /** - * Initial amount of tokens to mint. - */ - uint64_t amount; /** * Number of base 10 digits to the right of the decimal place. */ uint8_t decimals; + /** + * The authority/multisignature to mint tokens. + */ + Token_Pubkey mint_authority; + /** + * The freeze authority/multisignature of the mint. + */ + Token_COption_Pubkey freeze_authority; } Token_TokenInstruction_Token_InitializeMint_Body; typedef struct Token_TokenInstruction_Token_InitializeMultisig_Body { @@ -228,6 +342,17 @@ typedef struct Token_TokenInstruction_Token_Approve_Body { uint64_t amount; } Token_TokenInstruction_Token_Approve_Body; +typedef struct Token_TokenInstruction_Token_SetAuthority_Body { + /** + * The type of authority to update. + */ + Token_AuthorityType authority_type; + /** + * The new authority + */ + Token_COption_Pubkey new_authority; +} Token_TokenInstruction_Token_SetAuthority_Body; + typedef struct Token_TokenInstruction_Token_MintTo_Body { /** * The amount of new tokens to mint. @@ -249,48 +374,22 @@ typedef struct Token_TokenInstruction { Token_TokenInstruction_Token_InitializeMultisig_Body initialize_multisig; Token_TokenInstruction_Token_Transfer_Body transfer; Token_TokenInstruction_Token_Approve_Body approve; + Token_TokenInstruction_Token_SetAuthority_Body set_authority; Token_TokenInstruction_Token_MintTo_Body mint_to; Token_TokenInstruction_Token_Burn_Body burn; }; } Token_TokenInstruction; -typedef uint8_t Token_Pubkey[32]; - -/** - * A C representation of Rust's `std::option::Option` - */ -typedef enum Token_COption_Pubkey_Tag { - /** - * No value - */ - Token_COption_Pubkey_None_Pubkey, - /** - * Some value `T` - */ - Token_COption_Pubkey_Some_Pubkey, -} Token_COption_Pubkey_Tag; - -typedef struct Token_COption_Pubkey_Token_Some_Body_Pubkey { - Token_Pubkey _0; -} Token_COption_Pubkey_Token_Some_Body_Pubkey; - -typedef struct Token_COption_Pubkey { - Token_COption_Pubkey_Tag tag; - union { - Token_COption_Pubkey_Token_Some_Body_Pubkey some; - }; -} Token_COption_Pubkey; - /** * Mint data. */ typedef struct Token_Mint { /** - * Optional owner, used to mint new tokens. The owner may only - * be provided during mint creation. If no owner is present then the mint - * has a fixed supply and no further tokens may be minted. + * Optional authority used to mint new tokens. The mint authority may only be provided during + * mint creation. If no mint authority is present then the mint has a fixed supply and no + * further tokens may be minted. */ - Token_COption_Pubkey owner; + Token_COption_Pubkey mint_authority; /** * Number of base 10 digits to the right of the decimal place. */ @@ -299,6 +398,10 @@ typedef struct Token_Mint { * Is `true` if this structure has been initialized */ bool is_initialized; + /** + * Optional authority to freeze token accounts. + */ + Token_COption_Pubkey freeze_authority; } Token_Mint; /** @@ -323,9 +426,9 @@ typedef struct Token_Account { */ Token_COption_Pubkey delegate; /** - * Is `true` if this structure has been initialized + * The account's state */ - bool is_initialized; + Token_AccountState state; /** * Is this a native token */ @@ -334,6 +437,10 @@ typedef struct Token_Account { * The amount delegated */ uint64_t delegated_amount; + /** + * Optional authority to close the account. + */ + Token_COption_Pubkey close_authority; } Token_Account; /** From dca91f8adb7aacd5517cb127537368fc117b7e02 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Wed, 26 Aug 2020 22:58:42 -0700 Subject: [PATCH 023/248] InitializeMint's mint authority is no longer optional --- program/src/error.rs | 3 --- program/src/instruction.rs | 32 +++++++++++++++++--------------- program/src/processor.rs | 25 ++----------------------- 3 files changed, 19 insertions(+), 41 deletions(-) diff --git a/program/src/error.rs b/program/src/error.rs index cc7da55..a1d3152 100644 --- a/program/src/error.rs +++ b/program/src/error.rs @@ -22,9 +22,6 @@ pub enum TokenError { /// The account cannot be initialized because it is already being used. #[error("AlreadyInUse")] AlreadyInUse, - /// An owner is required if initial supply is zero. - #[error("An owner is required if supply is zero")] - OwnerRequiredIfNoInitialSupply, /// Invalid number of provided signers. #[error("Invalid number of provided signers")] InvalidNumberOfProvidedSigners, diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 96acf76..9c77516 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -30,8 +30,8 @@ pub enum TokenInstruction { InitializeMint { /// Number of base 10 digits to the right of the decimal place. decimals: u8, - /// The authority/multisignature to mint tokens. If present, further minting is supported. - mint_authority: COption, + /// The authority/multisignature to mint tokens. + mint_authority: Pubkey, /// The freeze authority/multisignature of the mint. freeze_authority: COption, }, @@ -226,7 +226,7 @@ impl TokenInstruction { } Ok(match input[0] { 0 => { - if input.len() < size_of::() + size_of::() + size_of::() { + if input.len() < size_of::() + size_of::() + size_of::() { return Err(TokenError::InvalidInstruction.into()); } let mut input_len = 0; @@ -235,11 +235,9 @@ impl TokenInstruction { let decimals = unsafe { *(&input[input_len] as *const u8) }; input_len += size_of::(); - let mint_authority = COption::unpack_or( - input, - &mut input_len, - Into::::into(TokenError::InvalidInstruction), - )?; + let mint_authority = unsafe { *(&input[input_len] as *const u8 as *const Pubkey) }; + input_len += size_of::(); + let freeze_authority = COption::unpack_or( input, &mut input_len, @@ -338,7 +336,11 @@ impl TokenInstruction { *value = *decimals; output_len += size_of::(); - mint_authority.pack(&mut output, &mut output_len); + #[allow(clippy::cast_ptr_alignment)] + let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut Pubkey) }; + *value = *mint_authority; + output_len += size_of::(); + freeze_authority.pack(&mut output, &mut output_len); } Self::InitializeAccount => { @@ -468,10 +470,9 @@ pub fn initialize_mint( freeze_authority_pubkey: Option<&Pubkey>, decimals: u8, ) -> Result { - let mint_authority = COption::Some(*mint_authority_pubkey); let freeze_authority = freeze_authority_pubkey.cloned().into(); let data = TokenInstruction::InitializeMint { - mint_authority, + mint_authority: *mint_authority_pubkey, freeze_authority, decimals, } @@ -806,23 +807,24 @@ mod test { fn test_instruction_packing() { let check = TokenInstruction::InitializeMint { decimals: 2, - mint_authority: COption::None, + mint_authority: Pubkey::new(&[1u8; 32]), freeze_authority: COption::None, }; let packed = check.pack().unwrap(); - let expect = Vec::from([0u8, 2, 0, 0]); + let mut expect = Vec::from([0u8, 2]); + expect.extend_from_slice(&[1u8; 32]); + expect.extend_from_slice(&[0]); assert_eq!(packed, expect); let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); let check = TokenInstruction::InitializeMint { decimals: 2, - mint_authority: COption::Some(Pubkey::new(&[2u8; 32])), + mint_authority: Pubkey::new(&[2u8; 32]), freeze_authority: COption::Some(Pubkey::new(&[3u8; 32])), }; let packed = check.pack().unwrap(); let mut expect = vec![0u8, 2]; - expect.extend_from_slice(&[1]); expect.extend_from_slice(&[2u8; 32]); expect.extend_from_slice(&[1]); expect.extend_from_slice(&[3u8; 32]); diff --git a/program/src/processor.rs b/program/src/processor.rs index d8baa68..e4fe542 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -26,7 +26,7 @@ impl Processor { pub fn process_initialize_mint( accounts: &[AccountInfo], decimals: u8, - mint_authority: COption, + mint_authority: Pubkey, freeze_authority: COption, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); @@ -38,11 +38,7 @@ impl Processor { return Err(TokenError::AlreadyInUse.into()); } - if mint_authority.is_none() { - return Err(TokenError::OwnerRequiredIfNoInitialSupply.into()); - } - - mint.mint_authority = mint_authority; + mint.mint_authority = COption::Some(mint_authority); mint.decimals = decimals; mint.is_initialized = true; mint.freeze_authority = freeze_authority; @@ -616,9 +612,6 @@ impl PrintProgramError for TokenError { TokenError::OwnerMismatch => info!("Error: owner does not match"), TokenError::FixedSupply => info!("Error: the total supply of this token is fixed"), TokenError::AlreadyInUse => info!("Error: account or token already in use"), - TokenError::OwnerRequiredIfNoInitialSupply => { - info!("Error: An owner is required if supply is zero") - } TokenError::InvalidNumberOfProvidedSigners => { info!("Error: Invalid number of provided signers") } @@ -714,20 +707,6 @@ mod tests { let mint2_key = pubkey_rand(); let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); - // mint_authority not provided - let mut instruction = initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(); - instruction.data = TokenInstruction::InitializeMint { - mint_authority: COption::None, - freeze_authority: COption::None, - decimals: 2, - } - .pack() - .unwrap(); - assert_eq!( - Err(TokenError::OwnerRequiredIfNoInitialSupply.into()), - do_process_instruction(instruction, vec![&mut mint_account]) - ); - // create new mint do_process_instruction( initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), From b05b9db1721e9fc68800d3725cd5a330509ed39a Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Thu, 27 Aug 2020 00:29:54 -0600 Subject: [PATCH 024/248] Pass rent_sysvar into init Instructions and check rent-exempt --- program/src/error.rs | 3 + program/src/instruction.rs | 15 +- program/src/processor.rs | 514 +++++++++++++++++++++++++++++-------- 3 files changed, 416 insertions(+), 116 deletions(-) diff --git a/program/src/error.rs b/program/src/error.rs index a1d3152..4a9e223 100644 --- a/program/src/error.rs +++ b/program/src/error.rs @@ -7,6 +7,9 @@ use thiserror::Error; /// Errors that may be returned by the Token program. #[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)] pub enum TokenError { + /// Lamport balance below rent-exempt threshold. + #[error("Lamport balance below rent-exempt threshold")] + NotRentExempt, /// Insufficient funds for the operation requested. #[error("Insufficient funds")] InsufficientFunds, diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 9c77516..79bd0a0 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -5,6 +5,7 @@ use solana_sdk::{ instruction::{AccountMeta, Instruction}, program_error::ProgramError, pubkey::Pubkey, + sysvar, }; use std::mem::size_of; @@ -26,6 +27,7 @@ pub enum TokenInstruction { /// Accounts expected by this instruction: /// /// 0. `[writable]` The mint to initialize. + /// 1. `[]` Rent sysvar /// InitializeMint { /// Number of base 10 digits to the right of the decimal place. @@ -47,6 +49,7 @@ pub enum TokenInstruction { /// 0. `[writable]` The account to initialize. /// 1. `[]` The mint this account will be associated with. /// 2. `[]` The new account's owner/multisignature. + /// 3. `[]` Rent sysvar InitializeAccount, /// Initializes a multisignature account with N provided signers. /// @@ -61,7 +64,8 @@ pub enum TokenInstruction { /// Accounts expected by this instruction: /// /// 0. `[writable]` The multisignature account to initialize. - /// 1. ..1+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. + /// 2. `[]` Rent sysvar + /// 3. ..2+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. InitializeMultisig { /// The number of signers (M) required to validate this multisignature account. m: u8, @@ -478,7 +482,10 @@ pub fn initialize_mint( } .pack()?; - let accounts = vec![AccountMeta::new(*mint_pubkey, false)]; + let accounts = vec![ + AccountMeta::new(*mint_pubkey, false), + AccountMeta::new_readonly(sysvar::rent::id(), false), + ]; Ok(Instruction { program_id: *token_program_id, @@ -500,6 +507,7 @@ pub fn initialize_account( AccountMeta::new(*account_pubkey, false), AccountMeta::new_readonly(*mint_pubkey, false), AccountMeta::new_readonly(*owner_pubkey, false), + AccountMeta::new_readonly(sysvar::rent::id(), false), ]; Ok(Instruction { @@ -524,8 +532,9 @@ pub fn initialize_multisig( } let data = TokenInstruction::InitializeMultisig { m }.pack()?; - let mut accounts = Vec::with_capacity(1 + signer_pubkeys.len()); + let mut accounts = Vec::with_capacity(1 + 1 + signer_pubkeys.len()); accounts.push(AccountMeta::new(*multisig_pubkey, false)); + accounts.push(AccountMeta::new_readonly(sysvar::rent::id(), false)); for signer_pubkey in signer_pubkeys.iter() { accounts.push(AccountMeta::new_readonly(**signer_pubkey, false)); } diff --git a/program/src/processor.rs b/program/src/processor.rs index e4fe542..eb72297 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -16,6 +16,7 @@ use solana_sdk::{ info, program_error::{PrintProgramError, ProgramError}, pubkey::Pubkey, + sysvar::{rent::Rent, Sysvar}, }; use std::mem::size_of; @@ -31,6 +32,8 @@ impl Processor { ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let mint_info = next_account_info(account_info_iter)?; + let mint_info_data_len = mint_info.data_len(); + let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?; let mut mint_info_data = mint_info.data.borrow_mut(); let mut mint: &mut Mint = state::unpack_unchecked(&mut mint_info_data)?; @@ -38,6 +41,10 @@ impl Processor { return Err(TokenError::AlreadyInUse.into()); } + if mint_info.lamports() < rent.minimum_balance(mint_info_data_len) { + return Err(TokenError::NotRentExempt.into()); + } + mint.mint_authority = COption::Some(mint_authority); mint.decimals = decimals; mint.is_initialized = true; @@ -52,6 +59,8 @@ impl Processor { let new_account_info = next_account_info(account_info_iter)?; let mint_info = next_account_info(account_info_iter)?; let owner_info = next_account_info(account_info_iter)?; + let new_account_info_data_len = new_account_info.data_len(); + let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?; let mut new_account_data = new_account_info.data.borrow_mut(); let mut account: &mut Account = state::unpack_unchecked(&mut new_account_data)?; @@ -59,6 +68,10 @@ impl Processor { return Err(TokenError::AlreadyInUse.into()); } + if new_account_info.lamports() < rent.minimum_balance(new_account_info_data_len) { + return Err(TokenError::NotRentExempt.into()); + } + account.mint = *mint_info.key; account.owner = *owner_info.key; account.delegate = COption::None; @@ -79,12 +92,19 @@ impl Processor { pub fn process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let multisig_info = next_account_info(account_info_iter)?; + let multisig_info_data_len = multisig_info.data_len(); + let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?; + let mut multisig_account_data = multisig_info.data.borrow_mut(); let mut multisig: &mut Multisig = state::unpack_unchecked(&mut multisig_account_data)?; if multisig.is_initialized { return Err(TokenError::AlreadyInUse.into()); } + if multisig_info.lamports() < rent.minimum_balance(multisig_info_data_len) { + return Err(TokenError::NotRentExempt.into()); + } + let signer_infos = account_info_iter.as_slice(); multisig.m = m; multisig.n = signer_infos.len() as u8; @@ -607,6 +627,9 @@ impl PrintProgramError for TokenError { E: 'static + std::error::Error + DecodeError + PrintProgramError + FromPrimitive, { match self { + TokenError::NotRentExempt => { + info!("Error: Lamport balance below rent-exempt threshold") + } TokenError::InsufficientFunds => info!("Error: insufficient funds"), TokenError::MintMismatch => info!("Error: Account not associated with this Mint"), TokenError::OwnerMismatch => info!("Error: owner does not match"), @@ -650,7 +673,7 @@ mod tests { }; use solana_sdk::{ account::Account as SolanaAccount, account_info::create_is_signer_account_infos, - clock::Epoch, instruction::Instruction, + clock::Epoch, instruction::Instruction, sysvar::rent, }; fn pubkey_rand() -> Pubkey { @@ -676,6 +699,22 @@ mod tests { TokenError::MintMismatch.into() } + fn rent_sysvar() -> SolanaAccount { + rent::create_account(42, &Rent::default()) + } + + fn mint_minimum_balance() -> u64 { + Rent::default().minimum_balance(size_of::()) + } + + fn account_minimum_balance() -> u64 { + Rent::default().minimum_balance(size_of::()) + } + + fn multisig_minimum_balance() -> u64 { + Rent::default().minimum_balance(size_of::()) + } + #[test] fn test_print_error() { let error = return_token_error_as_program_error(); @@ -683,7 +722,7 @@ mod tests { } #[test] - #[should_panic(expected = "Custom(1)")] + #[should_panic(expected = "Custom(2)")] fn test_error_unwrap() { Err::<(), ProgramError>(return_token_error_as_program_error()).unwrap(); } @@ -703,14 +742,27 @@ mod tests { let program_id = pubkey_rand(); let owner_key = pubkey_rand(); let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mint_account = SolanaAccount::new(42, size_of::(), &program_id); let mint2_key = pubkey_rand(); - let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mint2_account = + SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + let mut rent_sysvar = rent_sysvar(); + + // mint is not rent exempt + assert_eq!( + Err(TokenError::NotRentExempt.into()), + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar] + ) + ); + + mint_account.lamports = mint_minimum_balance(); // create new mint do_process_instruction( initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account], + vec![&mut mint_account, &mut rent_sysvar], ) .unwrap(); @@ -719,14 +771,14 @@ mod tests { Err(TokenError::AlreadyInUse.into()), do_process_instruction( initialize_mint(&program_id, &mint_key, &owner_key, None, 2,).unwrap(), - vec![&mut mint_account] + vec![&mut mint_account, &mut rent_sysvar] ) ); // create another mint that can freeze do_process_instruction( initialize_mint(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(), - vec![&mut mint2_account], + vec![&mut mint2_account, &mut rent_sysvar], ) .unwrap(); let mint2: &mut Mint = state::unpack(&mut mint2_account.data).unwrap(); @@ -737,16 +789,38 @@ mod tests { fn test_initialize_mint_account() { let program_id = pubkey_rand(); let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account_account = SolanaAccount::new(42, size_of::(), &program_id); let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); let mint_key = pubkey_rand(); let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut rent_sysvar = rent_sysvar(); + + // account is not rent exempt + assert_eq!( + Err(TokenError::NotRentExempt.into()), + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar + ], + ) + ); + + account_account.lamports = account_minimum_balance(); // create account do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], ) .unwrap(); @@ -755,7 +829,12 @@ mod tests { Err(TokenError::AlreadyInUse.into()), do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar + ], ) ); } @@ -764,42 +843,64 @@ mod tests { fn test_transfer() { let program_id = pubkey_rand(); let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account2_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let account3_key = pubkey_rand(); - let mut account3_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account3_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let delegate_key = pubkey_rand(); let mut delegate_account = SolanaAccount::default(); let mismatch_key = pubkey_rand(); - let mut mismatch_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mismatch_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); let owner2_key = pubkey_rand(); let mut owner2_account = SolanaAccount::default(); let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let mint2_key = pubkey_rand(); - let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mint2_account = + SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + let mut rent_sysvar = rent_sysvar(); // create account do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], ) .unwrap(); // create another account do_process_instruction( initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![&mut account2_account, &mut mint_account, &mut owner_account], + vec![ + &mut account2_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], ) .unwrap(); // create another account do_process_instruction( initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), - vec![&mut account3_account, &mut mint_account, &mut owner_account], + vec![ + &mut account3_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], ) .unwrap(); @@ -810,6 +911,7 @@ mod tests { &mut mismatch_account, &mut mint2_account, &mut owner_account, + &mut rent_sysvar, ], ) .unwrap(); @@ -817,7 +919,7 @@ mod tests { // create new mint & mint to account do_process_instruction( initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account], + vec![&mut mint_account, &mut rent_sysvar], ) .unwrap(); do_process_instruction( @@ -1132,16 +1234,24 @@ mod tests { fn test_mintable_token_with_zero_supply() { let program_id = pubkey_rand(); let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + let mut rent_sysvar = rent_sysvar(); // create account do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut owner_account, &mut mint_account], + vec![ + &mut account_account, + &mut owner_account, + &mut mint_account, + &mut rent_sysvar, + ], ) .unwrap(); @@ -1149,7 +1259,7 @@ mod tests { let decimals = 2; do_process_instruction( initialize_mint(&program_id, &mint_key, &owner_key, None, decimals).unwrap(), - vec![&mut mint_account], + vec![&mut mint_account, &mut rent_sysvar], ) .unwrap(); let mint: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); @@ -1179,9 +1289,11 @@ mod tests { fn test_approve() { let program_id = pubkey_rand(); let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account2_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let delegate_key = pubkey_rand(); let mut delegate_account = SolanaAccount::default(); let owner_key = pubkey_rand(); @@ -1189,26 +1301,38 @@ mod tests { let owner2_key = pubkey_rand(); let mut owner2_account = SolanaAccount::default(); let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + let mut rent_sysvar = rent_sysvar(); // create account do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut owner_account, &mut mint_account], + vec![ + &mut account_account, + &mut owner_account, + &mut mint_account, + &mut rent_sysvar, + ], ) .unwrap(); // create another account do_process_instruction( initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![&mut account2_account, &mut owner_account, &mut mint_account], + vec![ + &mut account2_account, + &mut owner_account, + &mut mint_account, + &mut rent_sysvar, + ], ) .unwrap(); // create new mint & mint to account do_process_instruction( initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut account_account], + vec![&mut mint_account, &mut rent_sysvar], ) .unwrap(); do_process_instruction( @@ -1292,18 +1416,23 @@ mod tests { fn test_set_authority() { let program_id = pubkey_rand(); let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account2_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); let owner2_key = pubkey_rand(); let mut owner2_account = SolanaAccount::default(); let owner3_key = pubkey_rand(); let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let mint2_key = pubkey_rand(); - let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mint2_account = + SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + let mut rent_sysvar = rent_sysvar(); // invalid account assert_eq!( @@ -1325,7 +1454,12 @@ mod tests { // create account do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], ) .unwrap(); @@ -1336,6 +1470,7 @@ mod tests { &mut account2_account, &mut mint2_account, &mut owner_account, + &mut rent_sysvar, ], ) .unwrap(); @@ -1455,7 +1590,7 @@ mod tests { // create new mint with owner do_process_instruction( initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account], + vec![&mut mint_account, &mut rent_sysvar], ) .unwrap(); @@ -1559,7 +1694,7 @@ mod tests { // create mint with owner and freeze_authority do_process_instruction( initialize_mint(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(), - vec![&mut mint2_account], + vec![&mut mint2_account, &mut rent_sysvar], ) .unwrap(); @@ -1614,42 +1749,65 @@ mod tests { fn test_mint_to() { let program_id = pubkey_rand(); let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account2_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let account3_key = pubkey_rand(); - let mut account3_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account3_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let mismatch_key = pubkey_rand(); - let mut mismatch_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mismatch_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); let owner2_key = pubkey_rand(); let mut owner2_account = SolanaAccount::default(); let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let mint2_key = pubkey_rand(); - let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mint2_account = + SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let uninitialized_key = pubkey_rand(); - let mut uninitialized_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut uninitialized_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut rent_sysvar = rent_sysvar(); // create account do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], ) .unwrap(); // create another account do_process_instruction( initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![&mut account2_account, &mut mint_account, &mut owner_account], + vec![ + &mut account2_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], ) .unwrap(); // create another account do_process_instruction( initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), - vec![&mut account3_account, &mut mint_account, &mut owner_account], + vec![ + &mut account3_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], ) .unwrap(); @@ -1660,6 +1818,7 @@ mod tests { &mut mismatch_account, &mut mint2_account, &mut owner_account, + &mut rent_sysvar, ], ) .unwrap(); @@ -1667,7 +1826,7 @@ mod tests { // create new mint with owner do_process_instruction( initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account], + vec![&mut mint_account, &mut rent_sysvar], ) .unwrap(); @@ -1764,42 +1923,64 @@ mod tests { fn test_burn() { let program_id = pubkey_rand(); let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account2_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let account3_key = pubkey_rand(); - let mut account3_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account3_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let delegate_key = pubkey_rand(); let mut delegate_account = SolanaAccount::default(); let mismatch_key = pubkey_rand(); - let mut mismatch_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mismatch_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); let owner2_key = pubkey_rand(); let mut owner2_account = SolanaAccount::default(); let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let mint2_key = pubkey_rand(); - let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mint2_account = + SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + let mut rent_sysvar = rent_sysvar(); // create account do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], ) .unwrap(); // create another account do_process_instruction( initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![&mut account2_account, &mut mint_account, &mut owner_account], + vec![ + &mut account2_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], ) .unwrap(); // create another account do_process_instruction( initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), - vec![&mut account3_account, &mut mint_account, &mut owner_account], + vec![ + &mut account3_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], ) .unwrap(); @@ -1810,6 +1991,7 @@ mod tests { &mut mismatch_account, &mut mint2_account, &mut owner_account, + &mut rent_sysvar, ], ) .unwrap(); @@ -1817,7 +1999,7 @@ mod tests { // create new mint do_process_instruction( initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account], + vec![&mut mint_account, &mut rent_sysvar], ) .unwrap(); do_process_instruction( @@ -1920,21 +2102,44 @@ mod tests { fn test_multisig() { let program_id = pubkey_rand(); let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let account_key = pubkey_rand(); - let mut account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account2_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); let multisig_key = pubkey_rand(); - let mut multisig_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut multisig_account = SolanaAccount::new(42, size_of::(), &program_id); let multisig_delegate_key = pubkey_rand(); - let mut multisig_delegate_account = - SolanaAccount::new(0, size_of::(), &program_id); + let mut multisig_delegate_account = SolanaAccount::new( + multisig_minimum_balance(), + size_of::(), + &program_id, + ); let signer_keys = vec![pubkey_rand(); MAX_SIGNERS]; let signer_key_refs: Vec<&Pubkey> = signer_keys.iter().map(|key| key).collect(); let mut signer_accounts = vec![SolanaAccount::new(0, 0, &program_id); MAX_SIGNERS]; + let mut rent_sysvar = rent_sysvar(); + + // multisig is not rent exempt + let account_info_iter = &mut signer_accounts.iter_mut(); + assert_eq!( + Err(TokenError::NotRentExempt.into()), + do_process_instruction( + initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(), + vec![ + &mut multisig_account, + &mut rent_sysvar, + &mut account_info_iter.next().unwrap(), + ], + ) + ); + + multisig_account.lamports = multisig_minimum_balance(); // single signer let account_info_iter = &mut signer_accounts.iter_mut(); @@ -1942,6 +2147,7 @@ mod tests { initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(), vec![ &mut multisig_account, + &mut rent_sysvar, &mut account_info_iter.next().unwrap(), ], ) @@ -1959,6 +2165,7 @@ mod tests { .unwrap(), vec![ &mut multisig_delegate_account, + &mut rent_sysvar, &mut account_info_iter.next().unwrap(), &mut account_info_iter.next().unwrap(), &mut account_info_iter.next().unwrap(), @@ -1977,7 +2184,12 @@ mod tests { // create account with multisig owner do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &multisig_key).unwrap(), - vec![&mut account, &mut mint_account, &mut multisig_account], + vec![ + &mut account, + &mut mint_account, + &mut multisig_account, + &mut rent_sysvar, + ], ) .unwrap(); @@ -1994,6 +2206,7 @@ mod tests { &mut account2_account, &mut mint_account, &mut multisig_account, + &mut rent_sysvar, ], ) .unwrap(); @@ -2001,7 +2214,7 @@ mod tests { // create new mint with multisig owner do_process_instruction( initialize_mint(&program_id, &mint_key, &multisig_key, None, 2).unwrap(), - vec![&mut mint_account], + vec![&mut mint_account, &mut rent_sysvar], ) .unwrap(); let account_info_iter = &mut signer_accounts.iter_mut(); @@ -2168,15 +2381,18 @@ mod tests { // freeze account let account3_key = pubkey_rand(); - let mut account3_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account3_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let mint2_key = pubkey_rand(); - let mut mint2_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mint2_account = + SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); do_process_instruction( initialize_account(&program_id, &account3_key, &mint2_key, &owner_key).unwrap(), vec![ &mut account3_account, &mut mint2_account, &mut owner_account, + &mut rent_sysvar, ], ) .unwrap(); @@ -2189,7 +2405,7 @@ mod tests { 2, ) .unwrap(), - vec![&mut mint2_account], + vec![&mut mint2_account, &mut rent_sysvar], ) .unwrap(); let account_info_iter = &mut signer_accounts.iter_mut(); @@ -2399,17 +2615,22 @@ mod tests { fn test_close_account() { let program_id = pubkey_rand(); let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(42, size_of::(), &program_id); + let mut account_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(2, size_of::(), &program_id); + let mut account2_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let account3_key = pubkey_rand(); - let mut account3_account = SolanaAccount::new(2, size_of::(), &program_id); + let mut account3_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); let owner2_key = pubkey_rand(); let mut owner2_account = SolanaAccount::default(); + let mut rent_sysvar = rent_sysvar(); // uninitialized assert_eq!( @@ -2427,17 +2648,27 @@ mod tests { // initialize and mint to non-native account do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], ) .unwrap(); do_process_instruction( initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], + vec![&mut mint_account, &mut rent_sysvar], ) .unwrap(); do_process_instruction( mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 42).unwrap(), - vec![&mut mint_account, &mut account_account, &mut owner_account], + vec![ + &mut mint_account, + &mut account_account, + &mut owner_account, + &mut rent_sysvar, + ], ) .unwrap(); let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); @@ -2452,12 +2683,17 @@ mod tests { &owner_key, ) .unwrap(), - vec![&mut account2_account, &mut mint_account, &mut owner_account], + vec![ + &mut account2_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], ) .unwrap(); let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); assert!(account.is_native); - assert_eq!(account.amount, 2); + assert_eq!(account.amount, account_minimum_balance()); // close non-native account with balance assert_eq!( @@ -2471,7 +2707,7 @@ mod tests { ], ) ); - assert_eq!(account_account.lamports, 42); + assert_eq!(account_account.lamports, account_minimum_balance()); // empty account do_process_instruction( @@ -2506,16 +2742,23 @@ mod tests { let account: &mut Account = state::unpack_unchecked(&mut account_account.data).unwrap(); assert_eq!(account_account.lamports, 0); assert_eq!(account.amount, 0); - assert_eq!(account3_account.lamports, 44); + assert_eq!(account3_account.lamports, 2 * account_minimum_balance()); // fund and initialize new non-native account to test close authority let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(42, size_of::(), &program_id); + let mut account_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let owner2_key = pubkey_rand(); - let mut owner2_account = SolanaAccount::new(42, size_of::(), &program_id); + let mut owner2_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], ) .unwrap(); account_account.lamports = 2; @@ -2559,7 +2802,7 @@ mod tests { .unwrap(); assert_eq!(account_account.lamports, 0); assert_eq!(account.amount, 0); - assert_eq!(account3_account.lamports, 46); + assert_eq!(account3_account.lamports, 2 * account_minimum_balance() + 2); // close native account do_process_instruction( @@ -2575,21 +2818,25 @@ mod tests { assert!(account.is_native); assert_eq!(account_account.lamports, 0); assert_eq!(account.amount, 0); - assert_eq!(account3_account.lamports, 48); + assert_eq!(account3_account.lamports, 3 * account_minimum_balance() + 2); } #[test] fn test_native_token() { let program_id = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(42, size_of::(), &program_id); + let mut account_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(2, size_of::(), &program_id); + let mut account2_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let account3_key = pubkey_rand(); - let mut account3_account = SolanaAccount::new(2, 0, &program_id); + let mut account3_account = SolanaAccount::new(account_minimum_balance(), 0, &program_id); let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); + let mut rent_sysvar = rent_sysvar(); // initialize native account do_process_instruction( @@ -2600,12 +2847,17 @@ mod tests { &owner_key, ) .unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], ) .unwrap(); let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); assert!(account.is_native); - assert_eq!(account.amount, 42); + assert_eq!(account.amount, account_minimum_balance()); // initialize native account do_process_instruction( @@ -2616,12 +2868,17 @@ mod tests { &owner_key, ) .unwrap(), - vec![&mut account2_account, &mut mint_account, &mut owner_account], + vec![ + &mut account2_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], ) .unwrap(); let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); assert!(account.is_native); - assert_eq!(account.amount, 2); + assert_eq!(account.amount, account_minimum_balance()); // mint_to unsupported assert_eq!( @@ -2670,12 +2927,12 @@ mod tests { let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); assert!(account.is_native); - assert_eq!(account_account.lamports, 2); - assert_eq!(account.amount, 2); + assert_eq!(account_account.lamports, account_minimum_balance() - 40); + assert_eq!(account.amount, account_minimum_balance() - 40); let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); assert!(account.is_native); - assert_eq!(account2_account.lamports, 42); - assert_eq!(account.amount, 42); + assert_eq!(account2_account.lamports, account_minimum_balance() + 40); + assert_eq!(account.amount, account_minimum_balance() + 40); // close native account do_process_instruction( @@ -2691,16 +2948,21 @@ mod tests { assert!(account.is_native); assert_eq!(account_account.lamports, 0); assert_eq!(account.amount, 0); - assert_eq!(account3_account.lamports, 4); + assert_eq!( + account3_account.lamports, + 2 * account_minimum_balance() - 40 + ); } #[test] fn test_overflow() { let program_id = pubkey_rand(); let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account2_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); let owner2_key = pubkey_rand(); @@ -2708,12 +2970,19 @@ mod tests { let mint_owner_key = pubkey_rand(); let mut mint_owner_account = SolanaAccount::default(); let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + let mut rent_sysvar = rent_sysvar(); // create victim account do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], ) .unwrap(); @@ -2724,6 +2993,7 @@ mod tests { &mut account2_account, &mut mint_account, &mut owner2_account, + &mut rent_sysvar, ], ) .unwrap(); @@ -2731,7 +3001,7 @@ mod tests { // create new mint with owner do_process_instruction( initialize_mint(&program_id, &mint_key, &mint_owner_key, None, 2).unwrap(), - vec![&mut mint_account], + vec![&mut mint_account, &mut rent_sysvar], ) .unwrap(); @@ -2847,32 +3117,46 @@ mod tests { fn test_frozen() { let program_id = pubkey_rand(); let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let account2_key = pubkey_rand(); - let mut account2_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account2_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + let mut rent_sysvar = rent_sysvar(); // create account do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], ) .unwrap(); // create another account do_process_instruction( initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), - vec![&mut account2_account, &mut mint_account, &mut owner_account], + vec![ + &mut account2_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], ) .unwrap(); // create new mint and fund first account do_process_instruction( initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account], + vec![&mut mint_account, &mut rent_sysvar], ) .unwrap(); do_process_instruction( @@ -2961,7 +3245,7 @@ mod tests { Err(TokenError::AccountFrozen.into()), do_process_instruction( revoke(&program_id, &account_key, &owner_key, &[]).unwrap(), - vec![&mut account_account, &mut owner_account,], + vec![&mut account_account, &mut owner_account], ) ); @@ -3006,7 +3290,8 @@ mod tests { fn test_freeze_account() { let program_id = pubkey_rand(); let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut account_account = + SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let account_owner_key = pubkey_rand(); let mut account_owner_account = SolanaAccount::default(); let owner_key = pubkey_rand(); @@ -3014,7 +3299,9 @@ mod tests { let owner2_key = pubkey_rand(); let mut owner2_account = SolanaAccount::default(); let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + let mut rent_sysvar = rent_sysvar(); // create account do_process_instruction( @@ -3023,6 +3310,7 @@ mod tests { &mut account_account, &mut mint_account, &mut account_owner_account, + &mut rent_sysvar, ], ) .unwrap(); @@ -3030,7 +3318,7 @@ mod tests { // create new mint with owner different from account owner do_process_instruction( initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account], + vec![&mut mint_account, &mut rent_sysvar], ) .unwrap(); do_process_instruction( From e91c45cf1717147a6974112e74288d19daba0904 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Thu, 27 Aug 2020 00:50:38 -0600 Subject: [PATCH 025/248] Add Account.rent_exempt_reserve --- program/src/processor.rs | 29 +++++++++++++++++------------ program/src/state.rs | 3 +++ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index eb72297..fe9ce1e 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -41,7 +41,7 @@ impl Processor { return Err(TokenError::AlreadyInUse.into()); } - if mint_info.lamports() < rent.minimum_balance(mint_info_data_len) { + if !rent.is_exempt(mint_info.lamports(), mint_info_data_len) { return Err(TokenError::NotRentExempt.into()); } @@ -68,7 +68,7 @@ impl Processor { return Err(TokenError::AlreadyInUse.into()); } - if new_account_info.lamports() < rent.minimum_balance(new_account_info_data_len) { + if !rent.is_exempt(new_account_info.lamports(), new_account_info_data_len) { return Err(TokenError::NotRentExempt.into()); } @@ -80,6 +80,7 @@ impl Processor { if *mint_info.key == crate::native_mint::id() { account.is_native = true; account.amount = new_account_info.lamports(); + account.rent_exempt_reserve = rent.minimum_balance(new_account_info_data_len); } else { account.is_native = false; account.amount = 0; @@ -101,7 +102,7 @@ impl Processor { return Err(TokenError::AlreadyInUse.into()); } - if multisig_info.lamports() < rent.minimum_balance(multisig_info_data_len) { + if !rent.is_exempt(multisig_info.lamports(), multisig_info_data_len) { return Err(TokenError::NotRentExempt.into()); } @@ -183,6 +184,10 @@ impl Processor { .ok_or(TokenError::Overflow)?; if source_account.is_native { + // Ensure that wrapped SOL accounts remain rent-exempt + if source_account_info.lamports() < source_account.rent_exempt_reserve + amount { + return Err(TokenError::InsufficientFunds.into()); + } **source_account_info.lamports.borrow_mut() -= amount; **dest_account_info.lamports.borrow_mut() += amount; } @@ -2827,8 +2832,11 @@ mod tests { let mut mint_account = SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let account_key = pubkey_rand(); - let mut account_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account_account = SolanaAccount::new( + account_minimum_balance() + 40, + size_of::(), + &program_id, + ); let account2_key = pubkey_rand(); let mut account2_account = SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); @@ -2857,7 +2865,7 @@ mod tests { .unwrap(); let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); assert!(account.is_native); - assert_eq!(account.amount, account_minimum_balance()); + assert_eq!(account.amount, account_minimum_balance() + 40); // initialize native account do_process_instruction( @@ -2927,8 +2935,8 @@ mod tests { let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); assert!(account.is_native); - assert_eq!(account_account.lamports, account_minimum_balance() - 40); - assert_eq!(account.amount, account_minimum_balance() - 40); + assert_eq!(account_account.lamports, account_minimum_balance()); + assert_eq!(account.amount, account_minimum_balance()); let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); assert!(account.is_native); assert_eq!(account2_account.lamports, account_minimum_balance() + 40); @@ -2948,10 +2956,7 @@ mod tests { assert!(account.is_native); assert_eq!(account_account.lamports, 0); assert_eq!(account.amount, 0); - assert_eq!( - account3_account.lamports, - 2 * account_minimum_balance() - 40 - ); + assert_eq!(account3_account.lamports, 2 * account_minimum_balance()); } #[test] diff --git a/program/src/state.rs b/program/src/state.rs index 423fa64..f53bbca 100644 --- a/program/src/state.rs +++ b/program/src/state.rs @@ -46,6 +46,9 @@ pub struct Account { pub delegated_amount: u64, /// Optional authority to close the account. pub close_authority: COption, + /// An Account is required to be rent-exempt. This value logs the reserve required to be + /// rent-exempt so that wrapped SOL accounts do not drop below this threshold. + pub rent_exempt_reserve: u64, } impl Account { /// Checks if account is frozen From c63c00cfe0ac64f6ccc123a1c51657c026689976 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Thu, 27 Aug 2020 01:02:30 -0600 Subject: [PATCH 026/248] C headers --- program/inc/token.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/program/inc/token.h b/program/inc/token.h index 44f8423..b475b9c 100644 --- a/program/inc/token.h +++ b/program/inc/token.h @@ -118,6 +118,7 @@ typedef enum Token_TokenInstruction_Tag { * Accounts expected by this instruction: * * 0. `[writable]` The mint to initialize. + * 1. `[]` Rent sysvar * */ Token_TokenInstruction_InitializeMint, @@ -134,6 +135,7 @@ typedef enum Token_TokenInstruction_Tag { * 0. `[writable]` The account to initialize. * 1. `[]` The mint this account will be associated with. * 2. `[]` The new account's owner/multisignature. + * 3. `[]` Rent sysvar */ Token_TokenInstruction_InitializeAccount, /** @@ -150,7 +152,8 @@ typedef enum Token_TokenInstruction_Tag { * Accounts expected by this instruction: * * 0. `[writable]` The multisignature account to initialize. - * 1. ..1+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. + * 2. `[]` Rent sysvar + * 3. ..2+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. */ Token_TokenInstruction_InitializeMultisig, /** @@ -441,6 +444,11 @@ typedef struct Token_Account { * Optional authority to close the account. */ Token_COption_Pubkey close_authority; + /** + * An Account is required to be rent-exempt. This value logs the reserve required to be + * rent-exempt so that wrapped SOL accounts do not drop below this threshold. + */ + uint64_t rent_exempt_reserve; } Token_Account; /** From ec61f707508a3476ef13d8ad56922b51ed678300 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Thu, 27 Aug 2020 01:20:49 -0600 Subject: [PATCH 027/248] Combine Account is_native and rent_exempt_reserve fields; wrapped SOL amount reflects only amoutn over rent_exempt_reserve --- program/src/processor.rs | 81 ++++++++++++++++++++++++++-------------- program/src/state.rs | 13 ++++--- 2 files changed, 60 insertions(+), 34 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index fe9ce1e..7c1302e 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -78,11 +78,11 @@ impl Processor { account.delegated_amount = 0; account.state = AccountState::Initialized; if *mint_info.key == crate::native_mint::id() { - account.is_native = true; - account.amount = new_account_info.lamports(); - account.rent_exempt_reserve = rent.minimum_balance(new_account_info_data_len); + let rent_exempt_reserve = rent.minimum_balance(new_account_info_data_len); + account.is_native = COption::Some(rent_exempt_reserve); + account.amount = new_account_info.lamports() - rent_exempt_reserve; } else { - account.is_native = false; + account.is_native = COption::None; account.amount = 0; }; @@ -183,11 +183,7 @@ impl Processor { .checked_add(amount) .ok_or(TokenError::Overflow)?; - if source_account.is_native { - // Ensure that wrapped SOL accounts remain rent-exempt - if source_account_info.lamports() < source_account.rent_exempt_reserve + amount { - return Err(TokenError::InsufficientFunds.into()); - } + if source_account.is_native() { **source_account_info.lamports.borrow_mut() -= amount; **dest_account_info.lamports.borrow_mut() += amount; } @@ -362,7 +358,7 @@ impl Processor { return Err(TokenError::AccountFrozen.into()); } - if dest_account.is_native { + if dest_account.is_native() { return Err(TokenError::NativeNotSupported.into()); } if mint_info.key != &dest_account.mint { @@ -407,7 +403,7 @@ impl Processor { let mut source_data = source_account_info.data.borrow_mut(); let source_account: &mut Account = state::unpack(&mut source_data)?; - if source_account.is_native { + if source_account.is_native() { return Err(TokenError::NativeNotSupported.into()); } if source_account.amount < amount { @@ -457,7 +453,7 @@ impl Processor { let mut source_data = source_account_info.data.borrow_mut(); let source_account: &mut Account = state::unpack(&mut source_data)?; - if !source_account.is_native && source_account.amount != 0 { + if !source_account.is_native() && source_account.amount != 0 { return Err(TokenError::NonNativeHasBalance.into()); } @@ -493,7 +489,7 @@ impl Processor { let mut source_data = source_account_info.data.borrow_mut(); let source_account: &mut Account = state::unpack(&mut source_data)?; - if source_account.is_native { + if source_account.is_native() { return Err(TokenError::NativeNotSupported.into()); } if mint_info.key != &source_account.mint { @@ -2626,8 +2622,11 @@ mod tests { let mut account_account = SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let account2_key = pubkey_rand(); - let mut account2_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account2_account = SolanaAccount::new( + account_minimum_balance() + 42, + size_of::(), + &program_id, + ); let account3_key = pubkey_rand(); let mut account3_account = SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); @@ -2697,8 +2696,8 @@ mod tests { ) .unwrap(); let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); - assert!(account.is_native); - assert_eq!(account.amount, account_minimum_balance()); + assert!(account.is_native()); + assert_eq!(account.amount, 42); // close non-native account with balance assert_eq!( @@ -2820,10 +2819,13 @@ mod tests { ) .unwrap(); let account: &mut Account = state::unpack_unchecked(&mut account2_account.data).unwrap(); - assert!(account.is_native); + assert!(account.is_native()); assert_eq!(account_account.lamports, 0); assert_eq!(account.amount, 0); - assert_eq!(account3_account.lamports, 3 * account_minimum_balance() + 2); + assert_eq!( + account3_account.lamports, + 3 * account_minimum_balance() + 2 + 42 + ); } #[test] @@ -2864,8 +2866,8 @@ mod tests { ) .unwrap(); let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert!(account.is_native); - assert_eq!(account.amount, account_minimum_balance() + 40); + assert!(account.is_native()); + assert_eq!(account.amount, 40); // initialize native account do_process_instruction( @@ -2885,8 +2887,8 @@ mod tests { ) .unwrap(); let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); - assert!(account.is_native); - assert_eq!(account.amount, account_minimum_balance()); + assert!(account.is_native()); + assert_eq!(account.amount, 0); // mint_to unsupported assert_eq!( @@ -2914,7 +2916,28 @@ mod tests { ) ); - // initialize native account + // ensure can't transfer below rent-exempt reserve + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &owner_key, + &[], + 50, + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner_account, + ], + ) + ); + + // transfer between native accounts do_process_instruction( transfer( &program_id, @@ -2934,13 +2957,13 @@ mod tests { .unwrap(); let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert!(account.is_native); + assert!(account.is_native()); assert_eq!(account_account.lamports, account_minimum_balance()); - assert_eq!(account.amount, account_minimum_balance()); + assert_eq!(account.amount, 0); let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); - assert!(account.is_native); + assert!(account.is_native()); assert_eq!(account2_account.lamports, account_minimum_balance() + 40); - assert_eq!(account.amount, account_minimum_balance() + 40); + assert_eq!(account.amount, 40); // close native account do_process_instruction( @@ -2953,7 +2976,7 @@ mod tests { ) .unwrap(); let account: &mut Account = state::unpack_unchecked(&mut account_account.data).unwrap(); - assert!(account.is_native); + assert!(account.is_native()); assert_eq!(account_account.lamports, 0); assert_eq!(account.amount, 0); assert_eq!(account3_account.lamports, 2 * account_minimum_balance()); diff --git a/program/src/state.rs b/program/src/state.rs index f53bbca..7f920ef 100644 --- a/program/src/state.rs +++ b/program/src/state.rs @@ -40,21 +40,24 @@ pub struct Account { pub delegate: COption, /// The account's state pub state: AccountState, - /// Is this a native token - pub is_native: bool, + /// If is_some, this is a native token, and the value logs the rent-exempt reserve. An Account + /// is required to be rent-exempt, so the value is used by the Processor to ensure that wrapped + /// SOL accounts do not drop below this threshold. + pub is_native: COption, /// The amount delegated pub delegated_amount: u64, /// Optional authority to close the account. pub close_authority: COption, - /// An Account is required to be rent-exempt. This value logs the reserve required to be - /// rent-exempt so that wrapped SOL accounts do not drop below this threshold. - pub rent_exempt_reserve: u64, } impl Account { /// Checks if account is frozen pub fn is_frozen(&self) -> bool { self.state == AccountState::Frozen } + /// Checks if account is native + pub fn is_native(&self) -> bool { + self.is_native.is_some() + } } impl IsInitialized for Account { fn is_initialized(&self) -> bool { From 58ac78af4eed1ab1a99230916309beb60aa9f2d3 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Thu, 27 Aug 2020 09:40:39 -0600 Subject: [PATCH 028/248] More C headers --- program/inc/token.h | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/program/inc/token.h b/program/inc/token.h index b475b9c..ac8cc98 100644 --- a/program/inc/token.h +++ b/program/inc/token.h @@ -407,6 +407,31 @@ typedef struct Token_Mint { Token_COption_Pubkey freeze_authority; } Token_Mint; +/** + * A C representation of Rust's `std::option::Option` + */ +typedef enum Token_COption_u64_Tag { + /** + * No value + */ + Token_COption_u64_None_u64, + /** + * Some value `T` + */ + Token_COption_u64_Some_u64, +} Token_COption_u64_Tag; + +typedef struct Token_COption_u64_Token_Some_Body_u64 { + uint64_t _0; +} Token_COption_u64_Token_Some_Body_u64; + +typedef struct Token_COption_u64 { + Token_COption_u64_Tag tag; + union { + Token_COption_u64_Token_Some_Body_u64 some; + }; +} Token_COption_u64; + /** * Account data. */ @@ -433,9 +458,11 @@ typedef struct Token_Account { */ Token_AccountState state; /** - * Is this a native token + * If is_some, this is a native token, and the value logs the rent-exempt reserve. An Account + * is required to be rent-exempt, so the value is used by the Processor to ensure that wrapped + * SOL accounts do not drop below this threshold. */ - bool is_native; + Token_COption_u64 is_native; /** * The amount delegated */ @@ -444,11 +471,6 @@ typedef struct Token_Account { * Optional authority to close the account. */ Token_COption_Pubkey close_authority; - /** - * An Account is required to be rent-exempt. This value logs the reserve required to be - * rent-exempt so that wrapped SOL accounts do not drop below this threshold. - */ - uint64_t rent_exempt_reserve; } Token_Account; /** From f6a2b204f410d199cc4c110cbaf6c8ad7f93b29e Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Thu, 27 Aug 2020 13:06:23 -0600 Subject: [PATCH 029/248] Reinstate Mint supply (#342) * Reinstate Mint supply, and adjust with Minto & Burn * C headers * Patch token-cli * Patch token-swap --- program/inc/token.h | 18 ++-- program/src/instruction.rs | 18 ++-- program/src/processor.rs | 213 ++++++++++++++++++++++++++++--------- program/src/state.rs | 2 + 4 files changed, 187 insertions(+), 64 deletions(-) diff --git a/program/inc/token.h b/program/inc/token.h index ac8cc98..a05c087 100644 --- a/program/inc/token.h +++ b/program/inc/token.h @@ -152,8 +152,8 @@ typedef enum Token_TokenInstruction_Tag { * Accounts expected by this instruction: * * 0. `[writable]` The multisignature account to initialize. - * 2. `[]` Rent sysvar - * 3. ..2+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. + * 1. `[]` Rent sysvar + * 2. ..2+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. */ Token_TokenInstruction_InitializeMultisig, /** @@ -219,7 +219,7 @@ typedef enum Token_TokenInstruction_Tag { * * Multisignature authority * 0. `[writable]` The mint or account to change the authority of. * 1. `[]` The mint's or account's multisignature authority. - * 3. ..3+M '[signer]' M signer accounts + * 2. ..2+M '[signer]' M signer accounts */ Token_TokenInstruction_SetAuthority, /** @@ -247,12 +247,14 @@ typedef enum Token_TokenInstruction_Tag { * * * Single owner/delegate * 0. `[writable]` The account to burn from. - * 1. `[signer]` The account's owner/delegate. + * 1. '[writable]' The token mint. + * 2. `[signer]` The account's owner/delegate. * * * Multisignature owner/delegate * 0. `[writable]` The account to burn from. - * 1. `[]` The account's multisignature owner/delegate. - * 2. ..2+M '[signer]' M signer accounts. + * 1. '[writable]' The token mint. + * 2. `[]` The account's multisignature owner/delegate. + * 3. ..3+M '[signer]' M signer accounts. */ Token_TokenInstruction_Burn, /** @@ -393,6 +395,10 @@ typedef struct Token_Mint { * further tokens may be minted. */ Token_COption_Pubkey mint_authority; + /** + * Total supply of tokens. + */ + uint64_t supply; /** * Number of base 10 digits to the right of the decimal place. */ diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 79bd0a0..0165600 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -64,8 +64,8 @@ pub enum TokenInstruction { /// Accounts expected by this instruction: /// /// 0. `[writable]` The multisignature account to initialize. - /// 2. `[]` Rent sysvar - /// 3. ..2+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. + /// 1. `[]` Rent sysvar + /// 2. ..2+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. InitializeMultisig { /// The number of signers (M) required to validate this multisignature account. m: u8, @@ -133,7 +133,7 @@ pub enum TokenInstruction { /// * Multisignature authority /// 0. `[writable]` The mint or account to change the authority of. /// 1. `[]` The mint's or account's multisignature authority. - /// 3. ..3+M '[signer]' M signer accounts + /// 2. ..2+M '[signer]' M signer accounts SetAuthority { /// The type of authority to update. authority_type: AuthorityType, @@ -165,12 +165,14 @@ pub enum TokenInstruction { /// /// * Single owner/delegate /// 0. `[writable]` The account to burn from. - /// 1. `[signer]` The account's owner/delegate. + /// 1. '[writable]' The token mint. + /// 2. `[signer]` The account's owner/delegate. /// /// * Multisignature owner/delegate /// 0. `[writable]` The account to burn from. - /// 1. `[]` The account's multisignature owner/delegate. - /// 2. ..2+M '[signer]' M signer accounts. + /// 1. '[writable]' The token mint. + /// 2. `[]` The account's multisignature owner/delegate. + /// 3. ..3+M '[signer]' M signer accounts. Burn { /// The amount of tokens to burn. amount: u64, @@ -696,14 +698,16 @@ pub fn mint_to( pub fn burn( token_program_id: &Pubkey, account_pubkey: &Pubkey, + mint_pubkey: &Pubkey, authority_pubkey: &Pubkey, signer_pubkeys: &[&Pubkey], amount: u64, ) -> Result { let data = TokenInstruction::Burn { amount }.pack()?; - let mut accounts = Vec::with_capacity(2 + signer_pubkeys.len()); + let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); accounts.push(AccountMeta::new(*account_pubkey, false)); + accounts.push(AccountMeta::new(*mint_pubkey, false)); accounts.push(AccountMeta::new_readonly( *authority_pubkey, signer_pubkeys.is_empty(), diff --git a/program/src/processor.rs b/program/src/processor.rs index 7c1302e..15181e0 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -387,6 +387,11 @@ impl Processor { .checked_add(amount) .ok_or(TokenError::Overflow)?; + mint.supply = mint + .supply + .checked_add(amount) + .ok_or(TokenError::Overflow)?; + Ok(()) } @@ -398,14 +403,21 @@ impl Processor { ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let source_account_info = next_account_info(account_info_iter)?; + let mint_info = next_account_info(account_info_iter)?; let authority_info = next_account_info(account_info_iter)?; + let mut mint_data = mint_info.data.borrow_mut(); + let mint: &mut Mint = state::unpack(&mut mint_data)?; + let mut source_data = source_account_info.data.borrow_mut(); let source_account: &mut Account = state::unpack(&mut source_data)?; if source_account.is_native() { return Err(TokenError::NativeNotSupported.into()); } + if mint_info.key != &source_account.mint { + return Err(TokenError::MintMismatch.into()); + } if source_account.amount < amount { return Err(TokenError::InsufficientFunds.into()); } @@ -439,6 +451,7 @@ impl Processor { } source_account.amount -= amount; + mint.supply -= amount; Ok(()) } @@ -1268,6 +1281,7 @@ mod tests { *mint, Mint { mint_authority: COption::Some(owner_key), + supply: 0, decimals, is_initialized: true, freeze_authority: COption::None, @@ -1832,13 +1846,26 @@ mod tests { .unwrap(); // mint to + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 42).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + + let mint: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); + assert_eq!(mint.supply, 42); + let dest_account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + assert_eq!(dest_account.amount, 42); + + // mint to another account to test supply accumulation do_process_instruction( mint_to(&program_id, &mint_key, &account2_key, &owner_key, &[], 42).unwrap(), vec![&mut mint_account, &mut account2_account, &mut owner_account], ) .unwrap(); - let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); + let mint: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); + assert_eq!(mint.supply, 84); let dest_account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); assert_eq!(dest_account.amount, 42); @@ -2010,13 +2037,18 @@ mod tests { .unwrap(); // missing signer - let mut instruction = burn(&program_id, &account_key, &delegate_key, &[], 42).unwrap(); + let mut instruction = + burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 42).unwrap(); instruction.accounts[1].is_signer = false; assert_eq!( Err(TokenError::OwnerMismatch.into()), do_process_instruction( instruction, - vec![&mut account_account, &mut delegate_account], + vec![ + &mut account_account, + &mut mint_account, + &mut delegate_account + ], ) ); @@ -2024,19 +2056,29 @@ mod tests { assert_eq!( Err(TokenError::OwnerMismatch.into()), do_process_instruction( - burn(&program_id, &account_key, &owner2_key, &[], 42).unwrap(), - vec![&mut account_account, &mut owner2_account], + burn(&program_id, &account_key, &mint_key, &owner2_key, &[], 42).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner2_account], + ) + ); + + // mint mismatch + assert_eq!( + Err(TokenError::MintMismatch.into()), + do_process_instruction( + burn(&program_id, &mismatch_key, &mint_key, &owner_key, &[], 42).unwrap(), + vec![&mut mismatch_account, &mut mint_account, &mut owner_account], ) ); // burn do_process_instruction( - burn(&program_id, &account_key, &owner_key, &[], 42).unwrap(), - vec![&mut account_account, &mut owner_account], + burn(&program_id, &account_key, &mint_key, &owner_key, &[], 42).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], ) .unwrap(); - let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); + let mint: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); + assert_eq!(mint.supply, 1000 - 42); let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); assert_eq!(account.amount, 1000 - 42); @@ -2044,8 +2086,16 @@ mod tests { assert_eq!( Err(TokenError::InsufficientFunds.into()), do_process_instruction( - burn(&program_id, &account_key, &owner_key, &[], 100_000_000).unwrap(), - vec![&mut account_account, &mut owner_account], + burn( + &program_id, + &account_key, + &mint_key, + &owner_key, + &[], + 100_000_000 + ) + .unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], ) ); @@ -2072,20 +2122,33 @@ mod tests { assert_eq!( Err(TokenError::InsufficientFunds.into()), do_process_instruction( - burn(&program_id, &account_key, &owner_key, &[], 100_000_000).unwrap(), - vec![&mut account_account, &mut owner_account], + burn( + &program_id, + &account_key, + &mint_key, + &owner_key, + &[], + 100_000_000 + ) + .unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], ) ); // burn via delegate do_process_instruction( - burn(&program_id, &account_key, &delegate_key, &[], 84).unwrap(), - vec![&mut account_account, &mut delegate_account], + burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 84).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut delegate_account, + ], ) .unwrap(); // match - let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); + let mint: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); + assert_eq!(mint.supply, 1000 - 42 - 84); let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); assert_eq!(account.amount, 1000 - 42 - 84); @@ -2093,8 +2156,20 @@ mod tests { assert_eq!( Err(TokenError::OwnerMismatch.into()), do_process_instruction( - burn(&program_id, &account_key, &delegate_key, &[], 100).unwrap(), - vec![&mut account_account, &mut delegate_account], + burn( + &program_id, + &account_key, + &mint_key, + &delegate_key, + &[], + 100 + ) + .unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut delegate_account + ], ) ); } @@ -2338,6 +2413,7 @@ mod tests { burn( &program_id, &account_key, + &mint_key, &multisig_key, &[&signer_keys[0]], 42, @@ -2345,6 +2421,7 @@ mod tests { .unwrap(), vec![ &mut account, + &mut mint_account, &mut multisig_account, &mut account_info_iter.next().unwrap(), ], @@ -2357,6 +2434,7 @@ mod tests { burn( &program_id, &account_key, + &mint_key, &multisig_delegate_key, &signer_key_refs, 42, @@ -2364,6 +2442,7 @@ mod tests { .unwrap(), vec![ &mut account, + &mut mint_account, &mut multisig_delegate_account, &mut account_info_iter.next().unwrap(), &mut account_info_iter.next().unwrap(), @@ -2715,8 +2794,8 @@ mod tests { // empty account do_process_instruction( - burn(&program_id, &account_key, &owner_key, &[], 42).unwrap(), - vec![&mut account_account, &mut owner_account], + burn(&program_id, &account_key, &mint_key, &owner_key, &[], 42).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], ) .unwrap(); @@ -2908,11 +2987,32 @@ mod tests { ); // burn unsupported + let bogus_mint_key = pubkey_rand(); + let mut bogus_mint_account = + SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + do_process_instruction( + initialize_mint(&program_id, &bogus_mint_key, &owner_key, None, 2).unwrap(), + vec![&mut bogus_mint_account, &mut rent_sysvar], + ) + .unwrap(); + assert_eq!( Err(TokenError::NativeNotSupported.into()), do_process_instruction( - burn(&program_id, &account_key, &owner_key, &[], 42).unwrap(), - vec![&mut account_account, &mut owner_account], + burn( + &program_id, + &account_key, + &bogus_mint_key, + &owner_key, + &[], + 42 + ) + .unwrap(), + vec![ + &mut account_account, + &mut bogus_mint_account, + &mut owner_account + ], ) ); @@ -3002,7 +3102,7 @@ mod tests { SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let mut rent_sysvar = rent_sysvar(); - // create victim account + // create an account do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), vec![ @@ -3033,28 +3133,7 @@ mod tests { ) .unwrap(); - // mint the max to attacker - do_process_instruction( - mint_to( - &program_id, - &mint_key, - &account2_key, - &mint_owner_key, - &[], - 42, - ) - .unwrap(), - vec![ - &mut mint_account, - &mut account2_account, - &mut mint_owner_account, - ], - ) - .unwrap(); - let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); - assert_eq!(account.amount, 42); - - // mint the max to victum + // mint the max to an account do_process_instruction( mint_to( &program_id, @@ -3075,7 +3154,7 @@ mod tests { let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); assert_eq!(account.amount, u64::MAX); - // mint one more + // attempt to mint one more to account assert_eq!( Err(TokenError::Overflow.into()), do_process_instruction( @@ -3095,10 +3174,39 @@ mod tests { ], ) ); + let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + assert_eq!(account.amount, u64::MAX); - // mint back to large amount + // atttempt to mint one more to the other account + assert_eq!( + Err(TokenError::Overflow.into()), + do_process_instruction( + mint_to( + &program_id, + &mint_key, + &account2_key, + &mint_owner_key, + &[], + 1, + ) + .unwrap(), + vec![ + &mut mint_account, + &mut account2_account, + &mut mint_owner_account, + ], + ) + ); + + // burn some of the supply + do_process_instruction( + burn(&program_id, &account_key, &mint_key, &owner_key, &[], 100).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - account.amount = 0; + assert_eq!(account.amount, u64::MAX - 100); + do_process_instruction( mint_to( &program_id, @@ -3106,7 +3214,7 @@ mod tests { &account_key, &mint_owner_key, &[], - u64::MAX, + 100, ) .unwrap(), vec![ @@ -3119,7 +3227,10 @@ mod tests { let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); assert_eq!(account.amount, u64::MAX); - // transfer to burn victim + // manipulate account balance to attempt overflow transfer + let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); + account.amount = 1; + assert_eq!( Err(TokenError::Overflow.into()), do_process_instruction( @@ -3308,8 +3419,8 @@ mod tests { assert_eq!( Err(TokenError::AccountFrozen.into()), do_process_instruction( - burn(&program_id, &account_key, &owner_key, &[], 100).unwrap(), - vec![&mut account_account, &mut owner_account,], + burn(&program_id, &account_key, &mint_key, &owner_key, &[], 100).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], ) ); } diff --git a/program/src/state.rs b/program/src/state.rs index 7f920ef..5b3981c 100644 --- a/program/src/state.rs +++ b/program/src/state.rs @@ -12,6 +12,8 @@ pub struct Mint { /// mint creation. If no mint authority is present then the mint has a fixed supply and no /// further tokens may be minted. pub mint_authority: COption, + /// Total supply of tokens. + pub supply: u64, /// Number of base 10 digits to the right of the decimal place. pub decimals: u8, /// Is `true` if this structure has been initialized From f3b30fee783d27708079c35a68683cb2a0d3dfca Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Thu, 27 Aug 2020 14:31:59 -0600 Subject: [PATCH 030/248] Use checked math everywhere (#346) --- program/src/processor.rs | 47 ++++++++++++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index 15181e0..e6dacf0 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -80,7 +80,10 @@ impl Processor { if *mint_info.key == crate::native_mint::id() { let rent_exempt_reserve = rent.minimum_balance(new_account_info_data_len); account.is_native = COption::Some(rent_exempt_reserve); - account.amount = new_account_info.lamports() - rent_exempt_reserve; + account.amount = new_account_info + .lamports() + .checked_sub(rent_exempt_reserve) + .ok_or(TokenError::Overflow)?; } else { account.is_native = COption::None; account.amount = 0; @@ -164,7 +167,10 @@ impl Processor { if source_account.delegated_amount < amount { return Err(TokenError::InsufficientFunds.into()); } - source_account.delegated_amount -= amount; + source_account.delegated_amount = source_account + .delegated_amount + .checked_sub(amount) + .ok_or(TokenError::Overflow)?; if source_account.delegated_amount == 0 { source_account.delegate = COption::None; } @@ -177,15 +183,25 @@ impl Processor { )?, }; - source_account.amount -= amount; + source_account.amount = source_account + .amount + .checked_sub(amount) + .ok_or(TokenError::Overflow)?; dest_account.amount = dest_account .amount .checked_add(amount) .ok_or(TokenError::Overflow)?; if source_account.is_native() { - **source_account_info.lamports.borrow_mut() -= amount; - **dest_account_info.lamports.borrow_mut() += amount; + let source_starting_lamports = source_account_info.lamports(); + **source_account_info.lamports.borrow_mut() = source_starting_lamports + .checked_sub(amount) + .ok_or(TokenError::Overflow)?; + + let dest_starting_lamports = dest_account_info.lamports(); + **dest_account_info.lamports.borrow_mut() = dest_starting_lamports + .checked_add(amount) + .ok_or(TokenError::Overflow)?; } Ok(()) @@ -437,7 +453,10 @@ impl Processor { if source_account.delegated_amount < amount { return Err(TokenError::InsufficientFunds.into()); } - source_account.delegated_amount -= amount; + source_account.delegated_amount = source_account + .delegated_amount + .checked_sub(amount) + .ok_or(TokenError::Overflow)?; if source_account.delegated_amount == 0 { source_account.delegate = COption::None; } @@ -450,8 +469,14 @@ impl Processor { )?, } - source_account.amount -= amount; - mint.supply -= amount; + source_account.amount = source_account + .amount + .checked_sub(amount) + .ok_or(TokenError::Overflow)?; + mint.supply = mint + .supply + .checked_sub(amount) + .ok_or(TokenError::Overflow)?; Ok(()) } @@ -480,7 +505,11 @@ impl Processor { account_info_iter.as_slice(), )?; - **dest_account_info.lamports.borrow_mut() += source_account_info.lamports(); + let dest_starting_lamports = dest_account_info.lamports(); + **dest_account_info.lamports.borrow_mut() = dest_starting_lamports + .checked_add(source_account_info.lamports()) + .ok_or(TokenError::Overflow)?; + **source_account_info.lamports.borrow_mut() = 0; source_account.amount = 0; From 2493fde3e276bbb13a4ae3cedb145d49347c4bf7 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Thu, 27 Aug 2020 15:22:03 -0600 Subject: [PATCH 031/248] Require valid Mint for InitializeAccount --- program/src/error.rs | 3 + program/src/instruction.rs | 6 +- program/src/processor.rs | 255 +++++++++++++++++++++---------------- 3 files changed, 155 insertions(+), 109 deletions(-) diff --git a/program/src/error.rs b/program/src/error.rs index 4a9e223..904dc21 100644 --- a/program/src/error.rs +++ b/program/src/error.rs @@ -13,6 +13,9 @@ pub enum TokenError { /// Insufficient funds for the operation requested. #[error("Insufficient funds")] InsufficientFunds, + /// Invalid Mint. + #[error("Invalid Mint")] + InvalidMint, /// Account not associated with this Mint. #[error("Account not associated with this Mint")] MintMismatch, diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 0165600..701f4bc 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -37,8 +37,10 @@ pub enum TokenInstruction { /// The freeze authority/multisignature of the mint. freeze_authority: COption, }, - /// Initializes a new account to hold tokens. If this account is associated with the native mint - /// then the token balance of the initialized account will be equal to the amount of SOL in the account. + /// Initializes a new account to hold tokens. If this account is associated with the native + /// mint then the token balance of the initialized account will be equal to the amount of SOL + /// in the account. If this account is associated with another mint, that mint must be + /// initialized before this command can succeed. /// /// The `InitializeAccount` instruction requires no signers and MUST be included within /// the same Transaction as the system program's `CreateInstruction` that creates the account diff --git a/program/src/processor.rs b/program/src/processor.rs index e6dacf0..ae39050 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -72,6 +72,12 @@ impl Processor { return Err(TokenError::NotRentExempt.into()); } + if *mint_info.key != crate::native_mint::id() { + let mut mint_info_data = mint_info.data.borrow_mut(); + let _: &mut Mint = state::unpack(&mut mint_info_data) + .map_err(|_| Into::::into(TokenError::InvalidMint))?; + } + account.mint = *mint_info.key; account.owner = *owner_info.key; account.delegate = COption::None; @@ -674,6 +680,7 @@ impl PrintProgramError for TokenError { info!("Error: Lamport balance below rent-exempt threshold") } TokenError::InsufficientFunds => info!("Error: insufficient funds"), + TokenError::InvalidMint => info!("Error: Invalid Mint"), TokenError::MintMismatch => info!("Error: Account not associated with this Mint"), TokenError::OwnerMismatch => info!("Error: owner does not match"), TokenError::FixedSupply => info!("Error: the total supply of this token is fixed"), @@ -765,7 +772,7 @@ mod tests { } #[test] - #[should_panic(expected = "Custom(2)")] + #[should_panic(expected = "Custom(3)")] fn test_error_unwrap() { Err::<(), ProgramError>(return_token_error_as_program_error()).unwrap(); } @@ -836,7 +843,8 @@ mod tests { let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(0, size_of::(), &program_id); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let mut rent_sysvar = rent_sysvar(); // account is not rent exempt @@ -855,6 +863,27 @@ mod tests { account_account.lamports = account_minimum_balance(); + // mint is not valid (not initialized) + assert_eq!( + Err(TokenError::InvalidMint.into()), + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar + ], + ) + ); + + // create mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + // create account do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), @@ -907,10 +936,15 @@ mod tests { let mut mint_account = SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let mint2_key = pubkey_rand(); - let mut mint2_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let mut rent_sysvar = rent_sysvar(); + // create mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + // create account do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), @@ -949,22 +983,19 @@ mod tests { // create mismatch account do_process_instruction( - initialize_account(&program_id, &mismatch_key, &mint2_key, &owner_key).unwrap(), + initialize_account(&program_id, &mismatch_key, &mint_key, &owner_key).unwrap(), vec![ &mut mismatch_account, - &mut mint2_account, + &mut mint_account, &mut owner_account, &mut rent_sysvar, ], ) .unwrap(); + let account: &mut Account = state::unpack(&mut mismatch_account.data).unwrap(); + account.mint = mint2_key; - // create new mint & mint to account - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); + // mint to account do_process_instruction( mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), vec![&mut mint_account, &mut account_account, &mut owner_account], @@ -1286,18 +1317,6 @@ mod tests { SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let mut rent_sysvar = rent_sysvar(); - // create account - do_process_instruction( - initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![ - &mut account_account, - &mut owner_account, - &mut mint_account, - &mut rent_sysvar, - ], - ) - .unwrap(); - // create mint-able token with zero supply let decimals = 2; do_process_instruction( @@ -1317,6 +1336,18 @@ mod tests { } ); + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + // mint to do_process_instruction( mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 42).unwrap(), @@ -1349,13 +1380,20 @@ mod tests { SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let mut rent_sysvar = rent_sysvar(); + // create mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + // create account do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), vec![ &mut account_account, - &mut owner_account, &mut mint_account, + &mut owner_account, &mut rent_sysvar, ], ) @@ -1366,19 +1404,14 @@ mod tests { initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), vec![ &mut account2_account, - &mut owner_account, &mut mint_account, + &mut owner_account, &mut rent_sysvar, ], ) .unwrap(); - // create new mint & mint to account - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); + // mint to account do_process_instruction( mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), vec![&mut mint_account, &mut account_account, &mut owner_account], @@ -1478,6 +1511,20 @@ mod tests { SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let mut rent_sysvar = rent_sysvar(); + // create new mint with owner + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + // create mint with owner and freeze_authority + do_process_instruction( + initialize_mint(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(), + vec![&mut mint2_account, &mut rent_sysvar], + ) + .unwrap(); + // invalid account assert_eq!( Err(TokenError::UninitializedState.into()), @@ -1631,13 +1678,6 @@ mod tests { ) .unwrap(); - // create new mint with owner - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - // wrong owner assert_eq!( Err(TokenError::OwnerMismatch.into()), @@ -1735,13 +1775,6 @@ mod tests { ) ); - // create mint with owner and freeze_authority - do_process_instruction( - initialize_mint(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(), - vec![&mut mint2_account, &mut rent_sysvar], - ) - .unwrap(); - // set freeze_authority do_process_instruction( set_authority( @@ -1812,13 +1845,18 @@ mod tests { let mut mint_account = SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let mint2_key = pubkey_rand(); - let mut mint2_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let uninitialized_key = pubkey_rand(); let mut uninitialized_account = SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); let mut rent_sysvar = rent_sysvar(); + // create new mint with owner + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + // create account do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), @@ -1857,22 +1895,17 @@ mod tests { // create mismatch account do_process_instruction( - initialize_account(&program_id, &mismatch_key, &mint2_key, &owner_key).unwrap(), + initialize_account(&program_id, &mismatch_key, &mint_key, &owner_key).unwrap(), vec![ &mut mismatch_account, - &mut mint2_account, + &mut mint_account, &mut owner_account, &mut rent_sysvar, ], ) .unwrap(); - - // create new mint with owner - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); + let account: &mut Account = state::unpack(&mut mismatch_account.data).unwrap(); + account.mint = mint2_key; // mint to do_process_instruction( @@ -2001,10 +2034,15 @@ mod tests { let mut mint_account = SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let mint2_key = pubkey_rand(); - let mut mint2_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let mut rent_sysvar = rent_sysvar(); + // create new mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + // create account do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), @@ -2043,22 +2081,19 @@ mod tests { // create mismatch account do_process_instruction( - initialize_account(&program_id, &mismatch_key, &mint2_key, &owner_key).unwrap(), + initialize_account(&program_id, &mismatch_key, &mint_key, &owner_key).unwrap(), vec![ &mut mismatch_account, - &mut mint2_account, + &mut mint_account, &mut owner_account, &mut rent_sysvar, ], ) .unwrap(); + let account: &mut Account = state::unpack(&mut mismatch_account.data).unwrap(); + account.mint = mint2_key; - // create new mint - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); + // mint to account do_process_instruction( mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), vec![&mut mint_account, &mut account_account, &mut owner_account], @@ -2286,6 +2321,13 @@ mod tests { ) .unwrap(); + // create new mint with multisig owner + do_process_instruction( + initialize_mint(&program_id, &mint_key, &multisig_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + // create account with multisig owner do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &multisig_key).unwrap(), @@ -2316,12 +2358,7 @@ mod tests { ) .unwrap(); - // create new mint with multisig owner - do_process_instruction( - initialize_mint(&program_id, &mint_key, &multisig_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); + // mint to account let account_info_iter = &mut signer_accounts.iter_mut(); do_process_instruction( mint_to( @@ -2495,16 +2532,6 @@ mod tests { let mint2_key = pubkey_rand(); let mut mint2_account = SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); - do_process_instruction( - initialize_account(&program_id, &account3_key, &mint2_key, &owner_key).unwrap(), - vec![ - &mut account3_account, - &mut mint2_account, - &mut owner_account, - &mut rent_sysvar, - ], - ) - .unwrap(); do_process_instruction( initialize_mint( &program_id, @@ -2517,6 +2544,16 @@ mod tests { vec![&mut mint2_account, &mut rent_sysvar], ) .unwrap(); + do_process_instruction( + initialize_account(&program_id, &account3_key, &mint2_key, &owner_key).unwrap(), + vec![ + &mut account3_account, + &mut mint2_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); let account_info_iter = &mut signer_accounts.iter_mut(); do_process_instruction( mint_to( @@ -2758,6 +2795,11 @@ mod tests { ); // initialize and mint to non-native account + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), vec![ @@ -2768,11 +2810,6 @@ mod tests { ], ) .unwrap(); - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); do_process_instruction( mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 42).unwrap(), vec![ @@ -3131,6 +3168,13 @@ mod tests { SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let mut rent_sysvar = rent_sysvar(); + // create new mint with owner + do_process_instruction( + initialize_mint(&program_id, &mint_key, &mint_owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + // create an account do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), @@ -3155,13 +3199,6 @@ mod tests { ) .unwrap(); - // create new mint with owner - do_process_instruction( - initialize_mint(&program_id, &mint_key, &mint_owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); - // mint the max to an account do_process_instruction( mint_to( @@ -3297,6 +3334,13 @@ mod tests { SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let mut rent_sysvar = rent_sysvar(); + // create new mint and fund first account + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + // create account do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), @@ -3321,12 +3365,7 @@ mod tests { ) .unwrap(); - // create new mint and fund first account - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); + // fund first account do_process_instruction( mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), vec![&mut mint_account, &mut account_account, &mut owner_account], @@ -3471,6 +3510,13 @@ mod tests { SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); let mut rent_sysvar = rent_sysvar(); + // create new mint with owner different from account owner + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + // create account do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &account_owner_key).unwrap(), @@ -3483,12 +3529,7 @@ mod tests { ) .unwrap(); - // create new mint with owner different from account owner - do_process_instruction( - initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), - vec![&mut mint_account, &mut rent_sysvar], - ) - .unwrap(); + // mint to account do_process_instruction( mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), vec![&mut mint_account, &mut account_account, &mut owner_account], From e781ae91551ea02fdce910f350217634c4011a46 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Thu, 27 Aug 2020 15:22:37 -0600 Subject: [PATCH 032/248] C headers doc --- program/inc/token.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/program/inc/token.h b/program/inc/token.h index a05c087..a3da9eb 100644 --- a/program/inc/token.h +++ b/program/inc/token.h @@ -123,8 +123,10 @@ typedef enum Token_TokenInstruction_Tag { */ Token_TokenInstruction_InitializeMint, /** - * Initializes a new account to hold tokens. If this account is associated with the native mint - * then the token balance of the initialized account will be equal to the amount of SOL in the account. + * Initializes a new account to hold tokens. If this account is associated with the native + * mint then the token balance of the initialized account will be equal to the amount of SOL + * in the account. If this account is associated with another mint, that mint must be + * initialized before this command can succeed. * * The `InitializeAccount` instruction requires no signers and MUST be included within * the same Transaction as the system program's `CreateInstruction` that creates the account From d8478e016b7c58caa069b126fd7b0d4c7dbcf28d Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Thu, 27 Aug 2020 11:12:49 -0700 Subject: [PATCH 033/248] Add Transfer2/Approve2/MintTo2/Burn2 instructions for improved hardware wallet support --- program/src/error.rs | 3 + program/src/instruction.rs | 385 ++++++++++++++++++++++++++++++++++++- program/src/processor.rs | 273 ++++++++++++++++++++++++-- 3 files changed, 647 insertions(+), 14 deletions(-) diff --git a/program/src/error.rs b/program/src/error.rs index 904dc21..a69e57e 100644 --- a/program/src/error.rs +++ b/program/src/error.rs @@ -61,6 +61,9 @@ pub enum TokenError { /// Account is frozen; all account operations will fail #[error("Account is frozen")] AccountFrozen, + /// Mint decimals mismatch between the client and mint + #[error("The provided decimals value different from the Mint decimals")] + MintDecimalsMismatch, } impl From for ProgramError { fn from(e: TokenError) -> Self { diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 701f4bc..1d21ce7 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -94,7 +94,7 @@ pub enum TokenInstruction { }, /// Approves a delegate. A delegate is given the authority over /// tokens on behalf of the source account's owner. - + /// /// Accounts expected by this instruction: /// /// * Single owner @@ -225,6 +225,109 @@ pub enum TokenInstruction { /// 2. `[]` The mint's multisignature freeze authority. /// 3. ..3+M '[signer]' M signer accounts. ThawAccount, + + /// Transfers tokens from one account to another either directly or via a delegate. If this + /// account is associated with the native mint then equal amounts of SOL and Tokens will be + /// transferred to the destination account. + /// + /// This instruction differs from Transfer in that the token mint and decimals value is + /// asserted by the caller. This may be useful when creating transactions offline or within a + /// hardware wallet. + /// + /// Accounts expected by this instruction: + /// + /// * Single owner/delegate + /// 0. `[writable]` The source account. + /// 1. '[]' The token mint. + /// 2. `[writable]` The destination account. + /// 3. '[signer]' The source account's owner/delegate. + /// + /// * Multisignature owner/delegate + /// 0. `[writable]` The source account. + /// 1. '[]' The token mint. + /// 2. `[writable]` The destination account. + /// 3. '[]' The source account's multisignature owner/delegate. + /// 4. ..4+M '[signer]' M signer accounts. + Transfer2 { + /// The amount of tokens to transfer. + amount: u64, + /// Expected number of base 10 digits to the right of the decimal place. + decimals: u8, + }, + /// Approves a delegate. A delegate is given the authority over + /// tokens on behalf of the source account's owner. + /// + /// This instruction differs from Approve in that the token mint and decimals value is asserted + /// by the caller. This may be useful when creating transactions offline or within a hardware + /// wallet. + /// + /// Accounts expected by this instruction: + /// + /// * Single owner + /// 0. `[writable]` The source account. + /// 1. '[]' The token mint. + /// 2. `[]` The delegate. + /// 3. `[signer]` The source account owner. + /// + /// * Multisignature owner + /// 0. `[writable]` The source account. + /// 1. '[]' The token mint. + /// 2. `[]` The delegate. + /// 3. '[]' The source account's multisignature owner. + /// 4. ..4+M '[signer]' M signer accounts + Approve2 { + /// The amount of tokens the delegate is approved for. + amount: u64, + /// Expected number of base 10 digits to the right of the decimal place. + decimals: u8, + }, + /// Mints new tokens to an account. The native mint does not support minting. + /// + /// This instruction differs from MintTo in that the decimals value is asserted by the + /// caller. This may be useful when creating transactions offline or within a hardware wallet. + /// + /// Accounts expected by this instruction: + /// + /// * Single authority + /// 0. `[writable]` The mint. + /// 1. `[writable]` The account to mint tokens to. + /// 2. `[signer]` The mint's minting authority. + /// + /// * Multisignature authority + /// 0. `[writable]` The mint. + /// 1. `[writable]` The account to mint tokens to. + /// 2. `[]` The mint's multisignature mint-tokens authority. + /// 3. ..3+M '[signer]' M signer accounts. + MintTo2 { + /// The amount of new tokens to mint. + amount: u64, + /// Expected number of base 10 digits to the right of the decimal place. + decimals: u8, + }, + /// Burns tokens by removing them from an account. `Burn2` does not support accounts + /// associated with the native mint, use `CloseAccount` instead. + /// + /// This instruction differs from Burn in that the decimals value is asserted by the caller. + /// This may be useful when creating transactions offline or within a hardware wallet. + /// + /// Accounts expected by this instruction: + /// + /// * Single owner/delegate + /// 0. `[writable]` The account to burn from. + /// 1. '[writable]' The token mint. + /// 2. `[signer]` The account's owner/delegate. + /// + /// * Multisignature owner/delegate + /// 0. `[writable]` The account to burn from. + /// 1. '[writable]' The token mint. + /// 2. `[]` The account's multisignature owner/delegate. + /// 3. ..3+M '[signer]' M signer accounts. + Burn2 { + /// The amount of tokens to burn. + amount: u64, + /// Expected number of base 10 digits to the right of the decimal place. + decimals: u8, + }, } impl TokenInstruction { /// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html). @@ -323,6 +426,67 @@ impl TokenInstruction { 9 => Self::CloseAccount, 10 => Self::FreezeAccount, 11 => Self::ThawAccount, + 12 => { + if input.len() < size_of::() + size_of::() + size_of::() { + return Err(TokenError::InvalidInstruction.into()); + } + let mut input_len = 0; + input_len += size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let amount = unsafe { *(&input[input_len] as *const u8 as *const u64) }; + input_len += size_of::(); + + let decimals = unsafe { *(&input[input_len] as *const u8) }; + + Self::Transfer2 { amount, decimals } + } + 13 => { + if input.len() < size_of::() + size_of::() + size_of::() { + return Err(TokenError::InvalidInstruction.into()); + } + let mut input_len = 0; + input_len += size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let amount = unsafe { *(&input[input_len] as *const u8 as *const u64) }; + input_len += size_of::(); + + let decimals = unsafe { *(&input[input_len] as *const u8) }; + + Self::Approve2 { amount, decimals } + } + 14 => { + if input.len() < size_of::() + size_of::() + size_of::() { + return Err(TokenError::InvalidInstruction.into()); + } + let mut input_len = 0; + input_len += size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let amount = unsafe { *(&input[input_len] as *const u8 as *const u64) }; + input_len += size_of::(); + + let decimals = unsafe { *(&input[input_len] as *const u8) }; + + Self::MintTo2 { amount, decimals } + } + 15 => { + if input.len() < size_of::() + size_of::() + size_of::() { + return Err(TokenError::InvalidInstruction.into()); + } + let mut input_len = 0; + input_len += size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let amount = unsafe { *(&input[input_len] as *const u8 as *const u64) }; + input_len += size_of::(); + + let decimals = unsafe { *(&input[input_len] as *const u8) }; + + Self::Burn2 { amount, decimals } + } + _ => return Err(TokenError::InvalidInstruction.into()), }) } @@ -428,6 +592,59 @@ impl TokenInstruction { output[output_len] = 11; output_len += size_of::(); } + Self::Transfer2 { amount, decimals } => { + output[output_len] = 12; + output_len += size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; + *value = *amount; + output_len += size_of::(); + + let value = unsafe { &mut *(&mut output[output_len] as *mut u8) }; + *value = *decimals; + output_len += size_of::(); + } + Self::Approve2 { amount, decimals } => { + output[output_len] = 13; + output_len += size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; + *value = *amount; + output_len += size_of::(); + + let value = unsafe { &mut *(&mut output[output_len] as *mut u8) }; + *value = *decimals; + output_len += size_of::(); + } + Self::MintTo2 { amount, decimals } => { + output[output_len] = 14; + output_len += size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; + *value = *amount; + output_len += size_of::(); + + let value = unsafe { &mut *(&mut output[output_len] as *mut u8) }; + *value = *decimals; + output_len += size_of::(); + } + + Self::Burn2 { amount, decimals } => { + output[output_len] = 15; + output_len += size_of::(); + + #[allow(clippy::cast_ptr_alignment)] + let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; + *value = *amount; + output_len += size_of::(); + + let value = unsafe { &mut *(&mut output[output_len] as *mut u8) }; + *value = *decimals; + output_len += size_of::(); + } } output.truncate(output_len); @@ -809,6 +1026,132 @@ pub fn thaw_account( }) } +/// Creates a `Transfer2` instruction. +#[allow(clippy::too_many_arguments)] +pub fn transfer2( + token_program_id: &Pubkey, + source_pubkey: &Pubkey, + mint_pubkey: &Pubkey, + destination_pubkey: &Pubkey, + authority_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], + amount: u64, + decimals: u8, +) -> Result { + let data = TokenInstruction::Transfer2 { amount, decimals }.pack()?; + + let mut accounts = Vec::with_capacity(4 + signer_pubkeys.len()); + accounts.push(AccountMeta::new(*source_pubkey, false)); + accounts.push(AccountMeta::new_readonly(*mint_pubkey, false)); + accounts.push(AccountMeta::new(*destination_pubkey, false)); + accounts.push(AccountMeta::new_readonly( + *authority_pubkey, + signer_pubkeys.is_empty(), + )); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new(**signer_pubkey, true)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates an `Approve2` instruction. +#[allow(clippy::too_many_arguments)] +pub fn approve2( + token_program_id: &Pubkey, + source_pubkey: &Pubkey, + mint_pubkey: &Pubkey, + delegate_pubkey: &Pubkey, + owner_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], + amount: u64, + decimals: u8, +) -> Result { + let data = TokenInstruction::Approve2 { amount, decimals }.pack()?; + + let mut accounts = Vec::with_capacity(4 + signer_pubkeys.len()); + accounts.push(AccountMeta::new(*source_pubkey, false)); + accounts.push(AccountMeta::new_readonly(*mint_pubkey, false)); + accounts.push(AccountMeta::new_readonly(*delegate_pubkey, false)); + accounts.push(AccountMeta::new_readonly( + *owner_pubkey, + signer_pubkeys.is_empty(), + )); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new(**signer_pubkey, true)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates a `MintTo2` instruction. +pub fn mint_to2( + token_program_id: &Pubkey, + mint_pubkey: &Pubkey, + account_pubkey: &Pubkey, + owner_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], + amount: u64, + decimals: u8, +) -> Result { + let data = TokenInstruction::MintTo2 { amount, decimals }.pack()?; + + let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); + accounts.push(AccountMeta::new(*mint_pubkey, false)); + accounts.push(AccountMeta::new(*account_pubkey, false)); + accounts.push(AccountMeta::new_readonly( + *owner_pubkey, + signer_pubkeys.is_empty(), + )); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new(**signer_pubkey, true)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + +/// Creates a `Burn2` instruction. +pub fn burn2( + token_program_id: &Pubkey, + account_pubkey: &Pubkey, + mint_pubkey: &Pubkey, + authority_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], + amount: u64, + decimals: u8, +) -> Result { + let data = TokenInstruction::Burn2 { amount, decimals }.pack()?; + + let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); + accounts.push(AccountMeta::new(*account_pubkey, false)); + accounts.push(AccountMeta::new(*mint_pubkey, false)); + accounts.push(AccountMeta::new_readonly( + *authority_pubkey, + signer_pubkeys.is_empty(), + )); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new(**signer_pubkey, true)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + /// Utility function that checks index is between MIN_SIGNERS and MAX_SIGNERS pub fn is_valid_signer_index(index: usize) -> bool { !(index < MIN_SIGNERS || index > MAX_SIGNERS) @@ -928,5 +1271,45 @@ mod test { assert_eq!(packed, expect); let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); + + let check = TokenInstruction::Transfer2 { + amount: 1, + decimals: 2, + }; + let packed = check.pack().unwrap(); + let expect = Vec::from([12u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::Approve2 { + amount: 1, + decimals: 2, + }; + let packed = check.pack().unwrap(); + let expect = Vec::from([13u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::MintTo2 { + amount: 1, + decimals: 2, + }; + let packed = check.pack().unwrap(); + let expect = Vec::from([14u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::Burn2 { + amount: 1, + decimals: 2, + }; + let packed = check.pack().unwrap(); + let expect = Vec::from([15u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); } } diff --git a/program/src/processor.rs b/program/src/processor.rs index ae39050..4826f2a 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -137,9 +137,18 @@ impl Processor { program_id: &Pubkey, accounts: &[AccountInfo], amount: u64, + expected_decimals: Option, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); + let source_account_info = next_account_info(account_info_iter)?; + + let expected_mint_info = if let Some(expected_decimals) = expected_decimals { + Some((next_account_info(account_info_iter)?, expected_decimals)) + } else { + None + }; + let dest_account_info = next_account_info(account_info_iter)?; let authority_info = next_account_info(account_info_iter)?; @@ -162,6 +171,19 @@ impl Processor { return Err(TokenError::AccountFrozen.into()); } + if let Some((mint_account_info, expected_decimals)) = expected_mint_info { + if source_account.mint != *mint_account_info.key { + return Err(TokenError::MintMismatch.into()); + } + + let mut mint_info_data = mint_account_info.data.borrow_mut(); + let mint: &Mint = state::unpack_unchecked(&mut mint_info_data)?; + + if expected_decimals != mint.decimals { + return Err(TokenError::MintDecimalsMismatch.into()); + } + } + match source_account.delegate { COption::Some(ref delegate) if authority_info.key == delegate => { Self::validate_owner( @@ -218,19 +240,39 @@ impl Processor { program_id: &Pubkey, accounts: &[AccountInfo], amount: u64, + expected_decimals: Option, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); + let source_account_info = next_account_info(account_info_iter)?; + let expected_mint_info = if let Some(expected_decimals) = expected_decimals { + Some((next_account_info(account_info_iter)?, expected_decimals)) + } else { + None + }; + let delegate_info = next_account_info(account_info_iter)?; + let owner_info = next_account_info(account_info_iter)?; let mut source_data = source_account_info.data.borrow_mut(); let mut source_account: &mut Account = state::unpack(&mut source_data)?; - let delegate_info = next_account_info(account_info_iter)?; - let owner_info = next_account_info(account_info_iter)?; if source_account.is_frozen() { return Err(TokenError::AccountFrozen.into()); } + if let Some((mint_account_info, expected_decimals)) = expected_mint_info { + if source_account.mint != *mint_account_info.key { + return Err(TokenError::MintMismatch.into()); + } + + let mut mint_info_data = mint_account_info.data.borrow_mut(); + let mint: &Mint = state::unpack_unchecked(&mut mint_info_data)?; + + if expected_decimals != mint.decimals { + return Err(TokenError::MintDecimalsMismatch.into()); + } + } + Self::validate_owner( program_id, &source_account.owner, @@ -367,6 +409,7 @@ impl Processor { program_id: &Pubkey, accounts: &[AccountInfo], amount: u64, + expected_decimals: Option, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let mint_info = next_account_info(account_info_iter)?; @@ -390,6 +433,12 @@ impl Processor { let mut mint_info_data = mint_info.data.borrow_mut(); let mint: &mut Mint = state::unpack(&mut mint_info_data)?; + if let Some(expected_decimals) = expected_decimals { + if expected_decimals != mint.decimals { + return Err(TokenError::MintDecimalsMismatch.into()); + } + } + match mint.mint_authority { COption::Some(mint_authority) => { Self::validate_owner( @@ -422,8 +471,10 @@ impl Processor { program_id: &Pubkey, accounts: &[AccountInfo], amount: u64, + expected_decimals: Option, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); + let source_account_info = next_account_info(account_info_iter)?; let mint_info = next_account_info(account_info_iter)?; let authority_info = next_account_info(account_info_iter)?; @@ -447,6 +498,12 @@ impl Processor { return Err(TokenError::AccountFrozen.into()); } + if let Some(expected_decimals) = expected_decimals { + if expected_decimals != mint.decimals { + return Err(TokenError::MintDecimalsMismatch.into()); + } + } + match source_account.delegate { COption::Some(ref delegate) if authority_info.key == delegate => { Self::validate_owner( @@ -596,11 +653,11 @@ impl Processor { } TokenInstruction::Transfer { amount } => { info!("Instruction: Transfer"); - Self::process_transfer(program_id, accounts, amount) + Self::process_transfer(program_id, accounts, amount, None) } TokenInstruction::Approve { amount } => { info!("Instruction: Approve"); - Self::process_approve(program_id, accounts, amount) + Self::process_approve(program_id, accounts, amount, None) } TokenInstruction::Revoke => { info!("Instruction: Revoke"); @@ -615,11 +672,11 @@ impl Processor { } TokenInstruction::MintTo { amount } => { info!("Instruction: MintTo"); - Self::process_mint_to(program_id, accounts, amount) + Self::process_mint_to(program_id, accounts, amount, None) } TokenInstruction::Burn { amount } => { info!("Instruction: Burn"); - Self::process_burn(program_id, accounts, amount) + Self::process_burn(program_id, accounts, amount, None) } TokenInstruction::CloseAccount => { info!("Instruction: CloseAccount"); @@ -633,6 +690,22 @@ impl Processor { info!("Instruction: FreezeAccount"); Self::process_toggle_freeze_account(program_id, accounts, false) } + TokenInstruction::Transfer2 { amount, decimals } => { + info!("Instruction: Transfer"); + Self::process_transfer(program_id, accounts, amount, Some(decimals)) + } + TokenInstruction::Approve2 { amount, decimals } => { + info!("Instruction: Approve"); + Self::process_approve(program_id, accounts, amount, Some(decimals)) + } + TokenInstruction::MintTo2 { amount, decimals } => { + info!("Instruction: MintTo"); + Self::process_mint_to(program_id, accounts, amount, Some(decimals)) + } + TokenInstruction::Burn2 { amount, decimals } => { + info!("Instruction: Burn"); + Self::process_burn(program_id, accounts, amount, Some(decimals)) + } } } @@ -706,6 +779,9 @@ impl PrintProgramError for TokenError { } TokenError::MintCannotFreeze => info!("Error: This token mint cannot freeze accounts"), TokenError::AccountFrozen => info!("Error: Account is frozen"), + TokenError::MintDecimalsMismatch => { + info!("Error: decimals different from the Mint decimals") + } } } } @@ -717,10 +793,7 @@ solana_sdk::program_stubs!(); #[cfg(test)] mod tests { use super::*; - use crate::instruction::{ - approve, burn, close_account, freeze_account, initialize_account, initialize_mint, - initialize_multisig, mint_to, revoke, set_authority, thaw_account, transfer, MAX_SIGNERS, - }; + use crate::instruction::*; use solana_sdk::{ account::Account as SolanaAccount, account_info::create_is_signer_account_infos, clock::Epoch, instruction::Instruction, sysvar::rent, @@ -1118,19 +1191,69 @@ mod tests { ) .unwrap(); - // transfer rest + // incorrect decimals + assert_eq!( + Err(TokenError::MintDecimalsMismatch.into()), + do_process_instruction( + transfer2( + &program_id, + &account2_key, + &mint_key, + &account_key, + &owner_key, + &[], + 1, + 10 // <-- incorrect decimals + ) + .unwrap(), + vec![ + &mut account2_account, + &mut mint_account, + &mut account_account, + &mut owner_account, + ], + ) + ); + + // incorrect mint + assert_eq!( + Err(TokenError::MintMismatch.into()), + do_process_instruction( + transfer2( + &program_id, + &account2_key, + &account3_key, // <-- incorrect mint + &account_key, + &owner_key, + &[], + 1, + 2 + ) + .unwrap(), + vec![ + &mut account2_account, + &mut account3_account, // <-- incorrect mint + &mut account_account, + &mut owner_account, + ], + ) + ); + // transfer rest with explicit decimals do_process_instruction( - transfer( + transfer2( &program_id, &account2_key, + &mint_key, &account_key, &owner_key, &[], 500, + 2, ) .unwrap(), vec![ &mut account2_account, + &mut mint_account, &mut account_account, &mut owner_account, ], @@ -1355,9 +1478,47 @@ mod tests { ) .unwrap(); + // mint to 2, with incorrect decimals + assert_eq!( + Err(TokenError::MintDecimalsMismatch.into()), + do_process_instruction( + mint_to2( + &program_id, + &mint_key, + &account_key, + &owner_key, + &[], + 42, + decimals + 1 + ) + .unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + ); + let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); let dest_account: &mut Account = state::unpack(&mut account_account.data).unwrap(); assert_eq!(dest_account.amount, 42); + + // mint to 2 + do_process_instruction( + mint_to2( + &program_id, + &mint_key, + &account_key, + &owner_key, + &[], + 42, + decimals, + ) + .unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + + let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); + let dest_account: &mut Account = state::unpack(&mut account_account.data).unwrap(); + assert_eq!(dest_account.amount, 84); } #[test] @@ -1481,6 +1642,76 @@ mod tests { ) .unwrap(); + // approve delegate 2, with incorrect decimals + assert_eq!( + Err(TokenError::MintDecimalsMismatch.into()), + do_process_instruction( + approve2( + &program_id, + &account_key, + &mint_key, + &delegate_key, + &owner_key, + &[], + 100, + 0 // <-- incorrect decimals + ) + .unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut delegate_account, + &mut owner_account, + ], + ) + ); + + // approve delegate 2, with incorrect mint + assert_eq!( + Err(TokenError::MintMismatch.into()), + do_process_instruction( + approve2( + &program_id, + &account_key, + &account2_key, // <-- bad mint + &delegate_key, + &owner_key, + &[], + 100, + 0 + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, // <-- bad mint + &mut delegate_account, + &mut owner_account, + ], + ) + ); + + // approve delegate 2 + do_process_instruction( + approve2( + &program_id, + &account_key, + &mint_key, + &delegate_key, + &owner_key, + &[], + 100, + 2, + ) + .unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut delegate_account, + &mut owner_account, + ], + ) + .unwrap(); + // revoke delegate do_process_instruction( revoke(&program_id, &account_key, &owner_key, &[]).unwrap(), @@ -2136,7 +2367,23 @@ mod tests { // burn do_process_instruction( - burn(&program_id, &account_key, &mint_key, &owner_key, &[], 42).unwrap(), + burn(&program_id, &account_key, &mint_key, &owner_key, &[], 21).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + .unwrap(); + + // burn2, with incorrect decimals + assert_eq!( + Err(TokenError::MintDecimalsMismatch.into()), + do_process_instruction( + burn2(&program_id, &account_key, &mint_key, &owner_key, &[], 21, 3).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + ); + + // burn2 + do_process_instruction( + burn2(&program_id, &account_key, &mint_key, &owner_key, &[], 21, 2).unwrap(), vec![&mut account_account, &mut mint_account, &mut owner_account], ) .unwrap(); From d53a9cb80b40e0da16a474ff67ecf3bbccbc9f37 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Thu, 27 Aug 2020 16:34:50 -0700 Subject: [PATCH 034/248] Adjust native mint --- program/src/native_mint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/src/native_mint.rs b/program/src/native_mint.rs index e502128..622199c 100644 --- a/program/src/native_mint.rs +++ b/program/src/native_mint.rs @@ -4,7 +4,7 @@ pub const DECIMALS: u8 = 9; // The Mint for native SOL Token accounts -solana_sdk::declare_id!("So12111111111111111111111111111111111111111"); +solana_sdk::declare_id!("So11111111111111111111111111111111111111112"); #[cfg(test)] mod tests { From 7de525ace14f556eaf328502a34848a4fb7b719b Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Thu, 27 Aug 2020 12:20:36 -0700 Subject: [PATCH 035/248] Update token.h --- program/inc/token.h | 139 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/program/inc/token.h b/program/inc/token.h index a3da9eb..32df44b 100644 --- a/program/inc/token.h +++ b/program/inc/token.h @@ -180,6 +180,7 @@ typedef enum Token_TokenInstruction_Tag { /** * Approves a delegate. A delegate is given the authority over * tokens on behalf of the source account's owner. + * * Accounts expected by this instruction: * * * Single owner @@ -311,6 +312,96 @@ typedef enum Token_TokenInstruction_Tag { * 3. ..3+M '[signer]' M signer accounts. */ Token_TokenInstruction_ThawAccount, + /** + * Transfers tokens from one account to another either directly or via a delegate. If this + * account is associated with the native mint then equal amounts of SOL and Tokens will be + * transferred to the destination account. + * + * This instruction differs from Transfer in that the token mint and decimals value is + * asserted by the caller. This may be useful when creating transactions offline or within a + * hardware wallet. + * + * Accounts expected by this instruction: + * + * * Single owner/delegate + * 0. `[writable]` The source account. + * 1. '[]' The token mint. + * 2. `[writable]` The destination account. + * 3. '[signer]' The source account's owner/delegate. + * + * * Multisignature owner/delegate + * 0. `[writable]` The source account. + * 1. '[]' The token mint. + * 2. `[writable]` The destination account. + * 3. '[]' The source account's multisignature owner/delegate. + * 4. ..4+M '[signer]' M signer accounts. + */ + Token_TokenInstruction_Transfer2, + /** + * Approves a delegate. A delegate is given the authority over + * tokens on behalf of the source account's owner. + * + * This instruction differs from Approve in that the token mint and decimals value is asserted + * by the caller. This may be useful when creating transactions offline or within a hardware + * wallet. + * + * Accounts expected by this instruction: + * + * * Single owner + * 0. `[writable]` The source account. + * 1. '[]' The token mint. + * 2. `[]` The delegate. + * 3. `[signer]` The source account owner. + * + * * Multisignature owner + * 0. `[writable]` The source account. + * 1. '[]' The token mint. + * 2. `[]` The delegate. + * 3. '[]' The source account's multisignature owner. + * 4. ..4+M '[signer]' M signer accounts + */ + Token_TokenInstruction_Approve2, + /** + * Mints new tokens to an account. The native mint does not support minting. + * + * This instruction differs from MintTo in that the decimals value is asserted by the + * caller. This may be useful when creating transactions offline or within a hardware wallet. + * + * Accounts expected by this instruction: + * + * * Single authority + * 0. `[writable]` The mint. + * 1. `[writable]` The account to mint tokens to. + * 2. `[signer]` The mint's minting authority. + * + * * Multisignature authority + * 0. `[writable]` The mint. + * 1. `[writable]` The account to mint tokens to. + * 2. `[]` The mint's multisignature mint-tokens authority. + * 3. ..3+M '[signer]' M signer accounts. + */ + Token_TokenInstruction_MintTo2, + /** + * Burns tokens by removing them from an account. `Burn2` does not support accounts + * associated with the native mint, use `CloseAccount` instead. + * + * This instruction differs from Burn in that the decimals value is asserted by the caller. + * This may be useful when creating transactions offline or within a hardware wallet. + * + * Accounts expected by this instruction: + * + * * Single owner/delegate + * 0. `[writable]` The account to burn from. + * 1. '[writable]' The token mint. + * 2. `[signer]` The account's owner/delegate. + * + * * Multisignature owner/delegate + * 0. `[writable]` The account to burn from. + * 1. '[writable]' The token mint. + * 2. `[]` The account's multisignature owner/delegate. + * 3. ..3+M '[signer]' M signer accounts. + */ + Token_TokenInstruction_Burn2, } Token_TokenInstruction_Tag; typedef struct Token_TokenInstruction_Token_InitializeMint_Body { @@ -374,6 +465,50 @@ typedef struct Token_TokenInstruction_Token_Burn_Body { uint64_t amount; } Token_TokenInstruction_Token_Burn_Body; +typedef struct Token_TokenInstruction_Token_Transfer2_Body { + /** + * The amount of tokens to transfer. + */ + uint64_t amount; + /** + * Expected number of base 10 digits to the right of the decimal place. + */ + uint8_t decimals; +} Token_TokenInstruction_Token_Transfer2_Body; + +typedef struct Token_TokenInstruction_Token_Approve2_Body { + /** + * The amount of tokens the delegate is approved for. + */ + uint64_t amount; + /** + * Expected number of base 10 digits to the right of the decimal place. + */ + uint8_t decimals; +} Token_TokenInstruction_Token_Approve2_Body; + +typedef struct Token_TokenInstruction_Token_MintTo2_Body { + /** + * The amount of new tokens to mint. + */ + uint64_t amount; + /** + * Expected number of base 10 digits to the right of the decimal place. + */ + uint8_t decimals; +} Token_TokenInstruction_Token_MintTo2_Body; + +typedef struct Token_TokenInstruction_Token_Burn2_Body { + /** + * The amount of tokens to burn. + */ + uint64_t amount; + /** + * Expected number of base 10 digits to the right of the decimal place. + */ + uint8_t decimals; +} Token_TokenInstruction_Token_Burn2_Body; + typedef struct Token_TokenInstruction { Token_TokenInstruction_Tag tag; union { @@ -384,6 +519,10 @@ typedef struct Token_TokenInstruction { Token_TokenInstruction_Token_SetAuthority_Body set_authority; Token_TokenInstruction_Token_MintTo_Body mint_to; Token_TokenInstruction_Token_Burn_Body burn; + Token_TokenInstruction_Token_Transfer2_Body transfer2; + Token_TokenInstruction_Token_Approve2_Body approve2; + Token_TokenInstruction_Token_MintTo2_Body mint_to2; + Token_TokenInstruction_Token_Burn2_Body burn2; }; } Token_TokenInstruction; From 8bf8d40cd3141071e3cd82ee19d16406eccbf870 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Thu, 27 Aug 2020 19:41:14 -0600 Subject: [PATCH 036/248] New spl-token v1.1 program id (#348) --- program/program-id.md | 2 +- program/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/program/program-id.md b/program/program-id.md index 0e3c586..6f7f354 100644 --- a/program/program-id.md +++ b/program/program-id.md @@ -1 +1 @@ -TokensVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o +TokenFSDHBLHfbT65SnYJx77ysXc1WTC2W3kvnXnZZR diff --git a/program/src/lib.rs b/program/src/lib.rs index c6096a5..e188755 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -25,4 +25,4 @@ pub fn amount_to_ui_amount(amount: u64, decimals: u8) -> f64 { amount as f64 / 10_usize.pow(decimals as u32) as f64 } -solana_sdk::declare_id!("TokensVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o"); +solana_sdk::declare_id!("TokenFSDHBLHfbT65SnYJx77ysXc1WTC2W3kvnXnZZR"); From 4149f776cc3cce0734b7d0ea8792c590e48bf65b Mon Sep 17 00:00:00 2001 From: Jack May Date: Thu, 27 Aug 2020 21:19:15 -0700 Subject: [PATCH 037/248] Safer pack/unpack (#349) * Safer pack/unpack * fix cli * clippy * fix swap * nit * clippy Co-authored-by: Michael Vines --- program/Cargo.toml | 2 + program/src/instruction.rs | 447 +++++--------- program/src/lib.rs | 1 + program/src/option.rs | 109 ---- program/src/pack.rs | 76 +++ program/src/processor.rs | 1201 ++++++++++++++++++++---------------- program/src/state.rs | 199 +++++- 7 files changed, 1101 insertions(+), 934 deletions(-) create mode 100644 program/src/pack.rs diff --git a/program/Cargo.toml b/program/Cargo.toml index e112088..2c8c9ca 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -23,6 +23,8 @@ num-traits = "0.2" remove_dir_all = "=0.5.0" solana-sdk = { version = "1.3.4", default-features = false, optional = true } thiserror = "1.0" +arrayref = "0.3.6" +num_enum = "0.5.1" [dev-dependencies] rand = { version = "0.7.0"} diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 1d21ce7..8e1a045 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -7,6 +7,7 @@ use solana_sdk::{ pubkey::Pubkey, sysvar, }; +use std::convert::TryInto; use std::mem::size_of; /// Minimum number of multisignature signers (min N) @@ -332,29 +333,14 @@ pub enum TokenInstruction { impl TokenInstruction { /// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html). pub fn unpack(input: &[u8]) -> Result { - if input.len() < size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - Ok(match input[0] { - 0 => { - if input.len() < size_of::() + size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - let mut input_len = 0; - input_len += size_of::(); - - let decimals = unsafe { *(&input[input_len] as *const u8) }; - input_len += size_of::(); - - let mint_authority = unsafe { *(&input[input_len] as *const u8 as *const Pubkey) }; - input_len += size_of::(); - - let freeze_authority = COption::unpack_or( - input, - &mut input_len, - Into::::into(TokenError::InvalidInstruction), - )?; + use TokenError::InvalidInstruction; + let (&tag, rest) = input.split_first().ok_or(InvalidInstruction)?; + Ok(match tag { + 0 => { + let (&decimals, rest) = rest.split_first().ok_or(InvalidInstruction)?; + let (mint_authority, rest) = Self::unpack_pubkey(rest)?; + let (freeze_authority, _rest) = Self::unpack_pubkey_option(rest)?; Self::InitializeMint { mint_authority, freeze_authority, @@ -363,126 +349,80 @@ impl TokenInstruction { } 1 => Self::InitializeAccount, 2 => { - if input.len() < size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - #[allow(clippy::cast_ptr_alignment)] - let m = unsafe { *(&input[1] as *const u8) }; + let &m = rest.get(0).ok_or(InvalidInstruction)?; Self::InitializeMultisig { m } } - 3 => { - if input.len() < size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; - Self::Transfer { amount } - } - 4 => { - if input.len() < size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); + 3 | 4 | 7 | 8 => { + let amount = rest + .get(..8) + .and_then(|slice| slice.try_into().ok()) + .map(u64::from_le_bytes) + .ok_or(InvalidInstruction)?; + match tag { + 3 => Self::Transfer { amount }, + 4 => Self::Approve { amount }, + 7 => Self::MintTo { amount }, + 8 => Self::Burn { amount }, + _ => unreachable!(), } - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; - Self::Approve { amount } } 5 => Self::Revoke, 6 => { - if input.len() < size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - let mut input_len = 0; - input_len += size_of::(); - let authority_type = AuthorityType::from(input[1])?; - input_len += size_of::(); - - let new_authority = COption::unpack_or( - input, - &mut input_len, - Into::::into(TokenError::InvalidInstruction), - )?; + let (authority_type, rest) = rest + .split_first() + .ok_or_else(|| ProgramError::from(InvalidInstruction)) + .and_then(|(&t, rest)| Ok((AuthorityType::from(t)?, rest)))?; + let (new_authority, _rest) = Self::unpack_pubkey_option(rest)?; Self::SetAuthority { authority_type, new_authority, } } - 7 => { - if input.len() < size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; - Self::MintTo { amount } - } - 8 => { - if input.len() < size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[size_of::()] as *const u8 as *const u64) }; - Self::Burn { amount } - } 9 => Self::CloseAccount, 10 => Self::FreezeAccount, 11 => Self::ThawAccount, 12 => { - if input.len() < size_of::() + size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - let mut input_len = 0; - input_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[input_len] as *const u8 as *const u64) }; - input_len += size_of::(); - - let decimals = unsafe { *(&input[input_len] as *const u8) }; + let (amount, rest) = rest.split_at(8); + let amount = amount + .try_into() + .ok() + .map(u64::from_le_bytes) + .ok_or(InvalidInstruction)?; + let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?; Self::Transfer2 { amount, decimals } } 13 => { - if input.len() < size_of::() + size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - let mut input_len = 0; - input_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[input_len] as *const u8 as *const u64) }; - input_len += size_of::(); - - let decimals = unsafe { *(&input[input_len] as *const u8) }; + let (amount, rest) = rest.split_at(8); + let amount = amount + .try_into() + .ok() + .map(u64::from_le_bytes) + .ok_or(InvalidInstruction)?; + let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?; Self::Approve2 { amount, decimals } } 14 => { - if input.len() < size_of::() + size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - let mut input_len = 0; - input_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[input_len] as *const u8 as *const u64) }; - input_len += size_of::(); - - let decimals = unsafe { *(&input[input_len] as *const u8) }; + let (amount, rest) = rest.split_at(8); + let amount = amount + .try_into() + .ok() + .map(u64::from_le_bytes) + .ok_or(InvalidInstruction)?; + let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?; Self::MintTo2 { amount, decimals } } 15 => { - if input.len() < size_of::() + size_of::() + size_of::() { - return Err(TokenError::InvalidInstruction.into()); - } - let mut input_len = 0; - input_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let amount = unsafe { *(&input[input_len] as *const u8 as *const u64) }; - input_len += size_of::(); - - let decimals = unsafe { *(&input[input_len] as *const u8) }; + let (amount, rest) = rest.split_at(8); + let amount = amount + .try_into() + .ok() + .map(u64::from_le_bytes) + .ok_or(InvalidInstruction)?; + let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?; Self::Burn2 { amount, decimals } } @@ -492,163 +432,106 @@ impl TokenInstruction { } /// Packs a [TokenInstruction](enum.TokenInstruction.html) into a byte buffer. - pub fn pack(&self) -> Result, ProgramError> { - let mut output = vec![0u8; size_of::()]; - let mut output_len = 0; + pub fn pack(&self) -> Vec { + let mut buf = Vec::with_capacity(size_of::()); match self { - Self::InitializeMint { - mint_authority, - freeze_authority, + &Self::InitializeMint { + ref mint_authority, + ref freeze_authority, decimals, } => { - output[output_len] = 0; - output_len += size_of::(); - - let value = unsafe { &mut *(&mut output[output_len] as *mut u8) }; - *value = *decimals; - output_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut Pubkey) }; - *value = *mint_authority; - output_len += size_of::(); - - freeze_authority.pack(&mut output, &mut output_len); + buf.push(0); + buf.push(decimals); + buf.extend_from_slice(mint_authority.as_ref()); + Self::pack_pubkey_option(freeze_authority, &mut buf); } - Self::InitializeAccount => { - output[output_len] = 1; - output_len += size_of::(); + Self::InitializeAccount => buf.push(1), + &Self::InitializeMultisig { m } => { + buf.push(2); + buf.push(m); } - Self::InitializeMultisig { m } => { - output[output_len] = 2; - output_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u8) }; - *value = *m; - output_len += size_of::(); + &Self::Transfer { amount } => { + buf.push(3); + buf.extend_from_slice(&amount.to_le_bytes()); } - Self::Transfer { amount } => { - output[output_len] = 3; - output_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; - *value = *amount; - output_len += size_of::(); + &Self::Approve { amount } => { + buf.push(4); + buf.extend_from_slice(&amount.to_le_bytes()); } - Self::Approve { amount } => { - output[output_len] = 4; - output_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; - *value = *amount; - output_len += size_of::(); + &Self::MintTo { amount } => { + buf.push(7); + buf.extend_from_slice(&amount.to_le_bytes()); } - Self::Revoke => { - output[output_len] = 5; - output_len += size_of::(); + &Self::Burn { amount } => { + buf.push(8); + buf.extend_from_slice(&amount.to_le_bytes()); } + Self::Revoke => buf.push(5), Self::SetAuthority { authority_type, - new_authority, + ref new_authority, } => { - output[output_len] = 6; - output_len += size_of::(); - - output[output_len] = authority_type.into(); - output_len += size_of::(); - - new_authority.pack(&mut output, &mut output_len); - } - Self::MintTo { amount } => { - output[output_len] = 7; - output_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; - *value = *amount; - output_len += size_of::(); - } - Self::Burn { amount } => { - output[output_len] = 8; - output_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; - *value = *amount; - output_len += size_of::(); + buf.push(6); + buf.push(authority_type.into()); + Self::pack_pubkey_option(new_authority, &mut buf); } - Self::CloseAccount => { - output[output_len] = 9; - output_len += size_of::(); + Self::CloseAccount => buf.push(9), + Self::FreezeAccount => buf.push(10), + Self::ThawAccount => buf.push(11), + &Self::Transfer2 { amount, decimals } => { + buf.push(12); + buf.extend_from_slice(&amount.to_le_bytes()); + buf.push(decimals); } - Self::FreezeAccount => { - output[output_len] = 10; - output_len += size_of::(); + &Self::Approve2 { amount, decimals } => { + buf.push(13); + buf.extend_from_slice(&amount.to_le_bytes()); + buf.push(decimals); } - Self::ThawAccount => { - output[output_len] = 11; - output_len += size_of::(); + &Self::MintTo2 { amount, decimals } => { + buf.push(14); + buf.extend_from_slice(&amount.to_le_bytes()); + buf.push(decimals); } - Self::Transfer2 { amount, decimals } => { - output[output_len] = 12; - output_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; - *value = *amount; - output_len += size_of::(); - - let value = unsafe { &mut *(&mut output[output_len] as *mut u8) }; - *value = *decimals; - output_len += size_of::(); + &Self::Burn2 { amount, decimals } => { + buf.push(15); + buf.extend_from_slice(&amount.to_le_bytes()); + buf.push(decimals); } - Self::Approve2 { amount, decimals } => { - output[output_len] = 13; - output_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; - *value = *amount; - output_len += size_of::(); - - let value = unsafe { &mut *(&mut output[output_len] as *mut u8) }; - *value = *decimals; - output_len += size_of::(); - } - Self::MintTo2 { amount, decimals } => { - output[output_len] = 14; - output_len += size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; - *value = *amount; - output_len += size_of::(); - - let value = unsafe { &mut *(&mut output[output_len] as *mut u8) }; - *value = *decimals; - output_len += size_of::(); - } - - Self::Burn2 { amount, decimals } => { - output[output_len] = 15; - output_len += size_of::(); + }; + buf + } - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[output_len] as *mut u8 as *mut u64) }; - *value = *amount; - output_len += size_of::(); + fn unpack_pubkey(input: &[u8]) -> Result<(Pubkey, &[u8]), ProgramError> { + if input.len() >= 32 { + let (key, rest) = input.split_at(32); + let pk = Pubkey::new(key); + Ok((pk, rest)) + } else { + Err(TokenError::InvalidInstruction.into()) + } + } - let value = unsafe { &mut *(&mut output[output_len] as *mut u8) }; - *value = *decimals; - output_len += size_of::(); + fn unpack_pubkey_option(input: &[u8]) -> Result<(COption, &[u8]), ProgramError> { + match input.split_first() { + Option::Some((&0, rest)) => Ok((COption::None, rest)), + Option::Some((&1, rest)) if rest.len() >= 32 => { + let (key, rest) = rest.split_at(32); + let pk = Pubkey::new(key); + Ok((COption::Some(pk), rest)) } + _ => Err(TokenError::InvalidInstruction.into()), } + } - output.truncate(output_len); - Ok(output) + fn pack_pubkey_option(value: &COption, buf: &mut Vec) { + match *value { + COption::Some(ref key) => { + buf.push(1); + buf.extend_from_slice(&key.to_bytes()); + } + COption::None => buf.push(0), + } } } @@ -701,7 +584,7 @@ pub fn initialize_mint( freeze_authority, decimals, } - .pack()?; + .pack(); let accounts = vec![ AccountMeta::new(*mint_pubkey, false), @@ -722,7 +605,7 @@ pub fn initialize_account( mint_pubkey: &Pubkey, owner_pubkey: &Pubkey, ) -> Result { - let data = TokenInstruction::InitializeAccount.pack()?; + let data = TokenInstruction::InitializeAccount.pack(); // TODO do we need to return result? let accounts = vec![ AccountMeta::new(*account_pubkey, false), @@ -751,7 +634,7 @@ pub fn initialize_multisig( { return Err(ProgramError::MissingRequiredSignature); } - let data = TokenInstruction::InitializeMultisig { m }.pack()?; + let data = TokenInstruction::InitializeMultisig { m }.pack(); let mut accounts = Vec::with_capacity(1 + 1 + signer_pubkeys.len()); accounts.push(AccountMeta::new(*multisig_pubkey, false)); @@ -776,7 +659,7 @@ pub fn transfer( signer_pubkeys: &[&Pubkey], amount: u64, ) -> Result { - let data = TokenInstruction::Transfer { amount }.pack()?; + let data = TokenInstruction::Transfer { amount }.pack(); let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); accounts.push(AccountMeta::new(*source_pubkey, false)); @@ -805,7 +688,7 @@ pub fn approve( signer_pubkeys: &[&Pubkey], amount: u64, ) -> Result { - let data = TokenInstruction::Approve { amount }.pack()?; + let data = TokenInstruction::Approve { amount }.pack(); let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); accounts.push(AccountMeta::new(*source_pubkey, false)); @@ -832,7 +715,7 @@ pub fn revoke( owner_pubkey: &Pubkey, signer_pubkeys: &[&Pubkey], ) -> Result { - let data = TokenInstruction::Revoke.pack()?; + let data = TokenInstruction::Revoke.pack(); let mut accounts = Vec::with_capacity(2 + signer_pubkeys.len()); accounts.push(AccountMeta::new_readonly(*source_pubkey, false)); @@ -865,7 +748,7 @@ pub fn set_authority( authority_type, new_authority, } - .pack()?; + .pack(); let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); accounts.push(AccountMeta::new(*owned_pubkey, false)); @@ -893,7 +776,7 @@ pub fn mint_to( signer_pubkeys: &[&Pubkey], amount: u64, ) -> Result { - let data = TokenInstruction::MintTo { amount }.pack()?; + let data = TokenInstruction::MintTo { amount }.pack(); let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); accounts.push(AccountMeta::new(*mint_pubkey, false)); @@ -922,7 +805,7 @@ pub fn burn( signer_pubkeys: &[&Pubkey], amount: u64, ) -> Result { - let data = TokenInstruction::Burn { amount }.pack()?; + let data = TokenInstruction::Burn { amount }.pack(); let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); accounts.push(AccountMeta::new(*account_pubkey, false)); @@ -950,7 +833,7 @@ pub fn close_account( owner_pubkey: &Pubkey, signer_pubkeys: &[&Pubkey], ) -> Result { - let data = TokenInstruction::CloseAccount.pack()?; + let data = TokenInstruction::CloseAccount.pack(); let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); accounts.push(AccountMeta::new(*account_pubkey, false)); @@ -978,7 +861,7 @@ pub fn freeze_account( owner_pubkey: &Pubkey, signer_pubkeys: &[&Pubkey], ) -> Result { - let data = TokenInstruction::FreezeAccount.pack()?; + let data = TokenInstruction::FreezeAccount.pack(); let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); accounts.push(AccountMeta::new(*account_pubkey, false)); @@ -1006,7 +889,7 @@ pub fn thaw_account( owner_pubkey: &Pubkey, signer_pubkeys: &[&Pubkey], ) -> Result { - let data = TokenInstruction::ThawAccount.pack()?; + let data = TokenInstruction::ThawAccount.pack(); let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); accounts.push(AccountMeta::new(*account_pubkey, false)); @@ -1038,7 +921,7 @@ pub fn transfer2( amount: u64, decimals: u8, ) -> Result { - let data = TokenInstruction::Transfer2 { amount, decimals }.pack()?; + let data = TokenInstruction::Transfer2 { amount, decimals }.pack(); let mut accounts = Vec::with_capacity(4 + signer_pubkeys.len()); accounts.push(AccountMeta::new(*source_pubkey, false)); @@ -1071,7 +954,7 @@ pub fn approve2( amount: u64, decimals: u8, ) -> Result { - let data = TokenInstruction::Approve2 { amount, decimals }.pack()?; + let data = TokenInstruction::Approve2 { amount, decimals }.pack(); let mut accounts = Vec::with_capacity(4 + signer_pubkeys.len()); accounts.push(AccountMeta::new(*source_pubkey, false)); @@ -1102,7 +985,7 @@ pub fn mint_to2( amount: u64, decimals: u8, ) -> Result { - let data = TokenInstruction::MintTo2 { amount, decimals }.pack()?; + let data = TokenInstruction::MintTo2 { amount, decimals }.pack(); let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); accounts.push(AccountMeta::new(*mint_pubkey, false)); @@ -1132,7 +1015,7 @@ pub fn burn2( amount: u64, decimals: u8, ) -> Result { - let data = TokenInstruction::Burn2 { amount, decimals }.pack()?; + let data = TokenInstruction::Burn2 { amount, decimals }.pack(); let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); accounts.push(AccountMeta::new(*account_pubkey, false)); @@ -1168,7 +1051,7 @@ mod test { mint_authority: Pubkey::new(&[1u8; 32]), freeze_authority: COption::None, }; - let packed = check.pack().unwrap(); + let packed = check.pack(); let mut expect = Vec::from([0u8, 2]); expect.extend_from_slice(&[1u8; 32]); expect.extend_from_slice(&[0]); @@ -1181,7 +1064,7 @@ mod test { mint_authority: Pubkey::new(&[2u8; 32]), freeze_authority: COption::Some(Pubkey::new(&[3u8; 32])), }; - let packed = check.pack().unwrap(); + let packed = check.pack(); let mut expect = vec![0u8, 2]; expect.extend_from_slice(&[2u8; 32]); expect.extend_from_slice(&[1]); @@ -1191,35 +1074,35 @@ mod test { assert_eq!(unpacked, check); let check = TokenInstruction::InitializeAccount; - let packed = check.pack().unwrap(); + let packed = check.pack(); let expect = Vec::from([1u8]); assert_eq!(packed, expect); let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); let check = TokenInstruction::InitializeMultisig { m: 1 }; - let packed = check.pack().unwrap(); + let packed = check.pack(); let expect = Vec::from([2u8, 1]); assert_eq!(packed, expect); let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); let check = TokenInstruction::Transfer { amount: 1 }; - let packed = check.pack().unwrap(); + let packed = check.pack(); let expect = Vec::from([3u8, 1, 0, 0, 0, 0, 0, 0, 0]); assert_eq!(packed, expect); let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); let check = TokenInstruction::Approve { amount: 1 }; - let packed = check.pack().unwrap(); + let packed = check.pack(); let expect = Vec::from([4u8, 1, 0, 0, 0, 0, 0, 0, 0]); assert_eq!(packed, expect); let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); let check = TokenInstruction::Revoke; - let packed = check.pack().unwrap(); + let packed = check.pack(); let expect = Vec::from([5u8]); assert_eq!(packed, expect); let unpacked = TokenInstruction::unpack(&expect).unwrap(); @@ -1229,7 +1112,7 @@ mod test { authority_type: AuthorityType::FreezeAccount, new_authority: COption::Some(Pubkey::new(&[4u8; 32])), }; - let packed = check.pack().unwrap(); + let packed = check.pack(); let mut expect = Vec::from([6u8, 1]); expect.extend_from_slice(&[1]); expect.extend_from_slice(&[4u8; 32]); @@ -1238,35 +1121,35 @@ mod test { assert_eq!(unpacked, check); let check = TokenInstruction::MintTo { amount: 1 }; - let packed = check.pack().unwrap(); + let packed = check.pack(); let expect = Vec::from([7u8, 1, 0, 0, 0, 0, 0, 0, 0]); assert_eq!(packed, expect); let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); let check = TokenInstruction::Burn { amount: 1 }; - let packed = check.pack().unwrap(); + let packed = check.pack(); let expect = Vec::from([8u8, 1, 0, 0, 0, 0, 0, 0, 0]); assert_eq!(packed, expect); let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); let check = TokenInstruction::CloseAccount; - let packed = check.pack().unwrap(); + let packed = check.pack(); let expect = Vec::from([9u8]); assert_eq!(packed, expect); let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); let check = TokenInstruction::FreezeAccount; - let packed = check.pack().unwrap(); + let packed = check.pack(); let expect = Vec::from([10u8]); assert_eq!(packed, expect); let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); let check = TokenInstruction::ThawAccount; - let packed = check.pack().unwrap(); + let packed = check.pack(); let expect = Vec::from([11u8]); assert_eq!(packed, expect); let unpacked = TokenInstruction::unpack(&expect).unwrap(); @@ -1276,7 +1159,7 @@ mod test { amount: 1, decimals: 2, }; - let packed = check.pack().unwrap(); + let packed = check.pack(); let expect = Vec::from([12u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]); assert_eq!(packed, expect); let unpacked = TokenInstruction::unpack(&expect).unwrap(); @@ -1286,7 +1169,7 @@ mod test { amount: 1, decimals: 2, }; - let packed = check.pack().unwrap(); + let packed = check.pack(); let expect = Vec::from([13u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]); assert_eq!(packed, expect); let unpacked = TokenInstruction::unpack(&expect).unwrap(); @@ -1296,7 +1179,7 @@ mod test { amount: 1, decimals: 2, }; - let packed = check.pack().unwrap(); + let packed = check.pack(); let expect = Vec::from([14u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]); assert_eq!(packed, expect); let unpacked = TokenInstruction::unpack(&expect).unwrap(); @@ -1306,7 +1189,7 @@ mod test { amount: 1, decimals: 2, }; - let packed = check.pack().unwrap(); + let packed = check.pack(); let expect = Vec::from([15u8, 1, 0, 0, 0, 0, 0, 0, 0, 2]); assert_eq!(packed, expect); let unpacked = TokenInstruction::unpack(&expect).unwrap(); diff --git a/program/src/lib.rs b/program/src/lib.rs index e188755..cb58c52 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -7,6 +7,7 @@ pub mod error; pub mod instruction; pub mod native_mint; pub mod option; +pub mod pack; pub mod processor; pub mod state; diff --git a/program/src/option.rs b/program/src/option.rs index 9e6f16d..677408b 100644 --- a/program/src/option.rs +++ b/program/src/option.rs @@ -676,55 +676,6 @@ impl COption { pub fn replace(&mut self, value: T) -> COption { mem::replace(self, COption::Some(value)) } - - ///////////////////////////////////////////////////////////////////////// - // SPL Token-Specific Methods - ///////////////////////////////////////////////////////////////////////// - - /// Packs a COption into a mutable slice as compactly as possible - #[inline] - pub fn pack(&self, output: &mut [u8], cursor: &mut usize) - where - T: Copy, - { - match self { - COption::Some(some_value) => { - output[*cursor] = 1; - *cursor += mem::size_of::(); - - #[allow(clippy::cast_ptr_alignment)] - let value = unsafe { &mut *(&mut output[*cursor] as *mut u8 as *mut T) }; - *value = *some_value; - *cursor += mem::size_of::(); - } - COption::None => { - output[*cursor] = 0; - *cursor += mem::size_of::(); - } - } - } - - /// Unpacks a COption from a compact slice - #[inline] - pub fn unpack_or(input: &[u8], cursor: &mut usize, error: E) -> Result, E> - where - T: Copy, - { - match input[*cursor] { - 0 => { - *cursor += mem::size_of::(); - Ok(COption::None) - } - 1 => { - *cursor += mem::size_of::(); - #[allow(clippy::cast_ptr_alignment)] - let result = unsafe { *(&input[*cursor] as *const u8 as *const T) }; - *cursor += mem::size_of::(); - Ok(COption::Some(result)) - } - _ => Err(error), - } - } } impl COption<&T> { @@ -1034,7 +985,6 @@ impl Into> for COption { #[cfg(test)] mod test { use super::*; - use solana_sdk::pubkey::Pubkey; #[test] fn test_from_rust_option() { @@ -1050,63 +1000,4 @@ mod test { let expected = c_option.into(); assert_eq!(option, expected); } - - #[test] - fn test_coption_packing() { - // Solana Pubkey - let option_pubkey = COption::Some(Pubkey::new(&[2u8; 32])); - let expected_size = mem::size_of::() + mem::size_of::(); - let mut output = vec![0u8; expected_size]; - let mut cursor = 0; - option_pubkey.pack(&mut output, &mut cursor); - - let mut expected = vec![1u8]; - expected.extend_from_slice(&[2u8; 32]); - assert_eq!(output, expected); - - let mut cursor = 0; - let unpacked = COption::unpack_or(&expected, &mut cursor, "Error".to_string()).unwrap(); - assert_eq!(unpacked, option_pubkey); - - let option_pubkey: COption = COption::None; - let expected_size = mem::size_of::(); - let mut output = vec![0u8; expected_size]; - let mut cursor = 0; - option_pubkey.pack(&mut output, &mut cursor); - - let expected = vec![0u8]; - assert_eq!(output, expected); - - let mut cursor = 0; - let unpacked = COption::unpack_or(&expected, &mut cursor, "Error".to_string()).unwrap(); - assert_eq!(unpacked, option_pubkey); - - // u64 - let option_pubkey = COption::Some(99u64); - let expected_size = mem::size_of::() + mem::size_of::(); - let mut output = vec![0u8; expected_size]; - let mut cursor = 0; - option_pubkey.pack(&mut output, &mut cursor); - - let mut expected = vec![1u8]; - expected.extend_from_slice(&[99, 0, 0, 0, 0, 0, 0, 0]); - assert_eq!(output, expected); - - let mut cursor = 0; - let unpacked = COption::unpack_or(&expected, &mut cursor, "Error".to_string()).unwrap(); - assert_eq!(unpacked, option_pubkey); - - let option_pubkey: COption = COption::None; - let expected_size = mem::size_of::(); - let mut output = vec![0u8; expected_size]; - let mut cursor = 0; - option_pubkey.pack(&mut output, &mut cursor); - - let expected = vec![0u8]; - assert_eq!(output, expected); - - let mut cursor = 0; - let unpacked = COption::unpack_or(&expected, &mut cursor, "Error".to_string()).unwrap(); - assert_eq!(unpacked, option_pubkey); - } } diff --git a/program/src/pack.rs b/program/src/pack.rs new file mode 100644 index 0000000..bf6a205 --- /dev/null +++ b/program/src/pack.rs @@ -0,0 +1,76 @@ +//! State transition types + +use crate::error::TokenError; +use solana_sdk::program_error::ProgramError; + +/// Check is a token state is initialized +pub trait IsInitialized { + /// Is initialized + fn is_initialized(&self) -> bool; +} + +/// Depends on Sized +pub trait Sealed: Sized {} + +/// Safely and efficiently (de)serialize account state +pub trait Pack: Sealed { + /// The length, in bytes, of the packed representation + const LEN: usize; + #[doc(hidden)] + fn pack_into_slice(&self, dst: &mut [u8]); + #[doc(hidden)] + fn unpack_from_slice(src: &[u8]) -> Result; + + /// Borrow `Self` from `input` for the duration of the call to `f`, but first check that `Self` + /// is initialized + #[inline(never)] + fn unpack_mut(input: &mut [u8], f: &mut F) -> Result + where + F: FnMut(&mut Self) -> Result, + Self: IsInitialized, + { + let mut t = unpack(input)?; + let u = f(&mut t)?; + pack(t, input)?; + Ok(u) + } + + /// Borrow `Self` from `input` for the duration of the call to `f`, without checking that + /// `Self` has been initialized + #[inline(never)] + fn unpack_unchecked_mut(input: &mut [u8], f: &mut F) -> Result + where + F: FnMut(&mut Self) -> Result, + { + let mut t = unpack_unchecked(input)?; + let u = f(&mut t)?; + pack(t, input)?; + Ok(u) + } +} + +fn pack(src: T, dst: &mut [u8]) -> Result<(), ProgramError> { + if dst.len() < T::LEN { + println!("dlen {:?} tlen {:?}", dst.len(), T::LEN); + return Err(ProgramError::InvalidAccountData); + } + src.pack_into_slice(dst); + Ok(()) +} + +fn unpack(input: &[u8]) -> Result { + let value: T = unpack_unchecked(input)?; + if value.is_initialized() { + Ok(value) + } else { + Err(TokenError::UninitializedState.into()) + } +} + +fn unpack_unchecked(input: &[u8]) -> Result { + if input.len() < T::LEN { + println!("ilen {:?} tlen {:?}", input.len(), T::LEN); + return Err(ProgramError::InvalidAccountData); + } + Ok(T::unpack_from_slice(input)?) +} diff --git a/program/src/processor.rs b/program/src/processor.rs index 4826f2a..d2f647e 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -6,7 +6,8 @@ use crate::{ error::TokenError, instruction::{is_valid_signer_index, AuthorityType, TokenInstruction}, option::COption, - state::{self, Account, AccountState, IsInitialized, Mint, Multisig}, + pack::{IsInitialized, Pack}, + state::{Account, AccountState, Mint, Multisig}, }; use num_traits::FromPrimitive; use solana_sdk::{ @@ -32,25 +33,26 @@ impl Processor { ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let mint_info = next_account_info(account_info_iter)?; - let mint_info_data_len = mint_info.data_len(); + let mint_data_len = mint_info.data_len(); let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?; - let mut mint_info_data = mint_info.data.borrow_mut(); - let mut mint: &mut Mint = state::unpack_unchecked(&mut mint_info_data)?; - if mint.is_initialized { - return Err(TokenError::AlreadyInUse.into()); - } + let mut mint_data = mint_info.data.borrow_mut(); + Mint::unpack_unchecked_mut(&mut mint_data, &mut |mint: &mut Mint| { + if mint.is_initialized { + return Err(TokenError::AlreadyInUse.into()); + } - if !rent.is_exempt(mint_info.lamports(), mint_info_data_len) { - return Err(TokenError::NotRentExempt.into()); - } + if !rent.is_exempt(mint_info.lamports(), mint_data_len) { + return Err(TokenError::NotRentExempt.into()); + } - mint.mint_authority = COption::Some(mint_authority); - mint.decimals = decimals; - mint.is_initialized = true; - mint.freeze_authority = freeze_authority; + mint.mint_authority = COption::Some(mint_authority); + mint.decimals = decimals; + mint.is_initialized = true; + mint.freeze_authority = freeze_authority; - Ok(()) + Ok(()) + }) } /// Processes an [InitializeAccount](enum.TokenInstruction.html) instruction. @@ -63,39 +65,40 @@ impl Processor { let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?; let mut new_account_data = new_account_info.data.borrow_mut(); - let mut account: &mut Account = state::unpack_unchecked(&mut new_account_data)?; - if account.is_initialized() { - return Err(TokenError::AlreadyInUse.into()); - } + Account::unpack_unchecked_mut(&mut new_account_data, &mut |account: &mut Account| { + if account.is_initialized() { + return Err(TokenError::AlreadyInUse.into()); + } - if !rent.is_exempt(new_account_info.lamports(), new_account_info_data_len) { - return Err(TokenError::NotRentExempt.into()); - } + if !rent.is_exempt(new_account_info.lamports(), new_account_info_data_len) { + return Err(TokenError::NotRentExempt.into()); + } - if *mint_info.key != crate::native_mint::id() { - let mut mint_info_data = mint_info.data.borrow_mut(); - let _: &mut Mint = state::unpack(&mut mint_info_data) - .map_err(|_| Into::::into(TokenError::InvalidMint))?; - } + if *mint_info.key != crate::native_mint::id() { + let mut mint_info_data = mint_info.data.borrow_mut(); + Mint::unpack_mut(&mut mint_info_data, &mut |_| Ok(())) + .map_err(|_| Into::::into(TokenError::InvalidMint))?; + } - account.mint = *mint_info.key; - account.owner = *owner_info.key; - account.delegate = COption::None; - account.delegated_amount = 0; - account.state = AccountState::Initialized; - if *mint_info.key == crate::native_mint::id() { - let rent_exempt_reserve = rent.minimum_balance(new_account_info_data_len); - account.is_native = COption::Some(rent_exempt_reserve); - account.amount = new_account_info - .lamports() - .checked_sub(rent_exempt_reserve) - .ok_or(TokenError::Overflow)?; - } else { - account.is_native = COption::None; - account.amount = 0; - }; + account.mint = *mint_info.key; + account.owner = *owner_info.key; + account.delegate = COption::None; + account.delegated_amount = 0; + account.state = AccountState::Initialized; + if *mint_info.key == crate::native_mint::id() { + let rent_exempt_reserve = rent.minimum_balance(new_account_info_data_len); + account.is_native = COption::Some(rent_exempt_reserve); + account.amount = new_account_info + .lamports() + .checked_sub(rent_exempt_reserve) + .ok_or(TokenError::Overflow)?; + } else { + account.is_native = COption::None; + account.amount = 0; + }; - Ok(()) + Ok(()) + }) } /// Processes a [InitializeMultisig](enum.TokenInstruction.html) instruction. @@ -106,30 +109,34 @@ impl Processor { let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?; let mut multisig_account_data = multisig_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack_unchecked(&mut multisig_account_data)?; - if multisig.is_initialized { - return Err(TokenError::AlreadyInUse.into()); - } + Multisig::unpack_unchecked_mut( + &mut multisig_account_data, + &mut |multisig: &mut Multisig| { + if multisig.is_initialized { + return Err(TokenError::AlreadyInUse.into()); + } - if !rent.is_exempt(multisig_info.lamports(), multisig_info_data_len) { - return Err(TokenError::NotRentExempt.into()); - } + if !rent.is_exempt(multisig_info.lamports(), multisig_info_data_len) { + return Err(TokenError::NotRentExempt.into()); + } - let signer_infos = account_info_iter.as_slice(); - multisig.m = m; - multisig.n = signer_infos.len() as u8; - if !is_valid_signer_index(multisig.n as usize) { - return Err(TokenError::InvalidNumberOfProvidedSigners.into()); - } - if !is_valid_signer_index(multisig.m as usize) { - return Err(TokenError::InvalidNumberOfRequiredSigners.into()); - } - for (i, signer_info) in signer_infos.iter().enumerate() { - multisig.signers[i] = *signer_info.key; - } - multisig.is_initialized = true; + let signer_infos = account_info_iter.as_slice(); + multisig.m = m; + multisig.n = signer_infos.len() as u8; + if !is_valid_signer_index(multisig.n as usize) { + return Err(TokenError::InvalidNumberOfProvidedSigners.into()); + } + if !is_valid_signer_index(multisig.m as usize) { + return Err(TokenError::InvalidNumberOfRequiredSigners.into()); + } + for (i, signer_info) in signer_infos.iter().enumerate() { + multisig.signers[i] = *signer_info.key; + } + multisig.is_initialized = true; - Ok(()) + Ok(()) + }, + ) } /// Processes a [Transfer](enum.TokenInstruction.html) instruction. @@ -157,82 +164,84 @@ impl Processor { } let mut source_data = source_account_info.data.borrow_mut(); - let mut source_account: &mut Account = state::unpack(&mut source_data)?; let mut dest_data = dest_account_info.data.borrow_mut(); - let mut dest_account: &mut Account = state::unpack(&mut dest_data)?; - - if source_account.amount < amount { - return Err(TokenError::InsufficientFunds.into()); - } - if source_account.mint != dest_account.mint { - return Err(TokenError::MintMismatch.into()); - } - if source_account.is_frozen() || dest_account.is_frozen() { - return Err(TokenError::AccountFrozen.into()); - } + Account::unpack_mut(&mut source_data, &mut |source_account: &mut Account| { + Account::unpack_mut(&mut dest_data, &mut |dest_account: &mut Account| { + if source_account.amount < amount { + return Err(TokenError::InsufficientFunds.into()); + } + if source_account.mint != dest_account.mint { + return Err(TokenError::MintMismatch.into()); + } + if source_account.is_frozen() || dest_account.is_frozen() { + return Err(TokenError::AccountFrozen.into()); + } - if let Some((mint_account_info, expected_decimals)) = expected_mint_info { - if source_account.mint != *mint_account_info.key { - return Err(TokenError::MintMismatch.into()); - } + if let Some((mint_account_info, expected_decimals)) = expected_mint_info { + if source_account.mint != *mint_account_info.key { + return Err(TokenError::MintMismatch.into()); + } - let mut mint_info_data = mint_account_info.data.borrow_mut(); - let mint: &Mint = state::unpack_unchecked(&mut mint_info_data)?; + let mut mint_info_data = mint_account_info.data.borrow_mut(); + Mint::unpack_mut(&mut mint_info_data, &mut |mint: &mut Mint| { + if expected_decimals != mint.decimals { + return Err(TokenError::MintDecimalsMismatch.into()); + } + Ok(()) + })?; + } - if expected_decimals != mint.decimals { - return Err(TokenError::MintDecimalsMismatch.into()); - } - } + match source_account.delegate { + COption::Some(ref delegate) if authority_info.key == delegate => { + Self::validate_owner( + program_id, + delegate, + authority_info, + account_info_iter.as_slice(), + )?; + if source_account.delegated_amount < amount { + return Err(TokenError::InsufficientFunds.into()); + } + source_account.delegated_amount = source_account + .delegated_amount + .checked_sub(amount) + .ok_or(TokenError::Overflow)?; + if source_account.delegated_amount == 0 { + source_account.delegate = COption::None; + } + } + _ => Self::validate_owner( + program_id, + &source_account.owner, + authority_info, + account_info_iter.as_slice(), + )?, + }; - match source_account.delegate { - COption::Some(ref delegate) if authority_info.key == delegate => { - Self::validate_owner( - program_id, - delegate, - authority_info, - account_info_iter.as_slice(), - )?; - if source_account.delegated_amount < amount { - return Err(TokenError::InsufficientFunds.into()); - } - source_account.delegated_amount = source_account - .delegated_amount + source_account.amount = source_account + .amount .checked_sub(amount) .ok_or(TokenError::Overflow)?; - if source_account.delegated_amount == 0 { - source_account.delegate = COption::None; - } - } - _ => Self::validate_owner( - program_id, - &source_account.owner, - authority_info, - account_info_iter.as_slice(), - )?, - }; + dest_account.amount = dest_account + .amount + .checked_add(amount) + .ok_or(TokenError::Overflow)?; - source_account.amount = source_account - .amount - .checked_sub(amount) - .ok_or(TokenError::Overflow)?; - dest_account.amount = dest_account - .amount - .checked_add(amount) - .ok_or(TokenError::Overflow)?; - - if source_account.is_native() { - let source_starting_lamports = source_account_info.lamports(); - **source_account_info.lamports.borrow_mut() = source_starting_lamports - .checked_sub(amount) - .ok_or(TokenError::Overflow)?; + if source_account.is_native() { + let source_starting_lamports = source_account_info.lamports(); + **source_account_info.lamports.borrow_mut() = source_starting_lamports + .checked_sub(amount) + .ok_or(TokenError::Overflow)?; - let dest_starting_lamports = dest_account_info.lamports(); - **dest_account_info.lamports.borrow_mut() = dest_starting_lamports - .checked_add(amount) - .ok_or(TokenError::Overflow)?; - } + let dest_starting_lamports = dest_account_info.lamports(); + **dest_account_info.lamports.borrow_mut() = dest_starting_lamports + .checked_add(amount) + .ok_or(TokenError::Overflow)?; + } - Ok(()) + Ok(()) + }) + }) } /// Processes an [Approve](enum.TokenInstruction.html) instruction. @@ -254,36 +263,37 @@ impl Processor { let owner_info = next_account_info(account_info_iter)?; let mut source_data = source_account_info.data.borrow_mut(); - let mut source_account: &mut Account = state::unpack(&mut source_data)?; - - if source_account.is_frozen() { - return Err(TokenError::AccountFrozen.into()); - } - - if let Some((mint_account_info, expected_decimals)) = expected_mint_info { - if source_account.mint != *mint_account_info.key { - return Err(TokenError::MintMismatch.into()); + Account::unpack_mut(&mut source_data, &mut |source_account: &mut Account| { + if source_account.is_frozen() { + return Err(TokenError::AccountFrozen.into()); } - let mut mint_info_data = mint_account_info.data.borrow_mut(); - let mint: &Mint = state::unpack_unchecked(&mut mint_info_data)?; + if let Some((mint_account_info, expected_decimals)) = expected_mint_info { + if source_account.mint != *mint_account_info.key { + return Err(TokenError::MintMismatch.into()); + } - if expected_decimals != mint.decimals { - return Err(TokenError::MintDecimalsMismatch.into()); + let mut mint_info_data = mint_account_info.data.borrow_mut(); + Mint::unpack_mut(&mut mint_info_data, &mut |mint: &mut Mint| { + if expected_decimals != mint.decimals { + return Err(TokenError::MintDecimalsMismatch.into()); + } + Ok(()) + })?; } - } - Self::validate_owner( - program_id, - &source_account.owner, - owner_info, - account_info_iter.as_slice(), - )?; + Self::validate_owner( + program_id, + &source_account.owner, + owner_info, + account_info_iter.as_slice(), + )?; - source_account.delegate = COption::Some(*delegate_info.key); - source_account.delegated_amount = amount; + source_account.delegate = COption::Some(*delegate_info.key); + source_account.delegated_amount = amount; - Ok(()) + Ok(()) + }) } /// Processes an [Revoke](enum.TokenInstruction.html) instruction. @@ -292,24 +302,25 @@ impl Processor { let source_account_info = next_account_info(account_info_iter)?; let mut source_data = source_account_info.data.borrow_mut(); - let mut source_account: &mut Account = state::unpack(&mut source_data)?; - let owner_info = next_account_info(account_info_iter)?; + Account::unpack_mut(&mut source_data, &mut |source_account: &mut Account| { + let owner_info = next_account_info(account_info_iter)?; - if source_account.is_frozen() { - return Err(TokenError::AccountFrozen.into()); - } + if source_account.is_frozen() { + return Err(TokenError::AccountFrozen.into()); + } - Self::validate_owner( - program_id, - &source_account.owner, - owner_info, - account_info_iter.as_slice(), - )?; + Self::validate_owner( + program_id, + &source_account.owner, + owner_info, + account_info_iter.as_slice(), + )?; - source_account.delegate = COption::None; - source_account.delegated_amount = 0; + source_account.delegate = COption::None; + source_account.delegated_amount = 0; - Ok(()) + Ok(()) + }) } /// Processes a [SetAuthority](enum.TokenInstruction.html) instruction. @@ -325,78 +336,80 @@ impl Processor { if account_info.data_len() == size_of::() { let mut account_data = account_info.data.borrow_mut(); - let mut account: &mut Account = state::unpack(&mut account_data)?; - - if account.is_frozen() { - return Err(TokenError::AccountFrozen.into()); - } - - match authority_type { - AuthorityType::AccountHolder => { - Self::validate_owner( - program_id, - &account.owner, - authority_info, - account_info_iter.as_slice(), - )?; + Account::unpack_mut(&mut account_data, &mut |account: &mut Account| { + if account.is_frozen() { + return Err(TokenError::AccountFrozen.into()); + } - if let COption::Some(authority) = new_authority { - account.owner = authority; - } else { - return Err(TokenError::InvalidInstruction.into()); + match authority_type { + AuthorityType::AccountHolder => { + Self::validate_owner( + program_id, + &account.owner, + authority_info, + account_info_iter.as_slice(), + )?; + + if let COption::Some(authority) = new_authority { + account.owner = authority; + } else { + return Err(TokenError::InvalidInstruction.into()); + } + } + AuthorityType::CloseAccount => { + let authority = account.close_authority.unwrap_or(account.owner); + Self::validate_owner( + program_id, + &authority, + authority_info, + account_info_iter.as_slice(), + )?; + account.close_authority = new_authority; + } + _ => { + return Err(TokenError::AuthorityTypeNotSupported.into()); } } - AuthorityType::CloseAccount => { - let authority = account.close_authority.unwrap_or(account.owner); - Self::validate_owner( - program_id, - &authority, - authority_info, - account_info_iter.as_slice(), - )?; - account.close_authority = new_authority; - } - _ => { - return Err(TokenError::AuthorityTypeNotSupported.into()); - } - } + Ok(()) + })?; } else if account_info.data_len() == size_of::() { - let mut account_data = account_info.data.borrow_mut(); - let mut mint: &mut Mint = state::unpack(&mut account_data)?; - - match authority_type { - AuthorityType::MintTokens => { - // Once a mint's supply is fixed, it cannot be undone by setting a new - // mint_authority - let mint_authority = mint - .mint_authority - .ok_or(Into::::into(TokenError::FixedSupply))?; - Self::validate_owner( - program_id, - &mint_authority, - authority_info, - account_info_iter.as_slice(), - )?; - mint.mint_authority = new_authority; - } - AuthorityType::FreezeAccount => { - // Once a mint's freeze authority is disabled, it cannot be re-enabled by - // setting a new freeze_authority - let freeze_authority = mint - .freeze_authority - .ok_or(Into::::into(TokenError::MintCannotFreeze))?; - Self::validate_owner( - program_id, - &freeze_authority, - authority_info, - account_info_iter.as_slice(), - )?; - mint.freeze_authority = new_authority; - } - _ => { - return Err(TokenError::AuthorityTypeNotSupported.into()); + let mut mint_data = account_info.data.borrow_mut(); + Mint::unpack_mut(&mut mint_data, &mut |mint: &mut Mint| { + match authority_type { + AuthorityType::MintTokens => { + // Once a mint's supply is fixed, it cannot be undone by setting a new + // mint_authority + let mint_authority = mint + .mint_authority + .ok_or(Into::::into(TokenError::FixedSupply))?; + Self::validate_owner( + program_id, + &mint_authority, + authority_info, + account_info_iter.as_slice(), + )?; + mint.mint_authority = new_authority; + } + AuthorityType::FreezeAccount => { + // Once a mint's freeze authority is disabled, it cannot be re-enabled by + // setting a new freeze_authority + let freeze_authority = mint + .freeze_authority + .ok_or(Into::::into(TokenError::MintCannotFreeze))?; + Self::validate_owner( + program_id, + &freeze_authority, + authority_info, + account_info_iter.as_slice(), + )?; + mint.freeze_authority = new_authority; + } + _ => { + return Err(TokenError::AuthorityTypeNotSupported.into()); + } } - } + Ok(()) + })?; } else { return Err(ProgramError::InvalidArgument); } @@ -417,53 +430,49 @@ impl Processor { let owner_info = next_account_info(account_info_iter)?; let mut dest_account_data = dest_account_info.data.borrow_mut(); - let mut dest_account: &mut Account = state::unpack(&mut dest_account_data)?; - - if dest_account.is_frozen() { - return Err(TokenError::AccountFrozen.into()); - } - - if dest_account.is_native() { - return Err(TokenError::NativeNotSupported.into()); - } - if mint_info.key != &dest_account.mint { - return Err(TokenError::MintMismatch.into()); - } - - let mut mint_info_data = mint_info.data.borrow_mut(); - let mint: &mut Mint = state::unpack(&mut mint_info_data)?; - - if let Some(expected_decimals) = expected_decimals { - if expected_decimals != mint.decimals { - return Err(TokenError::MintDecimalsMismatch.into()); + Account::unpack_mut(&mut dest_account_data, &mut |dest_account: &mut Account| { + if dest_account.is_frozen() { + return Err(TokenError::AccountFrozen.into()); } - } - match mint.mint_authority { - COption::Some(mint_authority) => { - Self::validate_owner( - program_id, - &mint_authority, - owner_info, - account_info_iter.as_slice(), - )?; + if dest_account.is_native() { + return Err(TokenError::NativeNotSupported.into()); } - COption::None => { - return Err(TokenError::FixedSupply.into()); + if mint_info.key != &dest_account.mint { + return Err(TokenError::MintMismatch.into()); } - } - dest_account.amount = dest_account - .amount - .checked_add(amount) - .ok_or(TokenError::Overflow)?; + let mut mint_info_data = mint_info.data.borrow_mut(); + Mint::unpack_mut(&mut mint_info_data, &mut |mint: &mut Mint| { + if let Some(expected_decimals) = expected_decimals { + if expected_decimals != mint.decimals { + return Err(TokenError::MintDecimalsMismatch.into()); + } + } + + match mint.mint_authority { + COption::Some(mint_authority) => Self::validate_owner( + program_id, + &mint_authority, + owner_info, + account_info_iter.as_slice(), + )?, + COption::None => return Err(TokenError::FixedSupply.into()), + } - mint.supply = mint - .supply - .checked_add(amount) - .ok_or(TokenError::Overflow)?; + dest_account.amount = dest_account + .amount + .checked_add(amount) + .ok_or(TokenError::Overflow)?; - Ok(()) + mint.supply = mint + .supply + .checked_add(amount) + .ok_or(TokenError::Overflow)?; + + Ok(()) + }) + }) } /// Processes a [Burn](enum.TokenInstruction.html) instruction. @@ -480,68 +489,68 @@ impl Processor { let authority_info = next_account_info(account_info_iter)?; let mut mint_data = mint_info.data.borrow_mut(); - let mint: &mut Mint = state::unpack(&mut mint_data)?; - let mut source_data = source_account_info.data.borrow_mut(); - let source_account: &mut Account = state::unpack(&mut source_data)?; - - if source_account.is_native() { - return Err(TokenError::NativeNotSupported.into()); - } - if mint_info.key != &source_account.mint { - return Err(TokenError::MintMismatch.into()); - } - if source_account.amount < amount { - return Err(TokenError::InsufficientFunds.into()); - } - if source_account.is_frozen() { - return Err(TokenError::AccountFrozen.into()); - } - - if let Some(expected_decimals) = expected_decimals { - if expected_decimals != mint.decimals { - return Err(TokenError::MintDecimalsMismatch.into()); - } - } + Mint::unpack_mut(&mut mint_data, &mut |mint: &mut Mint| { + Account::unpack_mut(&mut source_data, &mut |source_account: &mut Account| { + if source_account.is_native() { + return Err(TokenError::NativeNotSupported.into()); + } + if mint_info.key != &source_account.mint { + return Err(TokenError::MintMismatch.into()); + } + if source_account.amount < amount { + return Err(TokenError::InsufficientFunds.into()); + } + if source_account.is_frozen() { + return Err(TokenError::AccountFrozen.into()); + } - match source_account.delegate { - COption::Some(ref delegate) if authority_info.key == delegate => { - Self::validate_owner( - program_id, - delegate, - authority_info, - account_info_iter.as_slice(), - )?; + if let Some(expected_decimals) = expected_decimals { + if expected_decimals != mint.decimals { + return Err(TokenError::MintDecimalsMismatch.into()); + } + } - if source_account.delegated_amount < amount { - return Err(TokenError::InsufficientFunds.into()); + match source_account.delegate { + COption::Some(ref delegate) if authority_info.key == delegate => { + Self::validate_owner( + program_id, + delegate, + authority_info, + account_info_iter.as_slice(), + )?; + + if source_account.delegated_amount < amount { + return Err(TokenError::InsufficientFunds.into()); + } + source_account.delegated_amount = source_account + .delegated_amount + .checked_sub(amount) + .ok_or(TokenError::Overflow)?; + if source_account.delegated_amount == 0 { + source_account.delegate = COption::None; + } + } + _ => Self::validate_owner( + program_id, + &source_account.owner, + authority_info, + account_info_iter.as_slice(), + )?, } - source_account.delegated_amount = source_account - .delegated_amount + + source_account.amount = source_account + .amount + .checked_sub(amount) + .ok_or(TokenError::Overflow)?; + mint.supply = mint + .supply .checked_sub(amount) .ok_or(TokenError::Overflow)?; - if source_account.delegated_amount == 0 { - source_account.delegate = COption::None; - } - } - _ => Self::validate_owner( - program_id, - &source_account.owner, - authority_info, - account_info_iter.as_slice(), - )?, - } - - source_account.amount = source_account - .amount - .checked_sub(amount) - .ok_or(TokenError::Overflow)?; - mint.supply = mint - .supply - .checked_sub(amount) - .ok_or(TokenError::Overflow)?; - Ok(()) + Ok(()) + }) + }) } /// Processes a [CloseAccount](enum.TokenInstruction.html) instruction. @@ -552,31 +561,31 @@ impl Processor { let authority_info = next_account_info(account_info_iter)?; let mut source_data = source_account_info.data.borrow_mut(); - let source_account: &mut Account = state::unpack(&mut source_data)?; - - if !source_account.is_native() && source_account.amount != 0 { - return Err(TokenError::NonNativeHasBalance.into()); - } + Account::unpack_mut(&mut source_data, &mut |source_account: &mut Account| { + if !source_account.is_native() && source_account.amount != 0 { + return Err(TokenError::NonNativeHasBalance.into()); + } - let authority = source_account - .close_authority - .unwrap_or(source_account.owner); - Self::validate_owner( - program_id, - &authority, - authority_info, - account_info_iter.as_slice(), - )?; + let authority = source_account + .close_authority + .unwrap_or(source_account.owner); + Self::validate_owner( + program_id, + &authority, + authority_info, + account_info_iter.as_slice(), + )?; - let dest_starting_lamports = dest_account_info.lamports(); - **dest_account_info.lamports.borrow_mut() = dest_starting_lamports - .checked_add(source_account_info.lamports()) - .ok_or(TokenError::Overflow)?; + let dest_starting_lamports = dest_account_info.lamports(); + **dest_account_info.lamports.borrow_mut() = dest_starting_lamports + .checked_add(source_account_info.lamports()) + .ok_or(TokenError::Overflow)?; - **source_account_info.lamports.borrow_mut() = 0; - source_account.amount = 0; + **source_account_info.lamports.borrow_mut() = 0; + source_account.amount = 0; - Ok(()) + Ok(()) + }) } /// Processes a [FreezeAccount](enum.TokenInstruction.html) or a @@ -592,42 +601,39 @@ impl Processor { let authority_info = next_account_info(account_info_iter)?; let mut source_data = source_account_info.data.borrow_mut(); - let source_account: &mut Account = state::unpack(&mut source_data)?; - - if source_account.is_native() { - return Err(TokenError::NativeNotSupported.into()); - } - if mint_info.key != &source_account.mint { - return Err(TokenError::MintMismatch.into()); - } - if freeze && source_account.is_frozen() || !freeze && !source_account.is_frozen() { - return Err(TokenError::InvalidState.into()); - } - - let mut mint_info_data = mint_info.data.borrow_mut(); - let mint: &mut Mint = state::unpack(&mut mint_info_data)?; - - match mint.freeze_authority { - COption::Some(authority) => { - Self::validate_owner( - program_id, - &authority, - authority_info, - account_info_iter.as_slice(), - )?; + Account::unpack_mut(&mut source_data, &mut |source_account: &mut Account| { + if source_account.is_native() { + return Err(TokenError::NativeNotSupported.into()); } - COption::None => { - return Err(TokenError::MintCannotFreeze.into()); + if mint_info.key != &source_account.mint { + return Err(TokenError::MintMismatch.into()); + } + if freeze && source_account.is_frozen() || !freeze && !source_account.is_frozen() { + return Err(TokenError::InvalidState.into()); } - } - - source_account.state = if freeze { - AccountState::Frozen - } else { - AccountState::Initialized - }; - Ok(()) + let mut mint_data = mint_info.data.borrow_mut(); + Mint::unpack_mut( + &mut mint_data, + &mut |mint: &mut Mint| match mint.freeze_authority { + COption::Some(authority) => Self::validate_owner( + program_id, + &authority, + authority_info, + account_info_iter.as_slice(), + ), + COption::None => Err(TokenError::MintCannotFreeze.into()), + }, + )?; + + source_account.state = if freeze { + AccountState::Frozen + } else { + AccountState::Initialized + }; + + Ok(()) + }) } /// Processes an [Instruction](enum.Instruction.html). @@ -723,19 +729,21 @@ impl Processor { && owner_account_info.data_len() == std::mem::size_of::() { let mut owner_data = owner_account_info.data.borrow_mut(); - let multisig: &mut Multisig = state::unpack(&mut owner_data)?; - let mut num_signers = 0; - for signer in signers.iter() { - if multisig.signers[0..multisig.n as usize].contains(signer.key) { - if !signer.is_signer { - return Err(ProgramError::MissingRequiredSignature); + Multisig::unpack_mut(&mut owner_data, &mut |multisig: &mut Multisig| { + let mut num_signers = 0; + for signer in signers.iter() { + if multisig.signers[0..multisig.n as usize].contains(signer.key) { + if !signer.is_signer { + return Err(ProgramError::MissingRequiredSignature); + } + num_signers += 1; } - num_signers += 1; } - } - if num_signers < multisig.m { - return Err(ProgramError::MissingRequiredSignature); - } + if num_signers < multisig.m { + return Err(ProgramError::MissingRequiredSignature); + } + Ok(()) + })?; } else if !owner_account_info.is_signer { return Err(ProgramError::MissingRequiredSignature); } @@ -904,8 +912,11 @@ mod tests { vec![&mut mint2_account, &mut rent_sysvar], ) .unwrap(); - let mint2: &mut Mint = state::unpack(&mut mint2_account.data).unwrap(); - assert_eq!(mint2.freeze_authority, COption::Some(owner_key)); + Mint::unpack_unchecked_mut(&mut mint2_account.data, &mut |mint: &mut Mint| { + assert_eq!(mint.freeze_authority, COption::Some(owner_key)); + Ok(()) + }) + .unwrap(); } #[test] @@ -1065,8 +1076,11 @@ mod tests { ], ) .unwrap(); - let account: &mut Account = state::unpack(&mut mismatch_account.data).unwrap(); - account.mint = mint2_key; + Account::unpack_unchecked_mut(&mut mismatch_account.data, &mut |account: &mut Account| { + account.mint = mint2_key; + Ok(()) + }) + .unwrap(); // mint to account do_process_instruction( @@ -1292,8 +1306,11 @@ mod tests { ) .unwrap() } - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(account.amount, 1000); + Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + assert_eq!(account.amount, 1000); + Ok(()) + }) + .unwrap(); // insufficient funds assert_eq!( @@ -1447,17 +1464,20 @@ mod tests { vec![&mut mint_account, &mut rent_sysvar], ) .unwrap(); - let mint: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); - assert_eq!( - *mint, - Mint { - mint_authority: COption::Some(owner_key), - supply: 0, - decimals, - is_initialized: true, - freeze_authority: COption::None, - } - ); + Mint::unpack_unchecked_mut(&mut mint_account.data, &mut |mint: &mut Mint| { + assert_eq!( + *mint, + Mint { + mint_authority: COption::Some(owner_key), + supply: 0, + decimals, + is_initialized: true, + freeze_authority: COption::None, + } + ); + Ok(()) + }) + .unwrap(); // create account do_process_instruction( @@ -1477,6 +1497,12 @@ mod tests { vec![&mut mint_account, &mut account_account, &mut owner_account], ) .unwrap(); + Mint::unpack_unchecked_mut(&mut mint_account.data, &mut |_| Ok(())).unwrap(); + Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + assert_eq!(account.amount, 42); + Ok(()) + }) + .unwrap(); // mint to 2, with incorrect decimals assert_eq!( @@ -1496,9 +1522,12 @@ mod tests { ) ); - let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); - let dest_account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(dest_account.amount, 42); + Mint::unpack_unchecked_mut(&mut mint_account.data, &mut |_| Ok(())).unwrap(); + Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + assert_eq!(account.amount, 42); + Ok(()) + }) + .unwrap(); // mint to 2 do_process_instruction( @@ -1515,10 +1544,12 @@ mod tests { vec![&mut mint_account, &mut account_account, &mut owner_account], ) .unwrap(); - - let _: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); - let dest_account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(dest_account.amount, 84); + Mint::unpack_unchecked_mut(&mut mint_account.data, &mut |_| Ok(())).unwrap(); + Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + assert_eq!(account.amount, 84); + Ok(()) + }) + .unwrap(); } #[test] @@ -2135,8 +2166,11 @@ mod tests { ], ) .unwrap(); - let account: &mut Account = state::unpack(&mut mismatch_account.data).unwrap(); - account.mint = mint2_key; + Account::unpack_unchecked_mut(&mut mismatch_account.data, &mut |account: &mut Account| { + account.mint = mint2_key; + Ok(()) + }) + .unwrap(); // mint to do_process_instruction( @@ -2145,10 +2179,16 @@ mod tests { ) .unwrap(); - let mint: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); - assert_eq!(mint.supply, 42); - let dest_account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(dest_account.amount, 42); + Mint::unpack_unchecked_mut(&mut mint_account.data, &mut |mint: &mut Mint| { + assert_eq!(mint.supply, 42); + Ok(()) + }) + .unwrap(); + Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + assert_eq!(account.amount, 42); + Ok(()) + }) + .unwrap(); // mint to another account to test supply accumulation do_process_instruction( @@ -2157,10 +2197,16 @@ mod tests { ) .unwrap(); - let mint: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); - assert_eq!(mint.supply, 84); - let dest_account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); - assert_eq!(dest_account.amount, 42); + Mint::unpack_unchecked_mut(&mut mint_account.data, &mut |mint: &mut Mint| { + assert_eq!(mint.supply, 84); + Ok(()) + }) + .unwrap(); + Account::unpack_unchecked_mut(&mut account2_account.data, &mut |account: &mut Account| { + assert_eq!(account.amount, 42); + Ok(()) + }) + .unwrap(); // missing signer let mut instruction = @@ -2321,8 +2367,11 @@ mod tests { ], ) .unwrap(); - let account: &mut Account = state::unpack(&mut mismatch_account.data).unwrap(); - account.mint = mint2_key; + Account::unpack_unchecked_mut(&mut mismatch_account.data, &mut |account: &mut Account| { + account.mint = mint2_key; + Ok(()) + }) + .unwrap(); // mint to account do_process_instruction( @@ -2388,10 +2437,17 @@ mod tests { ) .unwrap(); - let mint: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); - assert_eq!(mint.supply, 1000 - 42); - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(account.amount, 1000 - 42); + Mint::unpack_unchecked_mut(&mut mint_account.data, &mut |mint: &mut Mint| { + assert_eq!(mint.supply, 1000 - 42); + + Ok(()) + }) + .unwrap(); + Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + assert_eq!(account.amount, 1000 - 42); + Ok(()) + }) + .unwrap(); // insufficient funds assert_eq!( @@ -2458,10 +2514,16 @@ mod tests { .unwrap(); // match - let mint: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); - assert_eq!(mint.supply, 1000 - 42 - 84); - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(account.amount, 1000 - 42 - 84); + Mint::unpack_unchecked_mut(&mut mint_account.data, &mut |mint: &mut Mint| { + assert_eq!(mint.supply, 1000 - 42 - 84); + Ok(()) + }) + .unwrap(); + Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + assert_eq!(account.amount, 1000 - 42 - 84); + Ok(()) + }) + .unwrap(); // insufficient funds approved via delegate assert_eq!( @@ -2908,11 +2970,14 @@ mod tests { } let mut lamports = 0; let mut data = vec![0; size_of::()]; - let mut multisig: &mut Multisig = state::unpack_unchecked(&mut data).unwrap(); - multisig.m = MAX_SIGNERS as u8; - multisig.n = MAX_SIGNERS as u8; - multisig.signers = signer_keys; - multisig.is_initialized = true; + Multisig::unpack_unchecked_mut(&mut data, &mut |multisig: &mut Multisig| { + multisig.m = MAX_SIGNERS as u8; + multisig.n = MAX_SIGNERS as u8; + multisig.signers = signer_keys; + multisig.is_initialized = true; + Ok(()) + }) + .unwrap(); let owner_account_info = AccountInfo::new( &owner_key, false, @@ -2930,17 +2995,23 @@ mod tests { // 1 of 11 { let mut data_ref_mut = owner_account_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); - multisig.m = 1; + Multisig::unpack_unchecked_mut(&mut data_ref_mut, &mut |multisig: &mut Multisig| { + multisig.m = 1; + Ok(()) + }) + .unwrap(); } Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap(); // 2:1 { let mut data_ref_mut = owner_account_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); - multisig.m = 2; - multisig.n = 1; + Multisig::unpack_unchecked_mut(&mut data_ref_mut, &mut |multisig: &mut Multisig| { + multisig.m = 2; + multisig.n = 1; + Ok(()) + }) + .unwrap(); } assert_eq!( Err(ProgramError::MissingRequiredSignature), @@ -2950,18 +3021,24 @@ mod tests { // 0:11 { let mut data_ref_mut = owner_account_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); - multisig.m = 0; - multisig.n = 11; + Multisig::unpack_unchecked_mut(&mut data_ref_mut, &mut |multisig: &mut Multisig| { + multisig.m = 0; + multisig.n = 11; + Ok(()) + }) + .unwrap(); } Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap(); // 2:11 but 0 provided { let mut data_ref_mut = owner_account_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); - multisig.m = 2; - multisig.n = 11; + Multisig::unpack_unchecked_mut(&mut data_ref_mut, &mut |multisig: &mut Multisig| { + multisig.m = 2; + multisig.n = 11; + Ok(()) + }) + .unwrap(); } assert_eq!( Err(ProgramError::MissingRequiredSignature), @@ -2970,9 +3047,12 @@ mod tests { // 2:11 but 1 provided { let mut data_ref_mut = owner_account_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); - multisig.m = 2; - multisig.n = 11; + Multisig::unpack_unchecked_mut(&mut data_ref_mut, &mut |multisig: &mut Multisig| { + multisig.m = 2; + multisig.n = 11; + Ok(()) + }) + .unwrap(); } assert_eq!( Err(ProgramError::MissingRequiredSignature), @@ -2982,9 +3062,12 @@ mod tests { // 2:11, 2 from middle provided { let mut data_ref_mut = owner_account_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); - multisig.m = 2; - multisig.n = 11; + Multisig::unpack_unchecked_mut(&mut data_ref_mut, &mut |multisig: &mut Multisig| { + multisig.m = 2; + multisig.n = 11; + Ok(()) + }) + .unwrap(); } Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers[5..7]) .unwrap(); @@ -2992,9 +3075,12 @@ mod tests { // 11:11, one is not a signer { let mut data_ref_mut = owner_account_info.data.borrow_mut(); - let mut multisig: &mut Multisig = state::unpack(&mut data_ref_mut).unwrap(); - multisig.m = 2; - multisig.n = 11; + Multisig::unpack_unchecked_mut(&mut data_ref_mut, &mut |multisig: &mut Multisig| { + multisig.m = 2; // TODO 11? + multisig.n = 11; + Ok(()) + }) + .unwrap(); } signers[5].is_signer = false; assert_eq!( @@ -3067,8 +3153,11 @@ mod tests { ], ) .unwrap(); - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(account.amount, 42); + Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + assert_eq!(account.amount, 42); + Ok(()) + }) + .unwrap(); // initialize native account do_process_instruction( @@ -3087,9 +3176,12 @@ mod tests { ], ) .unwrap(); - let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); - assert!(account.is_native()); - assert_eq!(account.amount, 42); + Account::unpack_unchecked_mut(&mut account2_account.data, &mut |account: &mut Account| { + assert!(account.is_native()); + assert_eq!(account.amount, 42); + Ok(()) + }) + .unwrap(); // close non-native account with balance assert_eq!( @@ -3135,10 +3227,13 @@ mod tests { ], ) .unwrap(); - let account: &mut Account = state::unpack_unchecked(&mut account_account.data).unwrap(); assert_eq!(account_account.lamports, 0); - assert_eq!(account.amount, 0); assert_eq!(account3_account.lamports, 2 * account_minimum_balance()); + Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + assert_eq!(account.amount, 0); + Ok(()) + }) + .unwrap(); // fund and initialize new non-native account to test close authority let account_key = pubkey_rand(); @@ -3197,8 +3292,12 @@ mod tests { ) .unwrap(); assert_eq!(account_account.lamports, 0); - assert_eq!(account.amount, 0); assert_eq!(account3_account.lamports, 2 * account_minimum_balance() + 2); + Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + assert_eq!(account.amount, 0); + Ok(()) + }) + .unwrap(); // close native account do_process_instruction( @@ -3210,14 +3309,17 @@ mod tests { ], ) .unwrap(); - let account: &mut Account = state::unpack_unchecked(&mut account2_account.data).unwrap(); - assert!(account.is_native()); - assert_eq!(account_account.lamports, 0); - assert_eq!(account.amount, 0); - assert_eq!( - account3_account.lamports, - 3 * account_minimum_balance() + 2 + 42 - ); + Account::unpack_unchecked_mut(&mut account2_account.data, &mut |account: &mut Account| { + assert!(account.is_native()); + assert_eq!(account_account.lamports, 0); + assert_eq!(account.amount, 0); + assert_eq!( + account3_account.lamports, + 3 * account_minimum_balance() + 2 + 42 + ); + Ok(()) + }) + .unwrap(); } #[test] @@ -3257,9 +3359,12 @@ mod tests { ], ) .unwrap(); - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert!(account.is_native()); - assert_eq!(account.amount, 40); + Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + assert!(account.is_native()); + assert_eq!(account.amount, 40); + Ok(()) + }) + .unwrap(); // initialize native account do_process_instruction( @@ -3278,9 +3383,12 @@ mod tests { ], ) .unwrap(); - let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); - assert!(account.is_native()); - assert_eq!(account.amount, 0); + Account::unpack_unchecked_mut(&mut account2_account.data, &mut |account: &mut Account| { + assert!(account.is_native()); + assert_eq!(account.amount, 0); + Ok(()) + }) + .unwrap(); // mint_to unsupported assert_eq!( @@ -3368,15 +3476,20 @@ mod tests { ], ) .unwrap(); - - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert!(account.is_native()); assert_eq!(account_account.lamports, account_minimum_balance()); - assert_eq!(account.amount, 0); - let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); - assert!(account.is_native()); + Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + assert!(account.is_native()); + assert_eq!(account.amount, 0); + Ok(()) + }) + .unwrap(); assert_eq!(account2_account.lamports, account_minimum_balance() + 40); - assert_eq!(account.amount, 40); + Account::unpack_unchecked_mut(&mut account2_account.data, &mut |account: &mut Account| { + assert!(account.is_native()); + assert_eq!(account.amount, 40); + Ok(()) + }) + .unwrap(); // close native account do_process_instruction( @@ -3388,11 +3501,14 @@ mod tests { ], ) .unwrap(); - let account: &mut Account = state::unpack_unchecked(&mut account_account.data).unwrap(); - assert!(account.is_native()); assert_eq!(account_account.lamports, 0); - assert_eq!(account.amount, 0); assert_eq!(account3_account.lamports, 2 * account_minimum_balance()); + Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + assert!(account.is_native()); + assert_eq!(account.amount, 0); + Ok(()) + }) + .unwrap(); } #[test] @@ -3464,8 +3580,11 @@ mod tests { ], ) .unwrap(); - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(account.amount, u64::MAX); + Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + assert_eq!(account.amount, u64::MAX); + Ok(()) + }) + .unwrap(); // attempt to mint one more to account assert_eq!( @@ -3487,8 +3606,11 @@ mod tests { ], ) ); - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(account.amount, u64::MAX); + Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + assert_eq!(account.amount, u64::MAX); + Ok(()) + }) + .unwrap(); // atttempt to mint one more to the other account assert_eq!( @@ -3517,8 +3639,11 @@ mod tests { vec![&mut account_account, &mut mint_account, &mut owner_account], ) .unwrap(); - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(account.amount, u64::MAX - 100); + Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + assert_eq!(account.amount, u64::MAX - 100); + Ok(()) + }) + .unwrap(); do_process_instruction( mint_to( @@ -3537,12 +3662,18 @@ mod tests { ], ) .unwrap(); - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(account.amount, u64::MAX); + Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + assert_eq!(account.amount, u64::MAX); + Ok(()) + }) + .unwrap(); // manipulate account balance to attempt overflow transfer - let account: &mut Account = state::unpack(&mut account2_account.data).unwrap(); - account.amount = 1; + Account::unpack_unchecked_mut(&mut account2_account.data, &mut |account: &mut Account| { + account.amount = 1; + Ok(()) + }) + .unwrap(); assert_eq!( Err(TokenError::Overflow.into()), @@ -3620,8 +3751,11 @@ mod tests { .unwrap(); // no transfer if either account is frozen - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - account.state = AccountState::Frozen; + Account::unpack_unchecked_mut(&mut account2_account.data, &mut |account: &mut Account| { + account.state = AccountState::Frozen; + Ok(()) + }) + .unwrap(); assert_eq!( Err(TokenError::AccountFrozen.into()), do_process_instruction( @@ -3642,10 +3776,16 @@ mod tests { ) ); - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - account.state = AccountState::Initialized; - let account2: &mut Account = state::unpack(&mut account2_account.data).unwrap(); - account2.state = AccountState::Frozen; + Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + account.state = AccountState::Initialized; + Ok(()) + }) + .unwrap(); + Account::unpack_unchecked_mut(&mut account2_account.data, &mut |account: &mut Account| { + account.state = AccountState::Frozen; + Ok(()) + }) + .unwrap(); assert_eq!( Err(TokenError::AccountFrozen.into()), do_process_instruction( @@ -3667,8 +3807,11 @@ mod tests { ); // no approve if account is frozen - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - account.state = AccountState::Frozen; + Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + account.state = AccountState::Frozen; + Ok(()) + }) + .unwrap(); let delegate_key = pubkey_rand(); let mut delegate_account = SolanaAccount::default(); assert_eq!( @@ -3692,9 +3835,12 @@ mod tests { ); // no revoke if account is frozen - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - account.delegate = COption::Some(delegate_key); - account.delegated_amount = 100; + Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + account.delegate = COption::Some(delegate_key); + account.delegated_amount = 100; + Ok(()) + }) + .unwrap(); assert_eq!( Err(TokenError::AccountFrozen.into()), do_process_instruction( @@ -3793,8 +3939,11 @@ mod tests { ); // missing freeze_authority - let mint: &mut Mint = state::unpack(&mut mint_account.data).unwrap(); - mint.freeze_authority = COption::Some(owner_key); + Mint::unpack_unchecked_mut(&mut mint_account.data, &mut |mint: &mut Mint| { + mint.freeze_authority = COption::Some(owner_key); + Ok(()) + }) + .unwrap(); assert_eq!( Err(TokenError::OwnerMismatch.into()), do_process_instruction( @@ -3818,8 +3967,11 @@ mod tests { vec![&mut account_account, &mut mint_account, &mut owner_account], ) .unwrap(); - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(account.state, AccountState::Frozen); + Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + assert_eq!(account.state, AccountState::Frozen); + Ok(()) + }) + .unwrap(); // check explicit freeze assert_eq!( @@ -3845,7 +3997,10 @@ mod tests { vec![&mut account_account, &mut mint_account, &mut owner_account], ) .unwrap(); - let account: &mut Account = state::unpack(&mut account_account.data).unwrap(); - assert_eq!(account.state, AccountState::Initialized); + Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + assert_eq!(account.state, AccountState::Initialized); + Ok(()) + }) + .unwrap(); } } diff --git a/program/src/state.rs b/program/src/state.rs index 5b3981c..08de37e 100644 --- a/program/src/state.rs +++ b/program/src/state.rs @@ -1,8 +1,18 @@ //! State transition types -use crate::{error::TokenError, instruction::MAX_SIGNERS, option::COption}; +use crate::{ + instruction::MAX_SIGNERS, + option::COption, + pack::{IsInitialized, Pack, Sealed}, +}; +use arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs}; +use num_enum::TryFromPrimitive; use solana_sdk::{program_error::ProgramError, pubkey::Pubkey}; -use std::mem::size_of; + +impl Sealed for Option {} +impl Sealed for Mint {} +impl Sealed for Account {} +impl Sealed for Multisig {} /// Mint data. #[repr(C)] @@ -26,6 +36,52 @@ impl IsInitialized for Mint { self.is_initialized } } +impl Pack for Mint { + const LEN: usize = 82; + fn unpack_from_slice(src: &[u8]) -> Result { + let src = array_ref![src, 0, 82]; + let (mint_authority, supply, decimals, is_initialized, freeze_authority) = + array_refs![src, 36, 8, 1, 1, 36]; + let mint_authority = unpack_coption_key(mint_authority)?; + let supply = u64::from_le_bytes(*supply); + let decimals = decimals[0]; + let is_initialized = match is_initialized { + [0] => false, + [1] => true, + _ => return Err(ProgramError::InvalidAccountData), + }; + let freeze_authority = unpack_coption_key(freeze_authority)?; + Ok(Mint { + mint_authority, + supply, + decimals, + is_initialized, + freeze_authority, + }) + } + fn pack_into_slice(&self, dst: &mut [u8]) { + let dst = array_mut_ref![dst, 0, 82]; + let ( + mint_authority_dst, + supply_dst, + decimals_dst, + is_initialized_dst, + freeze_authority_dst, + ) = mut_array_refs![dst, 36, 8, 1, 1, 36]; + let &Mint { + ref mint_authority, + supply, + decimals, + is_initialized, + ref freeze_authority, + } = self; + pack_coption_key(mint_authority, mint_authority_dst); + *supply_dst = supply.to_le_bytes(); + decimals_dst[0] = decimals; + is_initialized_dst[0] = is_initialized as u8; + pack_coption_key(freeze_authority, freeze_authority_dst); + } +} /// Account data. #[repr(C)] @@ -66,10 +122,60 @@ impl IsInitialized for Account { self.state != AccountState::Uninitialized } } +impl Pack for Account { + const LEN: usize = 165; + fn unpack_from_slice(src: &[u8]) -> Result { + let src = array_ref![src, 0, 165]; + let (mint, owner, amount, delegate, state, is_native, delegated_amount, close_authority) = + array_refs![src, 32, 32, 8, 36, 1, 12, 8, 36]; + Ok(Account { + mint: Pubkey::new_from_array(*mint), + owner: Pubkey::new_from_array(*owner), + amount: u64::from_le_bytes(*amount), + delegate: unpack_coption_key(delegate)?, + state: AccountState::try_from_primitive(state[0]) + .or(Err(ProgramError::InvalidAccountData))?, + is_native: unpack_coption_u64(is_native)?, + delegated_amount: u64::from_le_bytes(*delegated_amount), + close_authority: unpack_coption_key(close_authority)?, + }) + } + fn pack_into_slice(&self, dst: &mut [u8]) { + let dst = array_mut_ref![dst, 0, 165]; + let ( + mint_dst, + owner_dst, + amount_dst, + delegate_dst, + state_dst, + is_native_dst, + delegated_amount_dst, + close_authority_dst, + ) = mut_array_refs![dst, 32, 32, 8, 36, 1, 12, 8, 36]; + let &Account { + ref mint, + ref owner, + amount, + ref delegate, + state, + ref is_native, + delegated_amount, + ref close_authority, + } = self; + mint_dst.copy_from_slice(mint.as_ref()); + owner_dst.copy_from_slice(owner.as_ref()); + *amount_dst = amount.to_le_bytes(); + pack_coption_key(delegate, delegate_dst); + state_dst[0] = state as u8; + pack_coption_u64(is_native, is_native_dst); + *delegated_amount_dst = delegated_amount.to_le_bytes(); + pack_coption_key(close_authority, close_authority_dst); + } +} /// Account state. #[repr(u8)] -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, TryFromPrimitive)] pub enum AccountState { /// Account is not yet initialized Uninitialized, @@ -105,26 +211,79 @@ impl IsInitialized for Multisig { self.is_initialized } } - -/// Check is a token state is initialized -pub trait IsInitialized { - /// Is initialized - fn is_initialized(&self) -> bool; +impl Pack for Multisig { + const LEN: usize = 355; + fn unpack_from_slice(src: &[u8]) -> Result { + let src = array_ref![src, 0, 355]; + #[allow(clippy::ptr_offset_with_cast)] + let (m, n, is_initialized, signers_flat) = array_refs![src, 1, 1, 1, 32 * MAX_SIGNERS]; + let mut result = Multisig { + m: m[0], + n: n[0], + is_initialized: match is_initialized { + [0] => false, + [1] => true, + _ => return Err(ProgramError::InvalidAccountData), + }, + signers: [Pubkey::new_from_array([0u8; 32]); MAX_SIGNERS], + }; + for (src, dst) in signers_flat.chunks(32).zip(result.signers.iter_mut()) { + *dst = Pubkey::new(src); + } + Ok(result) + } + fn pack_into_slice(&self, dst: &mut [u8]) { + let dst = array_mut_ref![dst, 0, 355]; + #[allow(clippy::ptr_offset_with_cast)] + let (m, n, is_initialized, signers_flat) = mut_array_refs![dst, 1, 1, 1, 32 * MAX_SIGNERS]; + *m = [self.m]; + *n = [self.n]; + *is_initialized = [self.is_initialized as u8]; + for (i, src) in self.signers.iter().enumerate() { + let dst_array = array_mut_ref![signers_flat, 32 * i, 32]; + dst_array.copy_from_slice(src.as_ref()); + } + } } -/// Unpacks a token state from a bytes buffer while assuring that the state is initialized. -pub fn unpack(input: &mut [u8]) -> Result<&mut T, ProgramError> { - let mut_ref: &mut T = unpack_unchecked(input)?; - if !mut_ref.is_initialized() { - return Err(TokenError::UninitializedState.into()); +// Helpers +fn pack_coption_key(src: &COption, dst: &mut [u8; 36]) { + let (tag, body) = mut_array_refs![dst, 4, 32]; + match src { + COption::Some(key) => { + *tag = [1, 0, 0, 0]; + body.copy_from_slice(key.as_ref()); + } + COption::None => { + *tag = [0; 4]; + } + } +} +fn unpack_coption_key(src: &[u8; 36]) -> Result, ProgramError> { + let (tag, body) = array_refs![src, 4, 32]; + match *tag { + [0, 0, 0, 0] => Ok(COption::None), + [1, 0, 0, 0] => Ok(COption::Some(Pubkey::new_from_array(*body))), + _ => Err(ProgramError::InvalidAccountData), + } +} +fn pack_coption_u64(src: &COption, dst: &mut [u8; 12]) { + let (tag, body) = mut_array_refs![dst, 4, 8]; + match src { + COption::Some(amount) => { + *tag = [1, 0, 0, 0]; + *body = amount.to_le_bytes(); + } + COption::None => { + *tag = [0; 4]; + } } - Ok(mut_ref) } -/// Unpacks a token state from a bytes buffer without checking that the state is initialized. -pub fn unpack_unchecked(input: &mut [u8]) -> Result<&mut T, ProgramError> { - if input.len() != size_of::() { - return Err(ProgramError::InvalidAccountData); +fn unpack_coption_u64(src: &[u8; 12]) -> Result, ProgramError> { + let (tag, body) = array_refs![src, 4, 8]; + match *tag { + [0, 0, 0, 0] => Ok(COption::None), + [1, 0, 0, 0] => Ok(COption::Some(u64::from_le_bytes(*body))), + _ => Err(ProgramError::InvalidAccountData), } - #[allow(clippy::cast_ptr_alignment)] - Ok(unsafe { &mut *(&mut input[0] as *mut u8 as *mut T) }) } From bfda67e5d6223f1a98b7676446f3d888898212ec Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Thu, 27 Aug 2020 22:28:49 -0700 Subject: [PATCH 038/248] Fix various quotes in documentation --- program/src/instruction.rs | 66 +++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 8e1a045..5052237 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -82,13 +82,13 @@ pub enum TokenInstruction { /// * Single owner/delegate /// 0. `[writable]` The source account. /// 1. `[writable]` The destination account. - /// 2. '[signer]' The source account's owner/delegate. + /// 2. `[signer]` The source account's owner/delegate. /// /// * Multisignature owner/delegate /// 0. `[writable]` The source account. /// 1. `[writable]` The destination account. - /// 2. '[]' The source account's multisignature owner/delegate. - /// 3. ..3+M '[signer]' M signer accounts. + /// 2. `[]` The source account's multisignature owner/delegate. + /// 3. ..3+M `[signer]` M signer accounts. Transfer { /// The amount of tokens to transfer. amount: u64, @@ -106,8 +106,8 @@ pub enum TokenInstruction { /// * Multisignature owner /// 0. `[writable]` The source account. /// 1. `[]` The delegate. - /// 2. '[]' The source account's multisignature owner. - /// 3. ..3+M '[signer]' M signer accounts + /// 2. `[]` The source account's multisignature owner. + /// 3. ..3+M `[signer]` M signer accounts Approve { /// The amount of tokens the delegate is approved for. amount: u64, @@ -122,8 +122,8 @@ pub enum TokenInstruction { /// /// * Multisignature owner /// 0. `[writable]` The source account. - /// 1. '[]' The source account's multisignature owner. - /// 2. ..2+M '[signer]' M signer accounts + /// 1. `[]` The source account's multisignature owner. + /// 2. ..2+M `[signer]` M signer accounts Revoke, /// Sets a new authority of a mint or account. /// @@ -136,7 +136,7 @@ pub enum TokenInstruction { /// * Multisignature authority /// 0. `[writable]` The mint or account to change the authority of. /// 1. `[]` The mint's or account's multisignature authority. - /// 2. ..2+M '[signer]' M signer accounts + /// 2. ..2+M `[signer]` M signer accounts SetAuthority { /// The type of authority to update. authority_type: AuthorityType, @@ -156,7 +156,7 @@ pub enum TokenInstruction { /// 0. `[writable]` The mint. /// 1. `[writable]` The account to mint tokens to. /// 2. `[]` The mint's multisignature mint-tokens authority. - /// 3. ..3+M '[signer]' M signer accounts. + /// 3. ..3+M `[signer]` M signer accounts. MintTo { /// The amount of new tokens to mint. amount: u64, @@ -168,7 +168,7 @@ pub enum TokenInstruction { /// /// * Single owner/delegate /// 0. `[writable]` The account to burn from. - /// 1. '[writable]' The token mint. + /// 1. `[writable]` The token mint. /// 2. `[signer]` The account's owner/delegate. /// /// * Multisignature owner/delegate @@ -187,14 +187,14 @@ pub enum TokenInstruction { /// /// * Single owner /// 0. `[writable]` The account to close. - /// 1. '[writable]' The destination account. + /// 1. `[writable]` The destination account. /// 2. `[signer]` The account's owner. /// /// * Multisignature owner /// 0. `[writable]` The account to close. - /// 1. '[writable]' The destination account. + /// 1. `[writable]` The destination account. /// 2. `[]` The account's multisignature owner. - /// 3. ..3+M '[signer]' M signer accounts. + /// 3. ..3+M `[signer]` M signer accounts. CloseAccount, /// Freeze an Initialized account using the Mint's freeze_authority (if set). /// @@ -202,14 +202,14 @@ pub enum TokenInstruction { /// /// * Single owner /// 0. `[writable]` The account to freeze. - /// 1. '[]' The token mint. + /// 1. `[]` The token mint. /// 2. `[signer]` The mint freeze authority. /// /// * Multisignature owner /// 0. `[writable]` The account to freeze. - /// 1. '[]' The token mint. + /// 1. `[]` The token mint. /// 2. `[]` The mint's multisignature freeze authority. - /// 3. ..3+M '[signer]' M signer accounts. + /// 3. ..3+M `[signer]` M signer accounts. FreezeAccount, /// Thaw a Frozen account using the Mint's freeze_authority (if set). /// @@ -217,14 +217,14 @@ pub enum TokenInstruction { /// /// * Single owner /// 0. `[writable]` The account to freeze. - /// 1. '[]' The token mint. + /// 1. `[]` The token mint. /// 2. `[signer]` The mint freeze authority. /// /// * Multisignature owner /// 0. `[writable]` The account to freeze. - /// 1. '[]' The token mint. + /// 1. `[]` The token mint. /// 2. `[]` The mint's multisignature freeze authority. - /// 3. ..3+M '[signer]' M signer accounts. + /// 3. ..3+M `[signer]` M signer accounts. ThawAccount, /// Transfers tokens from one account to another either directly or via a delegate. If this @@ -239,16 +239,16 @@ pub enum TokenInstruction { /// /// * Single owner/delegate /// 0. `[writable]` The source account. - /// 1. '[]' The token mint. + /// 1. `[]` The token mint. /// 2. `[writable]` The destination account. - /// 3. '[signer]' The source account's owner/delegate. + /// 3. `[signer]` The source account's owner/delegate. /// /// * Multisignature owner/delegate /// 0. `[writable]` The source account. - /// 1. '[]' The token mint. + /// 1. `[]` The token mint. /// 2. `[writable]` The destination account. - /// 3. '[]' The source account's multisignature owner/delegate. - /// 4. ..4+M '[signer]' M signer accounts. + /// 3. `[]` The source account's multisignature owner/delegate. + /// 4. ..4+M `[signer]` M signer accounts. Transfer2 { /// The amount of tokens to transfer. amount: u64, @@ -266,16 +266,16 @@ pub enum TokenInstruction { /// /// * Single owner /// 0. `[writable]` The source account. - /// 1. '[]' The token mint. + /// 1. `[]` The token mint. /// 2. `[]` The delegate. /// 3. `[signer]` The source account owner. /// /// * Multisignature owner /// 0. `[writable]` The source account. - /// 1. '[]' The token mint. + /// 1. `[]` The token mint. /// 2. `[]` The delegate. - /// 3. '[]' The source account's multisignature owner. - /// 4. ..4+M '[signer]' M signer accounts + /// 3. `[]` The source account's multisignature owner. + /// 4. ..4+M `[signer]` M signer accounts Approve2 { /// The amount of tokens the delegate is approved for. amount: u64, @@ -298,7 +298,7 @@ pub enum TokenInstruction { /// 0. `[writable]` The mint. /// 1. `[writable]` The account to mint tokens to. /// 2. `[]` The mint's multisignature mint-tokens authority. - /// 3. ..3+M '[signer]' M signer accounts. + /// 3. ..3+M `[signer]` M signer accounts. MintTo2 { /// The amount of new tokens to mint. amount: u64, @@ -315,14 +315,14 @@ pub enum TokenInstruction { /// /// * Single owner/delegate /// 0. `[writable]` The account to burn from. - /// 1. '[writable]' The token mint. + /// 1. `[writable]` The token mint. /// 2. `[signer]` The account's owner/delegate. /// /// * Multisignature owner/delegate /// 0. `[writable]` The account to burn from. - /// 1. '[writable]' The token mint. + /// 1. `[writable]` The token mint. /// 2. `[]` The account's multisignature owner/delegate. - /// 3. ..3+M '[signer]' M signer accounts. + /// 3. ..3+M `[signer]` M signer accounts. Burn2 { /// The amount of tokens to burn. amount: u64, @@ -570,7 +570,7 @@ impl AuthorityType { } } -/// Creates a 'InitializeMint' instruction. +/// Creates a `InitializeMint` instruction. pub fn initialize_mint( token_program_id: &Pubkey, mint_pubkey: &Pubkey, From affbd2ece2cd847cfdcb6a3827b43caae917a006 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Fri, 28 Aug 2020 00:29:55 -0600 Subject: [PATCH 039/248] Bump spl-token to v2 (#352) --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 2c8c9ca..9a8d17d 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "spl-token" -version = "1.1.0" +version = "2.0.0" description = "Solana Program Library Token" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" From fa3e3841ac3e1b42a8566e70ca8246d46ad5f182 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Thu, 27 Aug 2020 21:58:57 -0700 Subject: [PATCH 040/248] Move c header generation out of build.rs --- program/Cargo.toml | 3 - program/build.rs | 65 ------- program/inc/token.h | 418 +++++++------------------------------------- 3 files changed, 65 insertions(+), 421 deletions(-) delete mode 100644 program/build.rs diff --git a/program/Cargo.toml b/program/Cargo.toml index 9a8d17d..e0b2394 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -29,8 +29,5 @@ num_enum = "0.5.1" [dev-dependencies] rand = { version = "0.7.0"} -[build-dependencies] -cbindgen = "=0.14.2" - [lib] crate-type = ["cdylib", "lib"] diff --git a/program/build.rs b/program/build.rs deleted file mode 100644 index 4862075..0000000 --- a/program/build.rs +++ /dev/null @@ -1,65 +0,0 @@ -extern crate cbindgen; - -use std::env; - -fn main() { - println!("cargo:rerun-if-env-changed=SPL_CBINDGEN"); - println!("cargo:rerun-if-changed=inc/token.h"); - if std::path::Path::new("inc/token.h").exists() && env::var("SPL_CBINDGEN").is_err() { - return; - } - - println!("cargo:warning=Generating inc/token.h"); - let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); - let config = cbindgen::Config { - header: Some("/* Autogenerated SPL Token program C Bindings */".to_string()), - after_includes: Some(format!( - "{}{}{}", - format!( - "\n#define TOKEN_MAJOR_VERSION {}", - env!("CARGO_PKG_VERSION_MAJOR") - ), - format!( - "\n#define TOKEN_MINOR_VERSION {}", - env!("CARGO_PKG_VERSION_MINOR") - ), - format!( - "\n#define TOKEN_PATCH_VERSION {}", - env!("CARGO_PKG_VERSION_PATCH") - ) - )), - language: cbindgen::Language::C, - line_length: 80, - style: cbindgen::Style::Both, - tab_width: 4, - cpp_compat: true, - pragma_once: true, - enumeration: cbindgen::EnumConfig { - prefix_with_name: true, - ..cbindgen::EnumConfig::default() - }, - export: cbindgen::ExportConfig { - prefix: Some("Token_".to_string()), - include: vec![ - "TokenInstruction".to_string(), - "Mint".to_string(), - "Account".to_string(), - "Multisig".to_string(), - ], - exclude: vec!["DECIMALS".to_string()], - ..cbindgen::ExportConfig::default() - }, - parse: cbindgen::ParseConfig { - parse_deps: true, - include: Some(vec!["solana-sdk".to_string()]), - ..cbindgen::ParseConfig::default() - }, - ..cbindgen::Config::default() - }; - cbindgen::Builder::new() - .with_crate(crate_dir) - .with_config(config) - .generate() - .unwrap() - .write_to_file("inc/token.h"); -} diff --git a/program/inc/token.h b/program/inc/token.h index 32df44b..75f394b 100644 --- a/program/inc/token.h +++ b/program/inc/token.h @@ -7,10 +7,6 @@ #include #include -#define TOKEN_MAJOR_VERSION 1 -#define TOKEN_MINOR_VERSION 1 -#define TOKEN_PATCH_VERSION 0 - /** * Maximum number of multisignature signers (max N) */ @@ -21,89 +17,6 @@ */ #define Token_MIN_SIGNERS 1 -/** - * Account state. - */ -enum Token_AccountState -#ifdef __cplusplus - : uint8_t -#endif // __cplusplus - { - /** - * Account is not yet initialized - */ - Token_AccountState_Uninitialized, - /** - * Account is initialized; the account owner and/or delegate may perform permitted operations - * on this account - */ - Token_AccountState_Initialized, - /** - * Account has been frozen by the mint freeze authority. Neither the account owner nor - * the delegate are able to perform operations on this account. - */ - Token_AccountState_Frozen, -}; -#ifndef __cplusplus -typedef uint8_t Token_AccountState; -#endif // __cplusplus - -/** - * Specifies the authority type for SetAuthority instructions - */ -enum Token_AuthorityType -#ifdef __cplusplus - : uint8_t -#endif // __cplusplus - { - /** - * Authority to mint new tokens - */ - Token_AuthorityType_MintTokens, - /** - * Authority to freeze any account associated with the Mint - */ - Token_AuthorityType_FreezeAccount, - /** - * Holder of a given token account - */ - Token_AuthorityType_AccountHolder, - /** - * Authority to close a token account - */ - Token_AuthorityType_CloseAccount, -}; -#ifndef __cplusplus -typedef uint8_t Token_AuthorityType; -#endif // __cplusplus - -typedef uint8_t Token_Pubkey[32]; - -/** - * A C representation of Rust's `std::option::Option` - */ -typedef enum Token_COption_Pubkey_Tag { - /** - * No value - */ - Token_COption_Pubkey_None_Pubkey, - /** - * Some value `T` - */ - Token_COption_Pubkey_Some_Pubkey, -} Token_COption_Pubkey_Tag; - -typedef struct Token_COption_Pubkey_Token_Some_Body_Pubkey { - Token_Pubkey _0; -} Token_COption_Pubkey_Token_Some_Body_Pubkey; - -typedef struct Token_COption_Pubkey { - Token_COption_Pubkey_Tag tag; - union { - Token_COption_Pubkey_Token_Some_Body_Pubkey some; - }; -} Token_COption_Pubkey; - /** * Instructions supported by the token program. */ @@ -118,15 +31,17 @@ typedef enum Token_TokenInstruction_Tag { * Accounts expected by this instruction: * * 0. `[writable]` The mint to initialize. - * 1. `[]` Rent sysvar + * 1. + * * If supply is non-zero: `[writable]` The account to hold all the newly minted tokens. + * * If supply is zero: `[]` The owner/multisignature of the mint. + * 2. `[]` (optional) The owner/multisignature of the mint if supply is non-zero, if + * present then further minting is supported. * */ Token_TokenInstruction_InitializeMint, /** - * Initializes a new account to hold tokens. If this account is associated with the native - * mint then the token balance of the initialized account will be equal to the amount of SOL - * in the account. If this account is associated with another mint, that mint must be - * initialized before this command can succeed. + * Initializes a new account to hold tokens. If this account is associated with the native mint + * then the token balance of the initialized account will be equal to the amount of SOL in the account. * * The `InitializeAccount` instruction requires no signers and MUST be included within * the same Transaction as the system program's `CreateInstruction` that creates the account @@ -137,7 +52,6 @@ typedef enum Token_TokenInstruction_Tag { * 0. `[writable]` The account to initialize. * 1. `[]` The mint this account will be associated with. * 2. `[]` The new account's owner/multisignature. - * 3. `[]` Rent sysvar */ Token_TokenInstruction_InitializeAccount, /** @@ -154,8 +68,7 @@ typedef enum Token_TokenInstruction_Tag { * Accounts expected by this instruction: * * 0. `[writable]` The multisignature account to initialize. - * 1. `[]` Rent sysvar - * 2. ..2+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. + * 1. ..1+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. */ Token_TokenInstruction_InitializeMultisig, /** @@ -180,7 +93,6 @@ typedef enum Token_TokenInstruction_Tag { /** * Approves a delegate. A delegate is given the authority over * tokens on behalf of the source account's owner. - * * Accounts expected by this instruction: * * * Single owner @@ -211,34 +123,36 @@ typedef enum Token_TokenInstruction_Tag { */ Token_TokenInstruction_Revoke, /** - * Sets a new authority of a mint or account. + * Sets a new owner of a mint or account. * * Accounts expected by this instruction: * - * * Single authority - * 0. `[writable]` The mint or account to change the authority of. - * 1. `[signer]` The current authority of the mint or account. + * * Single owner + * 0. `[writable]` The mint or account to change the owner of. + * 1. `[]` The new owner/delegate/multisignature. + * 2. `[signer]` The owner of the mint or account. * - * * Multisignature authority - * 0. `[writable]` The mint or account to change the authority of. - * 1. `[]` The mint's or account's multisignature authority. - * 2. ..2+M '[signer]' M signer accounts + * * Multisignature owner + * 0. `[writable]` The mint or account to change the owner of. + * 1. `[]` The new owner/delegate/multisignature. + * 2. `[]` The mint's or account's multisignature owner. + * 3. ..3+M '[signer]' M signer accounts */ - Token_TokenInstruction_SetAuthority, + Token_TokenInstruction_SetOwner, /** * Mints new tokens to an account. The native mint does not support minting. * * Accounts expected by this instruction: * - * * Single authority + * * Single owner * 0. `[writable]` The mint. * 1. `[writable]` The account to mint tokens to. - * 2. `[signer]` The mint's minting authority. + * 2. `[signer]` The mint's owner. * - * * Multisignature authority + * * Multisignature owner * 0. `[writable]` The mint. * 1. `[writable]` The account to mint tokens to. - * 2. `[]` The mint's multisignature mint-tokens authority. + * 2. `[]` The mint's multisignature owner. * 3. ..3+M '[signer]' M signer accounts. */ Token_TokenInstruction_MintTo, @@ -250,14 +164,12 @@ typedef enum Token_TokenInstruction_Tag { * * * Single owner/delegate * 0. `[writable]` The account to burn from. - * 1. '[writable]' The token mint. - * 2. `[signer]` The account's owner/delegate. + * 1. `[signer]` The account's owner/delegate. * * * Multisignature owner/delegate * 0. `[writable]` The account to burn from. - * 1. '[writable]' The token mint. - * 2. `[]` The account's multisignature owner/delegate. - * 3. ..3+M '[signer]' M signer accounts. + * 1. `[]` The account's multisignature owner/delegate. + * 2. ..2+M '[signer]' M signer accounts. */ Token_TokenInstruction_Burn, /** @@ -278,145 +190,17 @@ typedef enum Token_TokenInstruction_Tag { * 3. ..3+M '[signer]' M signer accounts. */ Token_TokenInstruction_CloseAccount, - /** - * Freeze an Initialized account using the Mint's freeze_authority (if set). - * - * Accounts expected by this instruction: - * - * * Single owner - * 0. `[writable]` The account to freeze. - * 1. '[]' The token mint. - * 2. `[signer]` The mint freeze authority. - * - * * Multisignature owner - * 0. `[writable]` The account to freeze. - * 1. '[]' The token mint. - * 2. `[]` The mint's multisignature freeze authority. - * 3. ..3+M '[signer]' M signer accounts. - */ - Token_TokenInstruction_FreezeAccount, - /** - * Thaw a Frozen account using the Mint's freeze_authority (if set). - * - * Accounts expected by this instruction: - * - * * Single owner - * 0. `[writable]` The account to freeze. - * 1. '[]' The token mint. - * 2. `[signer]` The mint freeze authority. - * - * * Multisignature owner - * 0. `[writable]` The account to freeze. - * 1. '[]' The token mint. - * 2. `[]` The mint's multisignature freeze authority. - * 3. ..3+M '[signer]' M signer accounts. - */ - Token_TokenInstruction_ThawAccount, - /** - * Transfers tokens from one account to another either directly or via a delegate. If this - * account is associated with the native mint then equal amounts of SOL and Tokens will be - * transferred to the destination account. - * - * This instruction differs from Transfer in that the token mint and decimals value is - * asserted by the caller. This may be useful when creating transactions offline or within a - * hardware wallet. - * - * Accounts expected by this instruction: - * - * * Single owner/delegate - * 0. `[writable]` The source account. - * 1. '[]' The token mint. - * 2. `[writable]` The destination account. - * 3. '[signer]' The source account's owner/delegate. - * - * * Multisignature owner/delegate - * 0. `[writable]` The source account. - * 1. '[]' The token mint. - * 2. `[writable]` The destination account. - * 3. '[]' The source account's multisignature owner/delegate. - * 4. ..4+M '[signer]' M signer accounts. - */ - Token_TokenInstruction_Transfer2, - /** - * Approves a delegate. A delegate is given the authority over - * tokens on behalf of the source account's owner. - * - * This instruction differs from Approve in that the token mint and decimals value is asserted - * by the caller. This may be useful when creating transactions offline or within a hardware - * wallet. - * - * Accounts expected by this instruction: - * - * * Single owner - * 0. `[writable]` The source account. - * 1. '[]' The token mint. - * 2. `[]` The delegate. - * 3. `[signer]` The source account owner. - * - * * Multisignature owner - * 0. `[writable]` The source account. - * 1. '[]' The token mint. - * 2. `[]` The delegate. - * 3. '[]' The source account's multisignature owner. - * 4. ..4+M '[signer]' M signer accounts - */ - Token_TokenInstruction_Approve2, - /** - * Mints new tokens to an account. The native mint does not support minting. - * - * This instruction differs from MintTo in that the decimals value is asserted by the - * caller. This may be useful when creating transactions offline or within a hardware wallet. - * - * Accounts expected by this instruction: - * - * * Single authority - * 0. `[writable]` The mint. - * 1. `[writable]` The account to mint tokens to. - * 2. `[signer]` The mint's minting authority. - * - * * Multisignature authority - * 0. `[writable]` The mint. - * 1. `[writable]` The account to mint tokens to. - * 2. `[]` The mint's multisignature mint-tokens authority. - * 3. ..3+M '[signer]' M signer accounts. - */ - Token_TokenInstruction_MintTo2, - /** - * Burns tokens by removing them from an account. `Burn2` does not support accounts - * associated with the native mint, use `CloseAccount` instead. - * - * This instruction differs from Burn in that the decimals value is asserted by the caller. - * This may be useful when creating transactions offline or within a hardware wallet. - * - * Accounts expected by this instruction: - * - * * Single owner/delegate - * 0. `[writable]` The account to burn from. - * 1. '[writable]' The token mint. - * 2. `[signer]` The account's owner/delegate. - * - * * Multisignature owner/delegate - * 0. `[writable]` The account to burn from. - * 1. '[writable]' The token mint. - * 2. `[]` The account's multisignature owner/delegate. - * 3. ..3+M '[signer]' M signer accounts. - */ - Token_TokenInstruction_Burn2, } Token_TokenInstruction_Tag; typedef struct Token_TokenInstruction_Token_InitializeMint_Body { /** - * Number of base 10 digits to the right of the decimal place. - */ - uint8_t decimals; - /** - * The authority/multisignature to mint tokens. + * Initial amount of tokens to mint. */ - Token_Pubkey mint_authority; + uint64_t amount; /** - * The freeze authority/multisignature of the mint. + * Number of base 10 digits to the right of the decimal place. */ - Token_COption_Pubkey freeze_authority; + uint8_t decimals; } Token_TokenInstruction_Token_InitializeMint_Body; typedef struct Token_TokenInstruction_Token_InitializeMultisig_Body { @@ -440,17 +224,6 @@ typedef struct Token_TokenInstruction_Token_Approve_Body { uint64_t amount; } Token_TokenInstruction_Token_Approve_Body; -typedef struct Token_TokenInstruction_Token_SetAuthority_Body { - /** - * The type of authority to update. - */ - Token_AuthorityType authority_type; - /** - * The new authority - */ - Token_COption_Pubkey new_authority; -} Token_TokenInstruction_Token_SetAuthority_Body; - typedef struct Token_TokenInstruction_Token_MintTo_Body { /** * The amount of new tokens to mint. @@ -465,50 +238,6 @@ typedef struct Token_TokenInstruction_Token_Burn_Body { uint64_t amount; } Token_TokenInstruction_Token_Burn_Body; -typedef struct Token_TokenInstruction_Token_Transfer2_Body { - /** - * The amount of tokens to transfer. - */ - uint64_t amount; - /** - * Expected number of base 10 digits to the right of the decimal place. - */ - uint8_t decimals; -} Token_TokenInstruction_Token_Transfer2_Body; - -typedef struct Token_TokenInstruction_Token_Approve2_Body { - /** - * The amount of tokens the delegate is approved for. - */ - uint64_t amount; - /** - * Expected number of base 10 digits to the right of the decimal place. - */ - uint8_t decimals; -} Token_TokenInstruction_Token_Approve2_Body; - -typedef struct Token_TokenInstruction_Token_MintTo2_Body { - /** - * The amount of new tokens to mint. - */ - uint64_t amount; - /** - * Expected number of base 10 digits to the right of the decimal place. - */ - uint8_t decimals; -} Token_TokenInstruction_Token_MintTo2_Body; - -typedef struct Token_TokenInstruction_Token_Burn2_Body { - /** - * The amount of tokens to burn. - */ - uint64_t amount; - /** - * Expected number of base 10 digits to the right of the decimal place. - */ - uint8_t decimals; -} Token_TokenInstruction_Token_Burn2_Body; - typedef struct Token_TokenInstruction { Token_TokenInstruction_Tag tag; union { @@ -516,30 +245,48 @@ typedef struct Token_TokenInstruction { Token_TokenInstruction_Token_InitializeMultisig_Body initialize_multisig; Token_TokenInstruction_Token_Transfer_Body transfer; Token_TokenInstruction_Token_Approve_Body approve; - Token_TokenInstruction_Token_SetAuthority_Body set_authority; Token_TokenInstruction_Token_MintTo_Body mint_to; Token_TokenInstruction_Token_Burn_Body burn; - Token_TokenInstruction_Token_Transfer2_Body transfer2; - Token_TokenInstruction_Token_Approve2_Body approve2; - Token_TokenInstruction_Token_MintTo2_Body mint_to2; - Token_TokenInstruction_Token_Burn2_Body burn2; }; } Token_TokenInstruction; +typedef uint8_t Token_Pubkey[32]; + /** - * Mint data. + * A C representation of Rust's `std::option::Option` */ -typedef struct Token_Mint { +typedef enum Token_COption_Pubkey_Tag { /** - * Optional authority used to mint new tokens. The mint authority may only be provided during - * mint creation. If no mint authority is present then the mint has a fixed supply and no - * further tokens may be minted. + * No value */ - Token_COption_Pubkey mint_authority; + Token_COption_Pubkey_None_Pubkey, /** - * Total supply of tokens. + * Some value `T` */ - uint64_t supply; + Token_COption_Pubkey_Some_Pubkey, +} Token_COption_Pubkey_Tag; + +typedef struct Token_COption_Pubkey_Token_Some_Body_Pubkey { + Token_Pubkey _0; +} Token_COption_Pubkey_Token_Some_Body_Pubkey; + +typedef struct Token_COption_Pubkey { + Token_COption_Pubkey_Tag tag; + union { + Token_COption_Pubkey_Token_Some_Body_Pubkey some; + }; +} Token_COption_Pubkey; + +/** + * Mint data. + */ +typedef struct Token_Mint { + /** + * Optional owner, used to mint new tokens. The owner may only + * be provided during mint creation. If no owner is present then the mint + * has a fixed supply and no further tokens may be minted. + */ + Token_COption_Pubkey owner; /** * Number of base 10 digits to the right of the decimal place. */ @@ -548,37 +295,8 @@ typedef struct Token_Mint { * Is `true` if this structure has been initialized */ bool is_initialized; - /** - * Optional authority to freeze token accounts. - */ - Token_COption_Pubkey freeze_authority; } Token_Mint; -/** - * A C representation of Rust's `std::option::Option` - */ -typedef enum Token_COption_u64_Tag { - /** - * No value - */ - Token_COption_u64_None_u64, - /** - * Some value `T` - */ - Token_COption_u64_Some_u64, -} Token_COption_u64_Tag; - -typedef struct Token_COption_u64_Token_Some_Body_u64 { - uint64_t _0; -} Token_COption_u64_Token_Some_Body_u64; - -typedef struct Token_COption_u64 { - Token_COption_u64_Tag tag; - union { - Token_COption_u64_Token_Some_Body_u64 some; - }; -} Token_COption_u64; - /** * Account data. */ @@ -601,23 +319,17 @@ typedef struct Token_Account { */ Token_COption_Pubkey delegate; /** - * The account's state + * Is `true` if this structure has been initialized */ - Token_AccountState state; + bool is_initialized; /** - * If is_some, this is a native token, and the value logs the rent-exempt reserve. An Account - * is required to be rent-exempt, so the value is used by the Processor to ensure that wrapped - * SOL accounts do not drop below this threshold. + * Is this a native token */ - Token_COption_u64 is_native; + bool is_native; /** * The amount delegated */ uint64_t delegated_amount; - /** - * Optional authority to close the account. - */ - Token_COption_Pubkey close_authority; } Token_Account; /** From 4d469e875946770bcc237c45fec9a284263d7f03 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Thu, 27 Aug 2020 22:48:31 -0700 Subject: [PATCH 041/248] Update token.h --- program/inc/token.h | 436 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 360 insertions(+), 76 deletions(-) diff --git a/program/inc/token.h b/program/inc/token.h index 75f394b..3a156fa 100644 --- a/program/inc/token.h +++ b/program/inc/token.h @@ -17,6 +17,89 @@ */ #define Token_MIN_SIGNERS 1 +/** + * Account state. + */ +enum Token_AccountState +#ifdef __cplusplus + : uint8_t +#endif // __cplusplus + { + /** + * Account is not yet initialized + */ + Token_AccountState_Uninitialized, + /** + * Account is initialized; the account owner and/or delegate may perform permitted operations + * on this account + */ + Token_AccountState_Initialized, + /** + * Account has been frozen by the mint freeze authority. Neither the account owner nor + * the delegate are able to perform operations on this account. + */ + Token_AccountState_Frozen, +}; +#ifndef __cplusplus +typedef uint8_t Token_AccountState; +#endif // __cplusplus + +/** + * Specifies the authority type for SetAuthority instructions + */ +enum Token_AuthorityType +#ifdef __cplusplus + : uint8_t +#endif // __cplusplus + { + /** + * Authority to mint new tokens + */ + Token_AuthorityType_MintTokens, + /** + * Authority to freeze any account associated with the Mint + */ + Token_AuthorityType_FreezeAccount, + /** + * Holder of a given token account + */ + Token_AuthorityType_AccountHolder, + /** + * Authority to close a token account + */ + Token_AuthorityType_CloseAccount, +}; +#ifndef __cplusplus +typedef uint8_t Token_AuthorityType; +#endif // __cplusplus + +typedef uint8_t Token_Pubkey[32]; + +/** + * A C representation of Rust's `std::option::Option` + */ +typedef enum Token_COption_Pubkey_Tag { + /** + * No value + */ + Token_COption_Pubkey_None_Pubkey, + /** + * Some value `T` + */ + Token_COption_Pubkey_Some_Pubkey, +} Token_COption_Pubkey_Tag; + +typedef struct Token_COption_Pubkey_Token_Some_Body_Pubkey { + Token_Pubkey _0; +} Token_COption_Pubkey_Token_Some_Body_Pubkey; + +typedef struct Token_COption_Pubkey { + Token_COption_Pubkey_Tag tag; + union { + Token_COption_Pubkey_Token_Some_Body_Pubkey some; + }; +} Token_COption_Pubkey; + /** * Instructions supported by the token program. */ @@ -31,17 +114,15 @@ typedef enum Token_TokenInstruction_Tag { * Accounts expected by this instruction: * * 0. `[writable]` The mint to initialize. - * 1. - * * If supply is non-zero: `[writable]` The account to hold all the newly minted tokens. - * * If supply is zero: `[]` The owner/multisignature of the mint. - * 2. `[]` (optional) The owner/multisignature of the mint if supply is non-zero, if - * present then further minting is supported. + * 1. `[]` Rent sysvar * */ Token_TokenInstruction_InitializeMint, /** - * Initializes a new account to hold tokens. If this account is associated with the native mint - * then the token balance of the initialized account will be equal to the amount of SOL in the account. + * Initializes a new account to hold tokens. If this account is associated with the native + * mint then the token balance of the initialized account will be equal to the amount of SOL + * in the account. If this account is associated with another mint, that mint must be + * initialized before this command can succeed. * * The `InitializeAccount` instruction requires no signers and MUST be included within * the same Transaction as the system program's `CreateInstruction` that creates the account @@ -52,6 +133,7 @@ typedef enum Token_TokenInstruction_Tag { * 0. `[writable]` The account to initialize. * 1. `[]` The mint this account will be associated with. * 2. `[]` The new account's owner/multisignature. + * 3. `[]` Rent sysvar */ Token_TokenInstruction_InitializeAccount, /** @@ -68,7 +150,8 @@ typedef enum Token_TokenInstruction_Tag { * Accounts expected by this instruction: * * 0. `[writable]` The multisignature account to initialize. - * 1. ..1+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. + * 1. `[]` Rent sysvar + * 2. ..2+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. */ Token_TokenInstruction_InitializeMultisig, /** @@ -81,18 +164,19 @@ typedef enum Token_TokenInstruction_Tag { * * Single owner/delegate * 0. `[writable]` The source account. * 1. `[writable]` The destination account. - * 2. '[signer]' The source account's owner/delegate. + * 2. `[signer]` The source account's owner/delegate. * * * Multisignature owner/delegate * 0. `[writable]` The source account. * 1. `[writable]` The destination account. - * 2. '[]' The source account's multisignature owner/delegate. - * 3. ..3+M '[signer]' M signer accounts. + * 2. `[]` The source account's multisignature owner/delegate. + * 3. ..3+M `[signer]` M signer accounts. */ Token_TokenInstruction_Transfer, /** * Approves a delegate. A delegate is given the authority over * tokens on behalf of the source account's owner. + * * Accounts expected by this instruction: * * * Single owner @@ -103,8 +187,8 @@ typedef enum Token_TokenInstruction_Tag { * * Multisignature owner * 0. `[writable]` The source account. * 1. `[]` The delegate. - * 2. '[]' The source account's multisignature owner. - * 3. ..3+M '[signer]' M signer accounts + * 2. `[]` The source account's multisignature owner. + * 3. ..3+M `[signer]` M signer accounts */ Token_TokenInstruction_Approve, /** @@ -118,42 +202,40 @@ typedef enum Token_TokenInstruction_Tag { * * * Multisignature owner * 0. `[writable]` The source account. - * 1. '[]' The source account's multisignature owner. - * 2. ..2+M '[signer]' M signer accounts + * 1. `[]` The source account's multisignature owner. + * 2. ..2+M `[signer]` M signer accounts */ Token_TokenInstruction_Revoke, /** - * Sets a new owner of a mint or account. + * Sets a new authority of a mint or account. * * Accounts expected by this instruction: * - * * Single owner - * 0. `[writable]` The mint or account to change the owner of. - * 1. `[]` The new owner/delegate/multisignature. - * 2. `[signer]` The owner of the mint or account. + * * Single authority + * 0. `[writable]` The mint or account to change the authority of. + * 1. `[signer]` The current authority of the mint or account. * - * * Multisignature owner - * 0. `[writable]` The mint or account to change the owner of. - * 1. `[]` The new owner/delegate/multisignature. - * 2. `[]` The mint's or account's multisignature owner. - * 3. ..3+M '[signer]' M signer accounts + * * Multisignature authority + * 0. `[writable]` The mint or account to change the authority of. + * 1. `[]` The mint's or account's multisignature authority. + * 2. ..2+M `[signer]` M signer accounts */ - Token_TokenInstruction_SetOwner, + Token_TokenInstruction_SetAuthority, /** * Mints new tokens to an account. The native mint does not support minting. * * Accounts expected by this instruction: * - * * Single owner + * * Single authority * 0. `[writable]` The mint. * 1. `[writable]` The account to mint tokens to. - * 2. `[signer]` The mint's owner. + * 2. `[signer]` The mint's minting authority. * - * * Multisignature owner + * * Multisignature authority * 0. `[writable]` The mint. * 1. `[writable]` The account to mint tokens to. - * 2. `[]` The mint's multisignature owner. - * 3. ..3+M '[signer]' M signer accounts. + * 2. `[]` The mint's multisignature mint-tokens authority. + * 3. ..3+M `[signer]` M signer accounts. */ Token_TokenInstruction_MintTo, /** @@ -164,12 +246,14 @@ typedef enum Token_TokenInstruction_Tag { * * * Single owner/delegate * 0. `[writable]` The account to burn from. - * 1. `[signer]` The account's owner/delegate. + * 1. `[writable]` The token mint. + * 2. `[signer]` The account's owner/delegate. * * * Multisignature owner/delegate * 0. `[writable]` The account to burn from. - * 1. `[]` The account's multisignature owner/delegate. - * 2. ..2+M '[signer]' M signer accounts. + * 1. '[writable]' The token mint. + * 2. `[]` The account's multisignature owner/delegate. + * 3. ..3+M '[signer]' M signer accounts. */ Token_TokenInstruction_Burn, /** @@ -180,27 +264,155 @@ typedef enum Token_TokenInstruction_Tag { * * * Single owner * 0. `[writable]` The account to close. - * 1. '[writable]' The destination account. + * 1. `[writable]` The destination account. * 2. `[signer]` The account's owner. * * * Multisignature owner * 0. `[writable]` The account to close. - * 1. '[writable]' The destination account. + * 1. `[writable]` The destination account. * 2. `[]` The account's multisignature owner. - * 3. ..3+M '[signer]' M signer accounts. + * 3. ..3+M `[signer]` M signer accounts. */ Token_TokenInstruction_CloseAccount, + /** + * Freeze an Initialized account using the Mint's freeze_authority (if set). + * + * Accounts expected by this instruction: + * + * * Single owner + * 0. `[writable]` The account to freeze. + * 1. `[]` The token mint. + * 2. `[signer]` The mint freeze authority. + * + * * Multisignature owner + * 0. `[writable]` The account to freeze. + * 1. `[]` The token mint. + * 2. `[]` The mint's multisignature freeze authority. + * 3. ..3+M `[signer]` M signer accounts. + */ + Token_TokenInstruction_FreezeAccount, + /** + * Thaw a Frozen account using the Mint's freeze_authority (if set). + * + * Accounts expected by this instruction: + * + * * Single owner + * 0. `[writable]` The account to freeze. + * 1. `[]` The token mint. + * 2. `[signer]` The mint freeze authority. + * + * * Multisignature owner + * 0. `[writable]` The account to freeze. + * 1. `[]` The token mint. + * 2. `[]` The mint's multisignature freeze authority. + * 3. ..3+M `[signer]` M signer accounts. + */ + Token_TokenInstruction_ThawAccount, + /** + * Transfers tokens from one account to another either directly or via a delegate. If this + * account is associated with the native mint then equal amounts of SOL and Tokens will be + * transferred to the destination account. + * + * This instruction differs from Transfer in that the token mint and decimals value is + * asserted by the caller. This may be useful when creating transactions offline or within a + * hardware wallet. + * + * Accounts expected by this instruction: + * + * * Single owner/delegate + * 0. `[writable]` The source account. + * 1. `[]` The token mint. + * 2. `[writable]` The destination account. + * 3. `[signer]` The source account's owner/delegate. + * + * * Multisignature owner/delegate + * 0. `[writable]` The source account. + * 1. `[]` The token mint. + * 2. `[writable]` The destination account. + * 3. `[]` The source account's multisignature owner/delegate. + * 4. ..4+M `[signer]` M signer accounts. + */ + Token_TokenInstruction_Transfer2, + /** + * Approves a delegate. A delegate is given the authority over + * tokens on behalf of the source account's owner. + * + * This instruction differs from Approve in that the token mint and decimals value is asserted + * by the caller. This may be useful when creating transactions offline or within a hardware + * wallet. + * + * Accounts expected by this instruction: + * + * * Single owner + * 0. `[writable]` The source account. + * 1. `[]` The token mint. + * 2. `[]` The delegate. + * 3. `[signer]` The source account owner. + * + * * Multisignature owner + * 0. `[writable]` The source account. + * 1. `[]` The token mint. + * 2. `[]` The delegate. + * 3. `[]` The source account's multisignature owner. + * 4. ..4+M `[signer]` M signer accounts + */ + Token_TokenInstruction_Approve2, + /** + * Mints new tokens to an account. The native mint does not support minting. + * + * This instruction differs from MintTo in that the decimals value is asserted by the + * caller. This may be useful when creating transactions offline or within a hardware wallet. + * + * Accounts expected by this instruction: + * + * * Single authority + * 0. `[writable]` The mint. + * 1. `[writable]` The account to mint tokens to. + * 2. `[signer]` The mint's minting authority. + * + * * Multisignature authority + * 0. `[writable]` The mint. + * 1. `[writable]` The account to mint tokens to. + * 2. `[]` The mint's multisignature mint-tokens authority. + * 3. ..3+M `[signer]` M signer accounts. + */ + Token_TokenInstruction_MintTo2, + /** + * Burns tokens by removing them from an account. `Burn2` does not support accounts + * associated with the native mint, use `CloseAccount` instead. + * + * This instruction differs from Burn in that the decimals value is asserted by the caller. + * This may be useful when creating transactions offline or within a hardware wallet. + * + * Accounts expected by this instruction: + * + * * Single owner/delegate + * 0. `[writable]` The account to burn from. + * 1. `[writable]` The token mint. + * 2. `[signer]` The account's owner/delegate. + * + * * Multisignature owner/delegate + * 0. `[writable]` The account to burn from. + * 1. `[writable]` The token mint. + * 2. `[]` The account's multisignature owner/delegate. + * 3. ..3+M `[signer]` M signer accounts. + */ + Token_TokenInstruction_Burn2, } Token_TokenInstruction_Tag; typedef struct Token_TokenInstruction_Token_InitializeMint_Body { - /** - * Initial amount of tokens to mint. - */ - uint64_t amount; /** * Number of base 10 digits to the right of the decimal place. */ uint8_t decimals; + /** + * The authority/multisignature to mint tokens. + */ + Token_Pubkey mint_authority; + /** + * The freeze authority/multisignature of the mint. + */ + Token_COption_Pubkey freeze_authority; } Token_TokenInstruction_Token_InitializeMint_Body; typedef struct Token_TokenInstruction_Token_InitializeMultisig_Body { @@ -224,6 +436,17 @@ typedef struct Token_TokenInstruction_Token_Approve_Body { uint64_t amount; } Token_TokenInstruction_Token_Approve_Body; +typedef struct Token_TokenInstruction_Token_SetAuthority_Body { + /** + * The type of authority to update. + */ + Token_AuthorityType authority_type; + /** + * The new authority + */ + Token_COption_Pubkey new_authority; +} Token_TokenInstruction_Token_SetAuthority_Body; + typedef struct Token_TokenInstruction_Token_MintTo_Body { /** * The amount of new tokens to mint. @@ -238,6 +461,50 @@ typedef struct Token_TokenInstruction_Token_Burn_Body { uint64_t amount; } Token_TokenInstruction_Token_Burn_Body; +typedef struct Token_TokenInstruction_Token_Transfer2_Body { + /** + * The amount of tokens to transfer. + */ + uint64_t amount; + /** + * Expected number of base 10 digits to the right of the decimal place. + */ + uint8_t decimals; +} Token_TokenInstruction_Token_Transfer2_Body; + +typedef struct Token_TokenInstruction_Token_Approve2_Body { + /** + * The amount of tokens the delegate is approved for. + */ + uint64_t amount; + /** + * Expected number of base 10 digits to the right of the decimal place. + */ + uint8_t decimals; +} Token_TokenInstruction_Token_Approve2_Body; + +typedef struct Token_TokenInstruction_Token_MintTo2_Body { + /** + * The amount of new tokens to mint. + */ + uint64_t amount; + /** + * Expected number of base 10 digits to the right of the decimal place. + */ + uint8_t decimals; +} Token_TokenInstruction_Token_MintTo2_Body; + +typedef struct Token_TokenInstruction_Token_Burn2_Body { + /** + * The amount of tokens to burn. + */ + uint64_t amount; + /** + * Expected number of base 10 digits to the right of the decimal place. + */ + uint8_t decimals; +} Token_TokenInstruction_Token_Burn2_Body; + typedef struct Token_TokenInstruction { Token_TokenInstruction_Tag tag; union { @@ -245,48 +512,30 @@ typedef struct Token_TokenInstruction { Token_TokenInstruction_Token_InitializeMultisig_Body initialize_multisig; Token_TokenInstruction_Token_Transfer_Body transfer; Token_TokenInstruction_Token_Approve_Body approve; + Token_TokenInstruction_Token_SetAuthority_Body set_authority; Token_TokenInstruction_Token_MintTo_Body mint_to; Token_TokenInstruction_Token_Burn_Body burn; + Token_TokenInstruction_Token_Transfer2_Body transfer2; + Token_TokenInstruction_Token_Approve2_Body approve2; + Token_TokenInstruction_Token_MintTo2_Body mint_to2; + Token_TokenInstruction_Token_Burn2_Body burn2; }; } Token_TokenInstruction; -typedef uint8_t Token_Pubkey[32]; - -/** - * A C representation of Rust's `std::option::Option` - */ -typedef enum Token_COption_Pubkey_Tag { - /** - * No value - */ - Token_COption_Pubkey_None_Pubkey, - /** - * Some value `T` - */ - Token_COption_Pubkey_Some_Pubkey, -} Token_COption_Pubkey_Tag; - -typedef struct Token_COption_Pubkey_Token_Some_Body_Pubkey { - Token_Pubkey _0; -} Token_COption_Pubkey_Token_Some_Body_Pubkey; - -typedef struct Token_COption_Pubkey { - Token_COption_Pubkey_Tag tag; - union { - Token_COption_Pubkey_Token_Some_Body_Pubkey some; - }; -} Token_COption_Pubkey; - /** * Mint data. */ typedef struct Token_Mint { /** - * Optional owner, used to mint new tokens. The owner may only - * be provided during mint creation. If no owner is present then the mint - * has a fixed supply and no further tokens may be minted. + * Optional authority used to mint new tokens. The mint authority may only be provided during + * mint creation. If no mint authority is present then the mint has a fixed supply and no + * further tokens may be minted. + */ + Token_COption_Pubkey mint_authority; + /** + * Total supply of tokens. */ - Token_COption_Pubkey owner; + uint64_t supply; /** * Number of base 10 digits to the right of the decimal place. */ @@ -295,8 +544,37 @@ typedef struct Token_Mint { * Is `true` if this structure has been initialized */ bool is_initialized; + /** + * Optional authority to freeze token accounts. + */ + Token_COption_Pubkey freeze_authority; } Token_Mint; +/** + * A C representation of Rust's `std::option::Option` + */ +typedef enum Token_COption_u64_Tag { + /** + * No value + */ + Token_COption_u64_None_u64, + /** + * Some value `T` + */ + Token_COption_u64_Some_u64, +} Token_COption_u64_Tag; + +typedef struct Token_COption_u64_Token_Some_Body_u64 { + uint64_t _0; +} Token_COption_u64_Token_Some_Body_u64; + +typedef struct Token_COption_u64 { + Token_COption_u64_Tag tag; + union { + Token_COption_u64_Token_Some_Body_u64 some; + }; +} Token_COption_u64; + /** * Account data. */ @@ -319,17 +597,23 @@ typedef struct Token_Account { */ Token_COption_Pubkey delegate; /** - * Is `true` if this structure has been initialized + * The account's state */ - bool is_initialized; + Token_AccountState state; /** - * Is this a native token + * If is_some, this is a native token, and the value logs the rent-exempt reserve. An Account + * is required to be rent-exempt, so the value is used by the Processor to ensure that wrapped + * SOL accounts do not drop below this threshold. */ - bool is_native; + Token_COption_u64 is_native; /** * The amount delegated */ uint64_t delegated_amount; + /** + * Optional authority to close the account. + */ + Token_COption_Pubkey close_authority; } Token_Account; /** From 410627261e6aced2adb17aa0d144df80b34649ba Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Fri, 28 Aug 2020 10:12:05 -0600 Subject: [PATCH 042/248] Token nits (#353) --- program/src/error.rs | 2 +- program/src/instruction.rs | 28 ++++++++++++++-------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/program/src/error.rs b/program/src/error.rs index a69e57e..32cd9d2 100644 --- a/program/src/error.rs +++ b/program/src/error.rs @@ -26,7 +26,7 @@ pub enum TokenError { #[error("Fixed supply")] FixedSupply, /// The account cannot be initialized because it is already being used. - #[error("AlreadyInUse")] + #[error("Already in use")] AlreadyInUse, /// Invalid number of provided signers. #[error("Invalid number of provided signers")] diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 5052237..4daeee4 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -669,7 +669,7 @@ pub fn transfer( signer_pubkeys.is_empty(), )); for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); + accounts.push(AccountMeta::new_readonly(**signer_pubkey, true)); } Ok(Instruction { @@ -698,7 +698,7 @@ pub fn approve( signer_pubkeys.is_empty(), )); for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); + accounts.push(AccountMeta::new_readonly(**signer_pubkey, true)); } Ok(Instruction { @@ -718,13 +718,13 @@ pub fn revoke( let data = TokenInstruction::Revoke.pack(); let mut accounts = Vec::with_capacity(2 + signer_pubkeys.len()); - accounts.push(AccountMeta::new_readonly(*source_pubkey, false)); + accounts.push(AccountMeta::new(*source_pubkey, false)); accounts.push(AccountMeta::new_readonly( *owner_pubkey, signer_pubkeys.is_empty(), )); for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); + accounts.push(AccountMeta::new_readonly(**signer_pubkey, true)); } Ok(Instruction { @@ -757,7 +757,7 @@ pub fn set_authority( signer_pubkeys.is_empty(), )); for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); + accounts.push(AccountMeta::new_readonly(**signer_pubkey, true)); } Ok(Instruction { @@ -786,7 +786,7 @@ pub fn mint_to( signer_pubkeys.is_empty(), )); for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); + accounts.push(AccountMeta::new_readonly(**signer_pubkey, true)); } Ok(Instruction { @@ -815,7 +815,7 @@ pub fn burn( signer_pubkeys.is_empty(), )); for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); + accounts.push(AccountMeta::new_readonly(**signer_pubkey, true)); } Ok(Instruction { @@ -843,7 +843,7 @@ pub fn close_account( signer_pubkeys.is_empty(), )); for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); + accounts.push(AccountMeta::new_readonly(**signer_pubkey, true)); } Ok(Instruction { @@ -871,7 +871,7 @@ pub fn freeze_account( signer_pubkeys.is_empty(), )); for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); + accounts.push(AccountMeta::new_readonly(**signer_pubkey, true)); } Ok(Instruction { @@ -899,7 +899,7 @@ pub fn thaw_account( signer_pubkeys.is_empty(), )); for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); + accounts.push(AccountMeta::new_readonly(**signer_pubkey, true)); } Ok(Instruction { @@ -932,7 +932,7 @@ pub fn transfer2( signer_pubkeys.is_empty(), )); for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); + accounts.push(AccountMeta::new_readonly(**signer_pubkey, true)); } Ok(Instruction { @@ -965,7 +965,7 @@ pub fn approve2( signer_pubkeys.is_empty(), )); for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); + accounts.push(AccountMeta::new_readonly(**signer_pubkey, true)); } Ok(Instruction { @@ -995,7 +995,7 @@ pub fn mint_to2( signer_pubkeys.is_empty(), )); for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); + accounts.push(AccountMeta::new_readonly(**signer_pubkey, true)); } Ok(Instruction { @@ -1025,7 +1025,7 @@ pub fn burn2( signer_pubkeys.is_empty(), )); for signer_pubkey in signer_pubkeys.iter() { - accounts.push(AccountMeta::new(**signer_pubkey, true)); + accounts.push(AccountMeta::new_readonly(**signer_pubkey, true)); } Ok(Instruction { From 24d1e22b9d2f965d6c0c6a7510896ef0f04bde5f Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Fri, 28 Aug 2020 12:11:57 -0600 Subject: [PATCH 043/248] s/AccountHolder/AccountOwner (#356) --- program/inc/token.h | 4 ++-- program/src/instruction.rs | 8 ++++---- program/src/processor.rs | 16 ++++++++-------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/program/inc/token.h b/program/inc/token.h index 3a156fa..954d841 100644 --- a/program/inc/token.h +++ b/program/inc/token.h @@ -61,9 +61,9 @@ enum Token_AuthorityType */ Token_AuthorityType_FreezeAccount, /** - * Holder of a given token account + * Owner of a given token account */ - Token_AuthorityType_AccountHolder, + Token_AuthorityType_AccountOwner, /** * Authority to close a token account */ diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 4daeee4..d51c8dd 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -543,8 +543,8 @@ pub enum AuthorityType { MintTokens, /// Authority to freeze any account associated with the Mint FreezeAccount, - /// Holder of a given token account - AccountHolder, + /// Owner of a given token account + AccountOwner, /// Authority to close a token account CloseAccount, } @@ -554,7 +554,7 @@ impl AuthorityType { match self { AuthorityType::MintTokens => 0, AuthorityType::FreezeAccount => 1, - AuthorityType::AccountHolder => 2, + AuthorityType::AccountOwner => 2, AuthorityType::CloseAccount => 3, } } @@ -563,7 +563,7 @@ impl AuthorityType { match index { 0 => Ok(AuthorityType::MintTokens), 1 => Ok(AuthorityType::FreezeAccount), - 2 => Ok(AuthorityType::AccountHolder), + 2 => Ok(AuthorityType::AccountOwner), 3 => Ok(AuthorityType::CloseAccount), _ => Err(TokenError::InvalidInstruction.into()), } diff --git a/program/src/processor.rs b/program/src/processor.rs index d2f647e..41373e1 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -342,7 +342,7 @@ impl Processor { } match authority_type { - AuthorityType::AccountHolder => { + AuthorityType::AccountOwner => { Self::validate_owner( program_id, &account.owner, @@ -1795,7 +1795,7 @@ mod tests { &program_id, &account_key, Some(&owner2_key), - AuthorityType::AccountHolder, + AuthorityType::AccountOwner, &owner_key, &[] ) @@ -1836,7 +1836,7 @@ mod tests { &program_id, &account_key, Some(&owner_key), - AuthorityType::AccountHolder, + AuthorityType::AccountOwner, &owner2_key, &[] ) @@ -1850,7 +1850,7 @@ mod tests { &program_id, &account_key, Some(&owner2_key), - AuthorityType::AccountHolder, + AuthorityType::AccountOwner, &owner_key, &[], ) @@ -1886,7 +1886,7 @@ mod tests { &program_id, &account_key, None, - AuthorityType::AccountHolder, + AuthorityType::AccountOwner, &owner_key, &[], ) @@ -1901,7 +1901,7 @@ mod tests { &program_id, &account_key, Some(&owner2_key), - AuthorityType::AccountHolder, + AuthorityType::AccountOwner, &owner_key, &[], ) @@ -2928,7 +2928,7 @@ mod tests { &program_id, &account_key, Some(&owner_key), - AuthorityType::AccountHolder, + AuthorityType::AccountOwner, &multisig_key, &[&signer_keys[0]], ) @@ -3858,7 +3858,7 @@ mod tests { &program_id, &account_key, Some(&new_owner_key), - AuthorityType::AccountHolder, + AuthorityType::AccountOwner, &owner_key, &[] ) From dbd936c1c1cc7319ab17cf66fde24af744546d31 Mon Sep 17 00:00:00 2001 From: Jack May Date: Fri, 28 Aug 2020 11:12:12 -0700 Subject: [PATCH 044/248] Remove last unsafes (#354) --- program/src/lib.rs | 1 + program/src/option.rs | 27 ++------------------------- 2 files changed, 3 insertions(+), 25 deletions(-) diff --git a/program/src/lib.rs b/program/src/lib.rs index cb58c52..2addcd2 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -1,4 +1,5 @@ #![deny(missing_docs)] +#![forbid(unsafe_code)] //! An ERC20-like Token program for the Solana blockchain diff --git a/program/src/option.rs b/program/src/option.rs index 677408b..01ba5df 100644 --- a/program/src/option.rs +++ b/program/src/option.rs @@ -4,9 +4,8 @@ //! This implementation mostly matches `std::option` except iterators since the iteration //! trait requires returning `std::option::Option` -use std::pin::Pin; use std::{ - convert, hint, mem, + convert, mem, ops::{Deref, DerefMut}, }; @@ -151,28 +150,6 @@ impl COption { } } - /// Converts from [`Pin`]`<&COption>` to `COption<`[`Pin`]`<&T>>`. - /// - /// [`Pin`]: ../pin/struct.Pin.html - #[inline] - #[allow(clippy::wrong_self_convention)] - pub fn as_pin_ref(self: Pin<&Self>) -> COption> { - unsafe { Pin::get_ref(self).as_ref().map(|x| Pin::new_unchecked(x)) } - } - - /// Converts from [`Pin`]`<&mut COption>` to `COption<`[`Pin`]`<&mut T>>`. - /// - /// [`Pin`]: ../pin/struct.Pin.html - #[inline] - #[allow(clippy::wrong_self_convention)] - pub fn as_pin_mut(self: Pin<&mut Self>) -> COption> { - unsafe { - Pin::get_unchecked_mut(self) - .as_mut() - .map(|x| Pin::new_unchecked(x)) - } - } - ///////////////////////////////////////////////////////////////////////// // Getting to contained values ///////////////////////////////////////////////////////////////////////// @@ -645,7 +622,7 @@ impl COption { match *self { COption::Some(ref mut v) => v, - COption::None => unsafe { hint::unreachable_unchecked() }, + COption::None => unreachable!(), } } From 0c5983f24fcc98e38b314cc61fa2facfd3bc104d Mon Sep 17 00:00:00 2001 From: Jack May Date: Fri, 28 Aug 2020 12:03:10 -0700 Subject: [PATCH 045/248] Don't repack if not needed (#355) --- program/src/pack.rs | 62 ++++++++++++++++++++----------------- program/src/processor.rs | 66 +++++++++++++++++----------------------- 2 files changed, 62 insertions(+), 66 deletions(-) diff --git a/program/src/pack.rs b/program/src/pack.rs index bf6a205..2650420 100644 --- a/program/src/pack.rs +++ b/program/src/pack.rs @@ -21,6 +21,28 @@ pub trait Pack: Sealed { #[doc(hidden)] fn unpack_from_slice(src: &[u8]) -> Result; + /// Unpack from slice and check if initialized + fn unpack(input: &[u8]) -> Result + where + Self: IsInitialized, + { + let value = Self::unpack_unchecked(input)?; + if value.is_initialized() { + Ok(value) + } else { + Err(TokenError::UninitializedState.into()) + } + } + + /// Unpack from slice without checking if initialized + fn unpack_unchecked(input: &[u8]) -> Result { + if input.len() < Self::LEN { + println!("ilen {:?} tlen {:?}", input.len(), Self::LEN); + return Err(ProgramError::InvalidAccountData); + } + Ok(Self::unpack_from_slice(input)?) + } + /// Borrow `Self` from `input` for the duration of the call to `f`, but first check that `Self` /// is initialized #[inline(never)] @@ -29,9 +51,9 @@ pub trait Pack: Sealed { F: FnMut(&mut Self) -> Result, Self: IsInitialized, { - let mut t = unpack(input)?; + let mut t = Self::unpack(input)?; let u = f(&mut t)?; - pack(t, input)?; + Self::pack(t, input)?; Ok(u) } @@ -42,35 +64,19 @@ pub trait Pack: Sealed { where F: FnMut(&mut Self) -> Result, { - let mut t = unpack_unchecked(input)?; + let mut t = Self::unpack_unchecked(input)?; let u = f(&mut t)?; - pack(t, input)?; + Self::pack(t, input)?; Ok(u) } -} - -fn pack(src: T, dst: &mut [u8]) -> Result<(), ProgramError> { - if dst.len() < T::LEN { - println!("dlen {:?} tlen {:?}", dst.len(), T::LEN); - return Err(ProgramError::InvalidAccountData); - } - src.pack_into_slice(dst); - Ok(()) -} - -fn unpack(input: &[u8]) -> Result { - let value: T = unpack_unchecked(input)?; - if value.is_initialized() { - Ok(value) - } else { - Err(TokenError::UninitializedState.into()) - } -} -fn unpack_unchecked(input: &[u8]) -> Result { - if input.len() < T::LEN { - println!("ilen {:?} tlen {:?}", input.len(), T::LEN); - return Err(ProgramError::InvalidAccountData); + /// Pack into slice + fn pack(src: Self, dst: &mut [u8]) -> Result<(), ProgramError> { + if dst.len() < Self::LEN { + println!("dlen {:?} tlen {:?}", dst.len(), Self::LEN); + return Err(ProgramError::InvalidAccountData); + } + src.pack_into_slice(dst); + Ok(()) } - Ok(T::unpack_from_slice(input)?) } diff --git a/program/src/processor.rs b/program/src/processor.rs index 41373e1..9ab174e 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -75,8 +75,7 @@ impl Processor { } if *mint_info.key != crate::native_mint::id() { - let mut mint_info_data = mint_info.data.borrow_mut(); - Mint::unpack_mut(&mut mint_info_data, &mut |_| Ok(())) + let _ = Mint::unpack(&mint_info.data.borrow_mut()) .map_err(|_| Into::::into(TokenError::InvalidMint))?; } @@ -177,18 +176,15 @@ impl Processor { return Err(TokenError::AccountFrozen.into()); } - if let Some((mint_account_info, expected_decimals)) = expected_mint_info { - if source_account.mint != *mint_account_info.key { + if let Some((mint_info, expected_decimals)) = expected_mint_info { + if source_account.mint != *mint_info.key { return Err(TokenError::MintMismatch.into()); } - let mut mint_info_data = mint_account_info.data.borrow_mut(); - Mint::unpack_mut(&mut mint_info_data, &mut |mint: &mut Mint| { - if expected_decimals != mint.decimals { - return Err(TokenError::MintDecimalsMismatch.into()); - } - Ok(()) - })?; + let mint = Mint::unpack(&mint_info.data.borrow_mut())?; + if expected_decimals != mint.decimals { + return Err(TokenError::MintDecimalsMismatch.into()); + } } match source_account.delegate { @@ -268,18 +264,15 @@ impl Processor { return Err(TokenError::AccountFrozen.into()); } - if let Some((mint_account_info, expected_decimals)) = expected_mint_info { - if source_account.mint != *mint_account_info.key { + if let Some((mint_info, expected_decimals)) = expected_mint_info { + if source_account.mint != *mint_info.key { return Err(TokenError::MintMismatch.into()); } - let mut mint_info_data = mint_account_info.data.borrow_mut(); - Mint::unpack_mut(&mut mint_info_data, &mut |mint: &mut Mint| { - if expected_decimals != mint.decimals { - return Err(TokenError::MintDecimalsMismatch.into()); - } - Ok(()) - })?; + let mint = Mint::unpack(&mint_info.data.borrow_mut())?; + if expected_decimals != mint.decimals { + return Err(TokenError::MintDecimalsMismatch.into()); + } } Self::validate_owner( @@ -442,8 +435,8 @@ impl Processor { return Err(TokenError::MintMismatch.into()); } - let mut mint_info_data = mint_info.data.borrow_mut(); - Mint::unpack_mut(&mut mint_info_data, &mut |mint: &mut Mint| { + let mut mint_data = mint_info.data.borrow_mut(); + Mint::unpack_mut(&mut mint_data, &mut |mint: &mut Mint| { if let Some(expected_decimals) = expected_decimals { if expected_decimals != mint.decimals { return Err(TokenError::MintDecimalsMismatch.into()); @@ -612,19 +605,16 @@ impl Processor { return Err(TokenError::InvalidState.into()); } - let mut mint_data = mint_info.data.borrow_mut(); - Mint::unpack_mut( - &mut mint_data, - &mut |mint: &mut Mint| match mint.freeze_authority { - COption::Some(authority) => Self::validate_owner( - program_id, - &authority, - authority_info, - account_info_iter.as_slice(), - ), - COption::None => Err(TokenError::MintCannotFreeze.into()), - }, - )?; + let mint = Mint::unpack(&mint_info.data.borrow_mut())?; + match mint.freeze_authority { + COption::Some(authority) => Self::validate_owner( + program_id, + &authority, + authority_info, + account_info_iter.as_slice(), + ), + COption::None => Err(TokenError::MintCannotFreeze.into()), + }?; source_account.state = if freeze { AccountState::Frozen @@ -1497,7 +1487,7 @@ mod tests { vec![&mut mint_account, &mut account_account, &mut owner_account], ) .unwrap(); - Mint::unpack_unchecked_mut(&mut mint_account.data, &mut |_| Ok(())).unwrap(); + let _ = Mint::unpack(&mut mint_account.data).unwrap(); Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { assert_eq!(account.amount, 42); Ok(()) @@ -1522,7 +1512,7 @@ mod tests { ) ); - Mint::unpack_unchecked_mut(&mut mint_account.data, &mut |_| Ok(())).unwrap(); + let _ = Mint::unpack(&mut mint_account.data).unwrap(); Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { assert_eq!(account.amount, 42); Ok(()) @@ -1544,7 +1534,7 @@ mod tests { vec![&mut mint_account, &mut account_account, &mut owner_account], ) .unwrap(); - Mint::unpack_unchecked_mut(&mut mint_account.data, &mut |_| Ok(())).unwrap(); + let _ = Mint::unpack(&mut mint_account.data).unwrap(); Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { assert_eq!(account.amount, 84); Ok(()) From 404dc075c023fad4276291c54bfbd6aa3ee920b0 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Fri, 28 Aug 2020 13:34:29 -0600 Subject: [PATCH 046/248] Bump version (#357) --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index e0b2394..45f7856 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "spl-token" -version = "2.0.0" +version = "2.0.1" description = "Solana Program Library Token" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" From ca81b675e0bbf5198cd2ef2aeada68fd42e5d289 Mon Sep 17 00:00:00 2001 From: Jack May Date: Fri, 28 Aug 2020 16:21:57 -0700 Subject: [PATCH 047/248] Fix account size comparisons (#359) --- program/src/pack.rs | 11 +- program/src/processor.rs | 395 +++++++++++++++++++++++++++++---------- program/src/state.rs | 6 +- 3 files changed, 306 insertions(+), 106 deletions(-) diff --git a/program/src/pack.rs b/program/src/pack.rs index 2650420..7a86cb9 100644 --- a/program/src/pack.rs +++ b/program/src/pack.rs @@ -21,6 +21,11 @@ pub trait Pack: Sealed { #[doc(hidden)] fn unpack_from_slice(src: &[u8]) -> Result; + /// Get the packed length + fn get_packed_len() -> usize { + Self::LEN + } + /// Unpack from slice and check if initialized fn unpack(input: &[u8]) -> Result where @@ -36,8 +41,7 @@ pub trait Pack: Sealed { /// Unpack from slice without checking if initialized fn unpack_unchecked(input: &[u8]) -> Result { - if input.len() < Self::LEN { - println!("ilen {:?} tlen {:?}", input.len(), Self::LEN); + if input.len() != Self::LEN { return Err(ProgramError::InvalidAccountData); } Ok(Self::unpack_from_slice(input)?) @@ -72,8 +76,7 @@ pub trait Pack: Sealed { /// Pack into slice fn pack(src: Self, dst: &mut [u8]) -> Result<(), ProgramError> { - if dst.len() < Self::LEN { - println!("dlen {:?} tlen {:?}", dst.len(), Self::LEN); + if dst.len() != Self::LEN { return Err(ProgramError::InvalidAccountData); } src.pack_into_slice(dst); diff --git a/program/src/processor.rs b/program/src/processor.rs index 9ab174e..32d3f7e 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -19,7 +19,6 @@ use solana_sdk::{ pubkey::Pubkey, sysvar::{rent::Rent, Sysvar}, }; -use std::mem::size_of; /// Program state handler. pub struct Processor {} @@ -327,7 +326,7 @@ impl Processor { let account_info = next_account_info(account_info_iter)?; let authority_info = next_account_info(account_info_iter)?; - if account_info.data_len() == size_of::() { + if account_info.data_len() == Account::get_packed_len() { let mut account_data = account_info.data.borrow_mut(); Account::unpack_mut(&mut account_data, &mut |account: &mut Account| { if account.is_frozen() { @@ -365,7 +364,7 @@ impl Processor { } Ok(()) })?; - } else if account_info.data_len() == size_of::() { + } else if account_info.data_len() == Mint::get_packed_len() { let mut mint_data = account_info.data.borrow_mut(); Mint::unpack_mut(&mut mint_data, &mut |mint: &mut Mint| { match authority_type { @@ -716,7 +715,7 @@ impl Processor { return Err(TokenError::OwnerMismatch.into()); } if program_id == owner_account_info.owner - && owner_account_info.data_len() == std::mem::size_of::() + && owner_account_info.data_len() == Multisig::get_packed_len() { let mut owner_data = owner_account_info.data.borrow_mut(); Multisig::unpack_mut(&mut owner_data, &mut |multisig: &mut Multisig| { @@ -825,15 +824,15 @@ mod tests { } fn mint_minimum_balance() -> u64 { - Rent::default().minimum_balance(size_of::()) + Rent::default().minimum_balance(Mint::get_packed_len()) } fn account_minimum_balance() -> u64 { - Rent::default().minimum_balance(size_of::()) + Rent::default().minimum_balance(Account::get_packed_len()) } fn multisig_minimum_balance() -> u64 { - Rent::default().minimum_balance(size_of::()) + Rent::default().minimum_balance(Multisig::get_packed_len()) } #[test] @@ -850,12 +849,117 @@ mod tests { #[test] fn test_unique_account_sizes() { - assert_ne!(size_of::(), 0); - assert_ne!(size_of::(), size_of::()); - assert_ne!(size_of::(), size_of::()); - assert_ne!(size_of::(), 0); - assert_ne!(size_of::(), size_of::()); - assert_ne!(size_of::(), 0); + assert_ne!(Mint::get_packed_len(), 0); + assert_ne!(Mint::get_packed_len(), Account::get_packed_len()); + assert_ne!(Mint::get_packed_len(), Multisig::get_packed_len()); + assert_ne!(Account::get_packed_len(), 0); + assert_ne!(Account::get_packed_len(), Multisig::get_packed_len()); + assert_ne!(Multisig::get_packed_len(), 0); + } + + #[test] + fn test_pack_unpack() { + // Mint + let check = Mint { + mint_authority: COption::Some(Pubkey::new(&[1; 32])), + supply: 42, + decimals: 7, + is_initialized: true, + freeze_authority: COption::Some(Pubkey::new(&[2; 32])), + }; + let mut packed = vec![0; Mint::get_packed_len() + 1]; + assert_eq!( + Err(ProgramError::InvalidAccountData), + Mint::pack(check, &mut packed) + ); + let mut packed = vec![0; Mint::get_packed_len() - 1]; + assert_eq!( + Err(ProgramError::InvalidAccountData), + Mint::pack(check, &mut packed) + ); + let mut packed = vec![0; Mint::get_packed_len()]; + Mint::pack(check, &mut packed).unwrap(); + let expect = vec![ + 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 42, 0, 0, 0, 0, 0, 0, 0, 7, 1, 1, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + ]; + assert_eq!(packed, expect); + let unpacked = Mint::unpack(&packed).unwrap(); + assert_eq!(unpacked, check); + + // Account + let check = Account { + mint: Pubkey::new(&[1; 32]), + owner: Pubkey::new(&[2; 32]), + amount: 3, + delegate: COption::Some(Pubkey::new(&[4; 32])), + state: AccountState::Frozen, + is_native: COption::Some(5), + delegated_amount: 6, + close_authority: COption::Some(Pubkey::new(&[7; 32])), + }; + let mut packed = vec![0; Account::get_packed_len() + 1]; + assert_eq!( + Err(ProgramError::InvalidAccountData), + Account::pack(check, &mut packed) + ); + let mut packed = vec![0; Account::get_packed_len() - 1]; + assert_eq!( + Err(ProgramError::InvalidAccountData), + Account::pack(check, &mut packed) + ); + let mut packed = vec![0; Account::get_packed_len()]; + Account::pack(check, &mut packed).unwrap(); + let expect = vec![ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 3, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 1, 0, 0, 0, 5, 0, 0, + 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + ]; + assert_eq!(packed, expect); + let unpacked = Account::unpack(&packed).unwrap(); + assert_eq!(unpacked, check); + + // Multisig + let check = Multisig { + m: 1, + n: 2, + is_initialized: true, + signers: [Pubkey::new(&[3; 32]); MAX_SIGNERS], + }; + let mut packed = vec![0; Multisig::get_packed_len() + 1]; + assert_eq!( + Err(ProgramError::InvalidAccountData), + Multisig::pack(check, &mut packed) + ); + let mut packed = vec![0; Multisig::get_packed_len() - 1]; + assert_eq!( + Err(ProgramError::InvalidAccountData), + Multisig::pack(check, &mut packed) + ); + let mut packed = vec![0; Multisig::get_packed_len()]; + Multisig::pack(check, &mut packed).unwrap(); + let expect = vec![ + 1, 2, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, + ]; + assert_eq!(packed, expect); + let unpacked = Multisig::unpack(&packed).unwrap(); + assert_eq!(unpacked, check); } #[test] @@ -863,10 +967,10 @@ mod tests { let program_id = pubkey_rand(); let owner_key = pubkey_rand(); let mint_key = pubkey_rand(); - let mut mint_account = SolanaAccount::new(42, size_of::(), &program_id); + let mut mint_account = SolanaAccount::new(42, Mint::get_packed_len(), &program_id); let mint2_key = pubkey_rand(); let mut mint2_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mut rent_sysvar = rent_sysvar(); // mint is not rent exempt @@ -913,12 +1017,12 @@ mod tests { fn test_initialize_mint_account() { let program_id = pubkey_rand(); let account_key = pubkey_rand(); - let mut account_account = SolanaAccount::new(42, size_of::(), &program_id); + let mut account_account = SolanaAccount::new(42, Account::get_packed_len(), &program_id); let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); let mint_key = pubkey_rand(); let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mut rent_sysvar = rent_sysvar(); // account is not rent exempt @@ -989,26 +1093,38 @@ mod tests { fn test_transfer() { let program_id = pubkey_rand(); let account_key = pubkey_rand(); - let mut account_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let account2_key = pubkey_rand(); - let mut account2_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let account3_key = pubkey_rand(); - let mut account3_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account3_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let delegate_key = pubkey_rand(); let mut delegate_account = SolanaAccount::default(); let mismatch_key = pubkey_rand(); - let mut mismatch_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut mismatch_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); let owner2_key = pubkey_rand(); let mut owner2_account = SolanaAccount::default(); let mint_key = pubkey_rand(); let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mint2_key = pubkey_rand(); let mut rent_sysvar = rent_sysvar(); @@ -1438,13 +1554,16 @@ mod tests { fn test_mintable_token_with_zero_supply() { let program_id = pubkey_rand(); let account_key = pubkey_rand(); - let mut account_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); let mint_key = pubkey_rand(); let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mut rent_sysvar = rent_sysvar(); // create mint-able token with zero supply @@ -1546,11 +1665,17 @@ mod tests { fn test_approve() { let program_id = pubkey_rand(); let account_key = pubkey_rand(); - let mut account_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let account2_key = pubkey_rand(); - let mut account2_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let delegate_key = pubkey_rand(); let mut delegate_account = SolanaAccount::default(); let owner_key = pubkey_rand(); @@ -1559,7 +1684,7 @@ mod tests { let mut owner2_account = SolanaAccount::default(); let mint_key = pubkey_rand(); let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mut rent_sysvar = rent_sysvar(); // create mint @@ -1745,11 +1870,17 @@ mod tests { fn test_set_authority() { let program_id = pubkey_rand(); let account_key = pubkey_rand(); - let mut account_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let account2_key = pubkey_rand(); - let mut account2_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); let owner2_key = pubkey_rand(); @@ -1757,10 +1888,10 @@ mod tests { let owner3_key = pubkey_rand(); let mint_key = pubkey_rand(); let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mint2_key = pubkey_rand(); let mut mint2_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mut rent_sysvar = rent_sysvar(); // create new mint with owner @@ -2078,28 +2209,43 @@ mod tests { fn test_mint_to() { let program_id = pubkey_rand(); let account_key = pubkey_rand(); - let mut account_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let account2_key = pubkey_rand(); - let mut account2_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let account3_key = pubkey_rand(); - let mut account3_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account3_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let mismatch_key = pubkey_rand(); - let mut mismatch_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut mismatch_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); let owner2_key = pubkey_rand(); let mut owner2_account = SolanaAccount::default(); let mint_key = pubkey_rand(); let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mint2_key = pubkey_rand(); let uninitialized_key = pubkey_rand(); - let mut uninitialized_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut uninitialized_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let mut rent_sysvar = rent_sysvar(); // create new mint with owner @@ -2280,26 +2426,38 @@ mod tests { fn test_burn() { let program_id = pubkey_rand(); let account_key = pubkey_rand(); - let mut account_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let account2_key = pubkey_rand(); - let mut account2_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let account3_key = pubkey_rand(); - let mut account3_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account3_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let delegate_key = pubkey_rand(); let mut delegate_account = SolanaAccount::default(); let mismatch_key = pubkey_rand(); - let mut mismatch_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut mismatch_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); let owner2_key = pubkey_rand(); let mut owner2_account = SolanaAccount::default(); let mint_key = pubkey_rand(); let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mint2_key = pubkey_rand(); let mut rent_sysvar = rent_sysvar(); @@ -2542,21 +2700,27 @@ mod tests { let program_id = pubkey_rand(); let mint_key = pubkey_rand(); let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let account_key = pubkey_rand(); - let mut account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let account2_key = pubkey_rand(); - let mut account2_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); let multisig_key = pubkey_rand(); - let mut multisig_account = SolanaAccount::new(42, size_of::(), &program_id); + let mut multisig_account = SolanaAccount::new(42, Multisig::get_packed_len(), &program_id); let multisig_delegate_key = pubkey_rand(); let mut multisig_delegate_account = SolanaAccount::new( multisig_minimum_balance(), - size_of::(), + Multisig::get_packed_len(), &program_id, ); let signer_keys = vec![pubkey_rand(); MAX_SIGNERS]; @@ -2826,11 +2990,14 @@ mod tests { // freeze account let account3_key = pubkey_rand(); - let mut account3_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account3_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let mint2_key = pubkey_rand(); let mut mint2_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); do_process_instruction( initialize_mint( &program_id, @@ -2959,7 +3126,7 @@ mod tests { signer.key = key; } let mut lamports = 0; - let mut data = vec![0; size_of::()]; + let mut data = vec![0; Multisig::get_packed_len()]; Multisig::unpack_unchecked_mut(&mut data, &mut |multisig: &mut Multisig| { multisig.m = MAX_SIGNERS as u8; multisig.n = MAX_SIGNERS as u8; @@ -3085,19 +3252,25 @@ mod tests { let program_id = pubkey_rand(); let mint_key = pubkey_rand(); let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let account_key = pubkey_rand(); - let mut account_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let account2_key = pubkey_rand(); let mut account2_account = SolanaAccount::new( account_minimum_balance() + 42, - size_of::(), + Account::get_packed_len(), &program_id, ); let account3_key = pubkey_rand(); - let mut account3_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account3_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); let owner2_key = pubkey_rand(); @@ -3227,11 +3400,17 @@ mod tests { // fund and initialize new non-native account to test close authority let account_key = pubkey_rand(); - let mut account_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let owner2_key = pubkey_rand(); - let mut owner2_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut owner2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), vec![ @@ -3316,16 +3495,19 @@ mod tests { fn test_native_token() { let program_id = pubkey_rand(); let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let account_key = pubkey_rand(); let mut account_account = SolanaAccount::new( account_minimum_balance() + 40, - size_of::(), + Account::get_packed_len(), &program_id, ); let account2_key = pubkey_rand(); - let mut account2_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let account3_key = pubkey_rand(); let mut account3_account = SolanaAccount::new(account_minimum_balance(), 0, &program_id); let owner_key = pubkey_rand(); @@ -3400,7 +3582,7 @@ mod tests { // burn unsupported let bogus_mint_key = pubkey_rand(); let mut bogus_mint_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); do_process_instruction( initialize_mint(&program_id, &bogus_mint_key, &owner_key, None, 2).unwrap(), vec![&mut bogus_mint_account, &mut rent_sysvar], @@ -3505,11 +3687,17 @@ mod tests { fn test_overflow() { let program_id = pubkey_rand(); let account_key = pubkey_rand(); - let mut account_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let account2_key = pubkey_rand(); - let mut account2_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); let owner2_key = pubkey_rand(); @@ -3518,7 +3706,7 @@ mod tests { let mut mint_owner_account = SolanaAccount::default(); let mint_key = pubkey_rand(); let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mut rent_sysvar = rent_sysvar(); // create new mint with owner @@ -3690,16 +3878,22 @@ mod tests { fn test_frozen() { let program_id = pubkey_rand(); let account_key = pubkey_rand(); - let mut account_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let account2_key = pubkey_rand(); - let mut account2_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let owner_key = pubkey_rand(); let mut owner_account = SolanaAccount::default(); let mint_key = pubkey_rand(); let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mut rent_sysvar = rent_sysvar(); // create new mint and fund first account @@ -3880,8 +4074,11 @@ mod tests { fn test_freeze_account() { let program_id = pubkey_rand(); let account_key = pubkey_rand(); - let mut account_account = - SolanaAccount::new(account_minimum_balance(), size_of::(), &program_id); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let account_owner_key = pubkey_rand(); let mut account_owner_account = SolanaAccount::default(); let owner_key = pubkey_rand(); @@ -3890,7 +4087,7 @@ mod tests { let mut owner2_account = SolanaAccount::default(); let mint_key = pubkey_rand(); let mut mint_account = - SolanaAccount::new(mint_minimum_balance(), size_of::(), &program_id); + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mut rent_sysvar = rent_sysvar(); // create new mint with owner different from account owner diff --git a/program/src/state.rs b/program/src/state.rs index 08de37e..5d14af7 100644 --- a/program/src/state.rs +++ b/program/src/state.rs @@ -10,9 +10,6 @@ use num_enum::TryFromPrimitive; use solana_sdk::{program_error::ProgramError, pubkey::Pubkey}; impl Sealed for Option {} -impl Sealed for Mint {} -impl Sealed for Account {} -impl Sealed for Multisig {} /// Mint data. #[repr(C)] @@ -31,6 +28,7 @@ pub struct Mint { /// Optional authority to freeze token accounts. pub freeze_authority: COption, } +impl Sealed for Mint {} impl IsInitialized for Mint { fn is_initialized(&self) -> bool { self.is_initialized @@ -117,6 +115,7 @@ impl Account { self.is_native.is_some() } } +impl Sealed for Account {} impl IsInitialized for Account { fn is_initialized(&self) -> bool { self.state != AccountState::Uninitialized @@ -206,6 +205,7 @@ pub struct Multisig { /// Signer public keys pub signers: [Pubkey; MAX_SIGNERS], } +impl Sealed for Multisig {} impl IsInitialized for Multisig { fn is_initialized(&self) -> bool { self.is_initialized From 90c1e8e1351f2764cdec6861fb91a22075b40349 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Sat, 29 Aug 2020 02:44:03 -0600 Subject: [PATCH 048/248] Bump token program id, version (#361) --- program/Cargo.toml | 2 +- program/program-id.md | 2 +- program/src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 45f7856..7f74922 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "spl-token" -version = "2.0.1" +version = "2.0.3" description = "Solana Program Library Token" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" diff --git a/program/program-id.md b/program/program-id.md index 6f7f354..f397edf 100644 --- a/program/program-id.md +++ b/program/program-id.md @@ -1 +1 @@ -TokenFSDHBLHfbT65SnYJx77ysXc1WTC2W3kvnXnZZR +TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA diff --git a/program/src/lib.rs b/program/src/lib.rs index 2addcd2..52dc77f 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -27,4 +27,4 @@ pub fn amount_to_ui_amount(amount: u64, decimals: u8) -> f64 { amount as f64 / 10_usize.pow(decimals as u32) as f64 } -solana_sdk::declare_id!("TokenFSDHBLHfbT65SnYJx77ysXc1WTC2W3kvnXnZZR"); +solana_sdk::declare_id!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); From 7e6b22edd9cad89ade5992f5b690c309b5665caf Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Sat, 29 Aug 2020 14:42:18 -0700 Subject: [PATCH 049/248] Update to Solana 1.3.6 --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 7f74922..4a72b1b 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -21,7 +21,7 @@ default = ["solana-sdk/default"] num-derive = "0.3" num-traits = "0.2" remove_dir_all = "=0.5.0" -solana-sdk = { version = "1.3.4", default-features = false, optional = true } +solana-sdk = { version = "1.3.6", default-features = false, optional = true } thiserror = "1.0" arrayref = "0.3.6" num_enum = "0.5.1" From 741f39fcf2faa6db7eee7abbb509b8b9aa6853e7 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Sat, 29 Aug 2020 21:59:35 -0700 Subject: [PATCH 050/248] Fix up doc quotes --- program/src/instruction.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index d51c8dd..bd19a9e 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -173,9 +173,9 @@ pub enum TokenInstruction { /// /// * Multisignature owner/delegate /// 0. `[writable]` The account to burn from. - /// 1. '[writable]' The token mint. + /// 1. `[writable]` The token mint. /// 2. `[]` The account's multisignature owner/delegate. - /// 3. ..3+M '[signer]' M signer accounts. + /// 3. ..3+M `[signer]` M signer accounts. Burn { /// The amount of tokens to burn. amount: u64, From 6a2437b25486860b32098063ebf27ef80cf3d3fb Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Thu, 3 Sep 2020 20:17:12 -0600 Subject: [PATCH 051/248] Bump solana crates to 1.3.8 --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 4a72b1b..4c1c6f3 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -21,7 +21,7 @@ default = ["solana-sdk/default"] num-derive = "0.3" num-traits = "0.2" remove_dir_all = "=0.5.0" -solana-sdk = { version = "1.3.6", default-features = false, optional = true } +solana-sdk = { version = "1.3.8", default-features = false, optional = true } thiserror = "1.0" arrayref = "0.3.6" num_enum = "0.5.1" From b8ebe449b5458fb7176aa8daad942aaf15790ba1 Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Thu, 3 Sep 2020 20:21:52 -0600 Subject: [PATCH 052/248] Bump Token version to 2.0.4 --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 4c1c6f3..63176a6 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "spl-token" -version = "2.0.3" +version = "2.0.4" description = "Solana Program Library Token" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" From e416f9c3d92e2c7e27903a8142fdebc1edd9381c Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Thu, 3 Sep 2020 21:09:13 -0600 Subject: [PATCH 053/248] Unrelated clippy... --- program/src/native_mint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/src/native_mint.rs b/program/src/native_mint.rs index 622199c..740b8ab 100644 --- a/program/src/native_mint.rs +++ b/program/src/native_mint.rs @@ -14,7 +14,7 @@ mod tests { #[test] fn test_decimals() { assert!( - lamports_to_sol(42) - crate::amount_to_ui_amount(42, DECIMALS).abs() < f64::EPSILON + (lamports_to_sol(42) - crate::amount_to_ui_amount(42, DECIMALS)).abs() < f64::EPSILON ); assert_eq!( sol_to_lamports(42.), From ead6bc177dfdb0d02c4a2ca8790b60c75506f918 Mon Sep 17 00:00:00 2001 From: Justin Starry Date: Thu, 10 Sep 2020 17:25:43 +0800 Subject: [PATCH 054/248] Bump dependencies from 1.3.8 to 1.3.9 (#415) --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 63176a6..9debf93 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -21,7 +21,7 @@ default = ["solana-sdk/default"] num-derive = "0.3" num-traits = "0.2" remove_dir_all = "=0.5.0" -solana-sdk = { version = "1.3.8", default-features = false, optional = true } +solana-sdk = { version = "1.3.9", default-features = false, optional = true } thiserror = "1.0" arrayref = "0.3.6" num_enum = "0.5.1" From 4ea51df936d5ce0350ae46783f5ebc8103fd893e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 16 Sep 2020 09:22:53 +0000 Subject: [PATCH 055/248] Bump solana-sdk from 1.3.9 to 1.3.11 (#454) Bumps [solana-sdk](https://github.com/solana-labs/solana) from 1.3.9 to 1.3.11. - [Release notes](https://github.com/solana-labs/solana/releases) - [Changelog](https://github.com/solana-labs/solana/blob/master/RELEASE.md) - [Commits](https://github.com/solana-labs/solana/compare/v1.3.9...v1.3.11) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 9debf93..fbba104 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -21,7 +21,7 @@ default = ["solana-sdk/default"] num-derive = "0.3" num-traits = "0.2" remove_dir_all = "=0.5.0" -solana-sdk = { version = "1.3.9", default-features = false, optional = true } +solana-sdk = { version = "1.3.11", default-features = false, optional = true } thiserror = "1.0" arrayref = "0.3.6" num_enum = "0.5.1" From 40d49e72229cca8426ea0d1b43156d1365824316 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Wed, 16 Sep 2020 08:55:03 -0700 Subject: [PATCH 056/248] Nit: add current --- program/src/instruction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index bd19a9e..5c8e1b0 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -135,7 +135,7 @@ pub enum TokenInstruction { /// /// * Multisignature authority /// 0. `[writable]` The mint or account to change the authority of. - /// 1. `[]` The mint's or account's multisignature authority. + /// 1. `[]` The mint's or account's current multisignature authority. /// 2. ..2+M `[signer]` M signer accounts SetAuthority { /// The type of authority to update. From a9402b263b3f55e06a7563bd2c0ccb74ffff12b0 Mon Sep 17 00:00:00 2001 From: Jack May Date: Fri, 18 Sep 2020 15:53:26 -0700 Subject: [PATCH 057/248] Fix multisign check (#479) --- program/src/instruction.rs | 19 +++++++++++-------- program/src/processor.rs | 17 +++++++++-------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 5c8e1b0..2e2d990 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -56,11 +56,14 @@ pub enum TokenInstruction { InitializeAccount, /// Initializes a multisignature account with N provided signers. /// + /// Warning, this instruction is compromised: + /// https://github.com/solana-labs/solana-program-library/issues/477 + /// /// Multisignature accounts can used in place of any single owner/delegate accounts in any /// token instruction that require an owner/delegate to be present. The variant field represents the /// number of signers (M) required to validate this multisignature account. /// - /// The `InitializeMultisig` instruction requires no signers and MUST be included within + /// The `DangerInitializeMultisig` instruction requires no signers and MUST be included within /// the same Transaction as the system program's `CreateInstruction` that creates the account /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. /// @@ -69,7 +72,7 @@ pub enum TokenInstruction { /// 0. `[writable]` The multisignature account to initialize. /// 1. `[]` Rent sysvar /// 2. ..2+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. - InitializeMultisig { + DangerInitializeMultisig { /// The number of signers (M) required to validate this multisignature account. m: u8, }, @@ -350,7 +353,7 @@ impl TokenInstruction { 1 => Self::InitializeAccount, 2 => { let &m = rest.get(0).ok_or(InvalidInstruction)?; - Self::InitializeMultisig { m } + Self::DangerInitializeMultisig { m } } 3 | 4 | 7 | 8 => { let amount = rest @@ -446,7 +449,7 @@ impl TokenInstruction { Self::pack_pubkey_option(freeze_authority, &mut buf); } Self::InitializeAccount => buf.push(1), - &Self::InitializeMultisig { m } => { + &Self::DangerInitializeMultisig { m } => { buf.push(2); buf.push(m); } @@ -621,8 +624,8 @@ pub fn initialize_account( }) } -/// Creates a `InitializeMultisig` instruction. -pub fn initialize_multisig( +/// Creates a `DangerInitializeMultisig` instruction. +pub fn danger_initialize_multisig( token_program_id: &Pubkey, multisig_pubkey: &Pubkey, signer_pubkeys: &[&Pubkey], @@ -634,7 +637,7 @@ pub fn initialize_multisig( { return Err(ProgramError::MissingRequiredSignature); } - let data = TokenInstruction::InitializeMultisig { m }.pack(); + let data = TokenInstruction::DangerInitializeMultisig { m }.pack(); let mut accounts = Vec::with_capacity(1 + 1 + signer_pubkeys.len()); accounts.push(AccountMeta::new(*multisig_pubkey, false)); @@ -1080,7 +1083,7 @@ mod test { let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); - let check = TokenInstruction::InitializeMultisig { m: 1 }; + let check = TokenInstruction::DangerInitializeMultisig { m: 1 }; let packed = check.pack(); let expect = Vec::from([2u8, 1]); assert_eq!(packed, expect); diff --git a/program/src/processor.rs b/program/src/processor.rs index 32d3f7e..3901cf4 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -99,8 +99,8 @@ impl Processor { }) } - /// Processes a [InitializeMultisig](enum.TokenInstruction.html) instruction. - pub fn process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult { + /// Processes a [DangerInitializeMultisig](enum.TokenInstruction.html) instruction. + pub fn process_danger_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let multisig_info = next_account_info(account_info_iter)?; let multisig_info_data_len = multisig_info.data_len(); @@ -642,9 +642,9 @@ impl Processor { info!("Instruction: InitializeAccount"); Self::process_initialize_account(accounts) } - TokenInstruction::InitializeMultisig { m } => { - info!("Instruction: InitializeMultisig"); - Self::process_initialize_multisig(accounts, m) + TokenInstruction::DangerInitializeMultisig { m } => { + info!("Instruction: DangerInitializeMultisig"); + Self::process_danger_initialize_multisig(accounts, m) } TokenInstruction::Transfer { amount } => { info!("Instruction: Transfer"); @@ -2733,7 +2733,8 @@ mod tests { assert_eq!( Err(TokenError::NotRentExempt.into()), do_process_instruction( - initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(), + danger_initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1) + .unwrap(), vec![ &mut multisig_account, &mut rent_sysvar, @@ -2747,7 +2748,7 @@ mod tests { // single signer let account_info_iter = &mut signer_accounts.iter_mut(); do_process_instruction( - initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(), + danger_initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(), vec![ &mut multisig_account, &mut rent_sysvar, @@ -2759,7 +2760,7 @@ mod tests { // multiple signer let account_info_iter = &mut signer_accounts.iter_mut(); do_process_instruction( - initialize_multisig( + danger_initialize_multisig( &program_id, &multisig_delegate_key, &signer_key_refs, From 9a3520723dafa9b95e94952089c9beef5e9c4740 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Fri, 18 Sep 2020 18:37:21 -0600 Subject: [PATCH 058/248] Bump spl-token v2 (#480) --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index fbba104..16e174f 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "spl-token" -version = "2.0.4" +version = "2.0.5" description = "Solana Program Library Token" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" From 4780bc4e14e82a1f0a3bbe941f590133914c9604 Mon Sep 17 00:00:00 2001 From: Jack May Date: Mon, 21 Sep 2020 12:43:32 -0700 Subject: [PATCH 059/248] backport #433 to v2 --- program/src/processor.rs | 337 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 328 insertions(+), 9 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index 3901cf4..38d3cbc 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -161,19 +161,18 @@ impl Processor { return Ok(()); } - let mut source_data = source_account_info.data.borrow_mut(); - let mut dest_data = dest_account_info.data.borrow_mut(); - Account::unpack_mut(&mut source_data, &mut |source_account: &mut Account| { - Account::unpack_mut(&mut dest_data, &mut |dest_account: &mut Account| { + let mut source_account = Account::unpack(&source_account_info.data.borrow())?; + let mut dest_account = Account::unpack(&dest_account_info.data.borrow())?; + if source_account.amount < amount { return Err(TokenError::InsufficientFunds.into()); } - if source_account.mint != dest_account.mint { - return Err(TokenError::MintMismatch.into()); - } if source_account.is_frozen() || dest_account.is_frozen() { return Err(TokenError::AccountFrozen.into()); } + if source_account.mint != dest_account.mint { + return Err(TokenError::MintMismatch.into()); + } if let Some((mint_info, expected_decimals)) = expected_mint_info { if source_account.mint != *mint_info.key { @@ -234,9 +233,10 @@ impl Processor { .ok_or(TokenError::Overflow)?; } + Account::pack(source_account, &mut source_account_info.data.borrow_mut())?; + Account::pack(dest_account, &mut dest_account_info.data.borrow_mut())?; + Ok(()) - }) - }) } /// Processes an [Approve](enum.TokenInstruction.html) instruction. @@ -815,6 +815,13 @@ mod tests { Processor::process(&instruction.program_id, &account_infos, &instruction.data) } + fn do_process_instruction_dups( + instruction: Instruction, + account_infos: Vec, + ) -> ProgramResult { + Processor::process(&instruction.program_id, &account_infos, &instruction.data) + } + fn return_token_error_as_program_error() -> ProgramError { TokenError::MintMismatch.into() } @@ -1089,6 +1096,318 @@ mod tests { ); } + #[test] + fn test_transfer_dups() { + let program_id = pubkey_rand(); + let account1_key = pubkey_rand(); + let mut account1_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let mut account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let mut account2_info: AccountInfo = (&account2_key, false, &mut account2_account).into(); + let account3_key = pubkey_rand(); + let mut account3_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account3_info: AccountInfo = (&account3_key, false, &mut account3_account).into(); + let account4_key = pubkey_rand(); + let mut account4_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account4_info: AccountInfo = (&account4_key, true, &mut account4_account).into(); + let multisig_key = pubkey_rand(); + let mut multisig_account = SolanaAccount::new( + multisig_minimum_balance(), + Multisig::get_packed_len(), + &program_id, + ); + let multisig_info: AccountInfo = (&multisig_key, true, &mut multisig_account).into(); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into(); + let mint_key = pubkey_rand(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mint_info: AccountInfo = (&mint_key, false, &mut mint_account).into(); + let rent_key = rent::id(); + let mut rent_sysvar = rent_sysvar(); + let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); + + // create mint + do_process_instruction_dups( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![mint_info.clone(), rent_info.clone()], + ) + .unwrap(); + + // create account + do_process_instruction_dups( + initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + + // create another account + do_process_instruction_dups( + initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), + vec![ + account2_info.clone(), + mint_info.clone(), + owner_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + + // mint to account + do_process_instruction_dups( + mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(), + vec![mint_info.clone(), account1_info.clone(), owner_info.clone()], + ) + .unwrap(); + + // source-owner transfer + do_process_instruction_dups( + transfer( + &program_id, + &account1_key, + &account2_key, + &account1_key, + &[], + 500, + ) + .unwrap(), + vec![ + account1_info.clone(), + account2_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // source-owner transfer2 + do_process_instruction_dups( + transfer2( + &program_id, + &account1_key, + &mint_key, + &account2_key, + &account1_key, + &[], + 500, + 2, + ) + .unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account2_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // source-delegate transfer + Account::unpack_unchecked_mut( + &mut account1_info.data.borrow_mut(), + &mut |account: &mut Account| { + account.amount = 1000; + account.delegated_amount = 1000; + account.delegate = COption::Some(account1_key); + account.owner = owner_key; + Ok(()) + }, + ) + .unwrap(); + + do_process_instruction_dups( + transfer( + &program_id, + &account1_key, + &account2_key, + &account1_key, + &[], + 500, + ) + .unwrap(), + vec![ + account1_info.clone(), + account2_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // source-delegate transfer2 + do_process_instruction_dups( + transfer2( + &program_id, + &account1_key, + &mint_key, + &account2_key, + &account1_key, + &[], + 500, + 2, + ) + .unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account2_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // test destination-owner transfer + do_process_instruction_dups( + initialize_account(&program_id, &account3_key, &mint_key, &account2_key).unwrap(), + vec![ + account3_info.clone(), + mint_info.clone(), + account2_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + do_process_instruction_dups( + mint_to(&program_id, &mint_key, &account3_key, &owner_key, &[], 1000).unwrap(), + vec![mint_info.clone(), account3_info.clone(), owner_info.clone()], + ) + .unwrap(); + + account1_info.is_signer = false; + account2_info.is_signer = true; + do_process_instruction_dups( + transfer( + &program_id, + &account3_key, + &account2_key, + &account2_key, + &[], + 500, + ) + .unwrap(), + vec![ + account3_info.clone(), + account2_info.clone(), + account2_info.clone(), + ], + ) + .unwrap(); + + // destination-owner transfer2 + do_process_instruction_dups( + transfer2( + &program_id, + &account3_key, + &mint_key, + &account2_key, + &account2_key, + &[], + 500, + 2, + ) + .unwrap(), + vec![ + account3_info.clone(), + mint_info.clone(), + account2_info.clone(), + account2_info.clone(), + ], + ) + .unwrap(); + + // test source-multisig signer + do_process_instruction_dups( + initialize_multisig(&program_id, &multisig_key, &[&account4_key], 1).unwrap(), + vec![ + multisig_info.clone(), + rent_info.clone(), + account4_info.clone(), + ], + ) + .unwrap(); + + do_process_instruction_dups( + initialize_account(&program_id, &account4_key, &mint_key, &multisig_key).unwrap(), + vec![ + account4_info.clone(), + mint_info.clone(), + multisig_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + + do_process_instruction_dups( + mint_to(&program_id, &mint_key, &account4_key, &owner_key, &[], 1000).unwrap(), + vec![mint_info.clone(), account4_info.clone(), owner_info.clone()], + ) + .unwrap(); + + // source-multisig-signer transfer + do_process_instruction_dups( + transfer( + &program_id, + &account4_key, + &account2_key, + &multisig_key, + &[&account4_key], + 500, + ) + .unwrap(), + vec![ + account4_info.clone(), + account2_info.clone(), + multisig_info.clone(), + account4_info.clone(), + ], + ) + .unwrap(); + + // source-multisig-signer transfer2 + do_process_instruction_dups( + transfer2( + &program_id, + &account4_key, + &mint_key, + &account2_key, + &multisig_key, + &[&account4_key], + 500, + 2, + ) + .unwrap(), + vec![ + account4_info.clone(), + mint_info.clone(), + account2_info.clone(), + multisig_info.clone(), + account4_info.clone(), + ], + ) + .unwrap(); + } + #[test] fn test_transfer() { let program_id = pubkey_rand(); From 7c6cfa51eceb48f9408958a7eaa299f08b5f0ca2 Mon Sep 17 00:00:00 2001 From: Jack May Date: Mon, 21 Sep 2020 12:45:24 -0700 Subject: [PATCH 060/248] backport #438 to v2 --- program/src/processor.rs | 172 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 166 insertions(+), 6 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index 38d3cbc..4618abe 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -552,8 +552,7 @@ impl Processor { let dest_account_info = next_account_info(account_info_iter)?; let authority_info = next_account_info(account_info_iter)?; - let mut source_data = source_account_info.data.borrow_mut(); - Account::unpack_mut(&mut source_data, &mut |source_account: &mut Account| { + let mut source_account = Account::unpack(&source_account_info.data.borrow())?; if !source_account.is_native() && source_account.amount != 0 { return Err(TokenError::NonNativeHasBalance.into()); } @@ -576,8 +575,9 @@ impl Processor { **source_account_info.lamports.borrow_mut() = 0; source_account.amount = 0; + Account::pack(source_account, &mut source_account_info.data.borrow_mut())?; + Ok(()) - }) } /// Processes a [FreezeAccount](enum.TokenInstruction.html) or a @@ -592,8 +592,7 @@ impl Processor { let mint_info = next_account_info(account_info_iter)?; let authority_info = next_account_info(account_info_iter)?; - let mut source_data = source_account_info.data.borrow_mut(); - Account::unpack_mut(&mut source_data, &mut |source_account: &mut Account| { + let mut source_account = Account::unpack(&source_account_info.data.borrow())?; if source_account.is_native() { return Err(TokenError::NativeNotSupported.into()); } @@ -621,8 +620,9 @@ impl Processor { AccountState::Initialized }; + Account::pack(source_account, &mut source_account_info.data.borrow_mut())?; + Ok(()) - }) } /// Processes an [Instruction](enum.Instruction.html). @@ -3567,6 +3567,97 @@ mod tests { signers[5].is_signer = true; } + #[test] + fn test_close_account_dups() { + let program_id = pubkey_rand(); + let account1_key = pubkey_rand(); + let mut account1_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account2_info: AccountInfo = (&account2_key, true, &mut account2_account).into(); + let owner_key = pubkey_rand(); + let mint_key = pubkey_rand(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mint_info: AccountInfo = (&mint_key, false, &mut mint_account).into(); + let rent_key = rent::id(); + let mut rent_sysvar = rent_sysvar(); + let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); + + // create mint + do_process_instruction_dups( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![mint_info.clone(), rent_info.clone()], + ) + .unwrap(); + + // create account + do_process_instruction_dups( + initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + + // source-owner close + do_process_instruction_dups( + close_account( + &program_id, + &account1_key, + &account2_key, + &account1_key, + &[], + ) + .unwrap(), + vec![ + account1_info.clone(), + account2_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // source-close-authority close + Account::unpack_unchecked_mut( + &mut account1_info.data.borrow_mut(), + &mut |account: &mut Account| { + account.close_authority = COption::Some(account1_key); + account.owner = owner_key; + Ok(()) + }, + ) + .unwrap(); + do_process_instruction_dups( + close_account( + &program_id, + &account1_key, + &account2_key, + &account1_key, + &[], + ) + .unwrap(), + vec![ + account1_info.clone(), + account2_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + } + #[test] fn test_close_account() { let program_id = pubkey_rand(); @@ -4390,6 +4481,75 @@ mod tests { ); } + #[test] + fn test_freeze_thaw_dups() { + let program_id = pubkey_rand(); + let account1_key = pubkey_rand(); + let mut account1_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); + let owner_key = pubkey_rand(); + let mint_key = pubkey_rand(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mint_info: AccountInfo = (&mint_key, true, &mut mint_account).into(); + let rent_key = rent::id(); + let mut rent_sysvar = rent_sysvar(); + let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); + + // create mint + do_process_instruction_dups( + initialize_mint(&program_id, &mint_key, &owner_key, Some(&account1_key), 2).unwrap(), + vec![mint_info.clone(), rent_info.clone()], + ) + .unwrap(); + + // create account + do_process_instruction_dups( + initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + + // freeze where mint freeze_authority is account + do_process_instruction_dups( + freeze_account(&program_id, &account1_key, &mint_key, &account1_key, &[]).unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // thaw where mint freeze_authority is account + Account::unpack_unchecked_mut( + &mut account1_info.data.borrow_mut(), + &mut |account: &mut Account| { + account.state = AccountState::Frozen; + Ok(()) + }, + ) + .unwrap(); + do_process_instruction_dups( + thaw_account(&program_id, &account1_key, &mint_key, &account1_key, &[]).unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + } + #[test] fn test_freeze_account() { let program_id = pubkey_rand(); From 78f6a0e090ef9a7ff4c210bdc6c1e5e9f26453d7 Mon Sep 17 00:00:00 2001 From: Jack May Date: Mon, 21 Sep 2020 12:46:42 -0700 Subject: [PATCH 061/248] backport #437 to v2 --- program/src/processor.rs | 227 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 221 insertions(+), 6 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index 4618abe..4d7c070 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -480,10 +480,9 @@ impl Processor { let mint_info = next_account_info(account_info_iter)?; let authority_info = next_account_info(account_info_iter)?; - let mut mint_data = mint_info.data.borrow_mut(); - let mut source_data = source_account_info.data.borrow_mut(); - Mint::unpack_mut(&mut mint_data, &mut |mint: &mut Mint| { - Account::unpack_mut(&mut source_data, &mut |source_account: &mut Account| { + let mut source_account = Account::unpack(&source_account_info.data.borrow())?; + let mut mint = Mint::unpack(&mint_info.data.borrow())?; + if source_account.is_native() { return Err(TokenError::NativeNotSupported.into()); } @@ -540,9 +539,10 @@ impl Processor { .checked_sub(amount) .ok_or(TokenError::Overflow)?; + Account::pack(source_account, &mut source_account_info.data.borrow_mut())?; + Mint::pack(mint, &mut mint_info.data.borrow_mut())?; + Ok(()) - }) - }) } /// Processes a [CloseAccount](enum.TokenInstruction.html) instruction. @@ -2741,6 +2741,221 @@ mod tests { ); } + #[test] + fn test_burn_dups() { + let program_id = pubkey_rand(); + let account1_key = pubkey_rand(); + let mut account1_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into(); + let mint_key = pubkey_rand(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mint_info: AccountInfo = (&mint_key, true, &mut mint_account).into(); + let rent_key = rent::id(); + let mut rent_sysvar = rent_sysvar(); + let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); + + // create mint + do_process_instruction_dups( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![mint_info.clone(), rent_info.clone()], + ) + .unwrap(); + + // create account + do_process_instruction_dups( + initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + + // mint to account + do_process_instruction_dups( + mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(), + vec![mint_info.clone(), account1_info.clone(), owner_info.clone()], + ) + .unwrap(); + + // source-owner burn + do_process_instruction_dups( + burn( + &program_id, + &mint_key, + &account1_key, + &account1_key, + &[], + 500, + ) + .unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // source-owner burn2 + do_process_instruction_dups( + burn2( + &program_id, + &account1_key, + &mint_key, + &account1_key, + &[], + 500, + 2, + ) + .unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // mint-owner burn + do_process_instruction_dups( + mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(), + vec![mint_info.clone(), account1_info.clone(), owner_info.clone()], + ) + .unwrap(); + Account::unpack_unchecked_mut( + &mut account1_info.data.borrow_mut(), + &mut |account: &mut Account| { + account.owner = mint_key; + Ok(()) + }, + ) + .unwrap(); + do_process_instruction_dups( + burn(&program_id, &account1_key, &mint_key, &mint_key, &[], 500).unwrap(), + vec![account1_info.clone(), mint_info.clone(), mint_info.clone()], + ) + .unwrap(); + + // mint-owner burn2 + do_process_instruction_dups( + burn2( + &program_id, + &account1_key, + &mint_key, + &mint_key, + &[], + 500, + 2, + ) + .unwrap(), + vec![account1_info.clone(), mint_info.clone(), mint_info.clone()], + ) + .unwrap(); + + // source-delegate burn + do_process_instruction_dups( + mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(), + vec![mint_info.clone(), account1_info.clone(), owner_info.clone()], + ) + .unwrap(); + Account::unpack_unchecked_mut( + &mut account1_info.data.borrow_mut(), + &mut |account: &mut Account| { + account.delegated_amount = 1000; + account.delegate = COption::Some(account1_key); + account.owner = owner_key; + Ok(()) + }, + ) + .unwrap(); + do_process_instruction_dups( + burn( + &program_id, + &account1_key, + &mint_key, + &account1_key, + &[], + 500, + ) + .unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // source-delegate burn2 + do_process_instruction_dups( + burn2( + &program_id, + &account1_key, + &mint_key, + &account1_key, + &[], + 500, + 2, + ) + .unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // mint-delegate burn + do_process_instruction_dups( + mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(), + vec![mint_info.clone(), account1_info.clone(), owner_info.clone()], + ) + .unwrap(); + Account::unpack_unchecked_mut( + &mut account1_info.data.borrow_mut(), + &mut |account: &mut Account| { + account.delegated_amount = 1000; + account.delegate = COption::Some(mint_key); + account.owner = owner_key; + Ok(()) + }, + ) + .unwrap(); + do_process_instruction_dups( + burn(&program_id, &account1_key, &mint_key, &mint_key, &[], 500).unwrap(), + vec![account1_info.clone(), mint_info.clone(), mint_info.clone()], + ) + .unwrap(); + + // mint-delegate burn2 + do_process_instruction_dups( + burn2( + &program_id, + &account1_key, + &mint_key, + &mint_key, + &[], + 500, + 2, + ) + .unwrap(), + vec![account1_info.clone(), mint_info.clone(), mint_info.clone()], + ) + .unwrap(); + } + #[test] fn test_burn() { let program_id = pubkey_rand(); From ae3d4207908f07ff1057fcb375968778692a9ca0 Mon Sep 17 00:00:00 2001 From: Jack May Date: Mon, 21 Sep 2020 12:47:55 -0700 Subject: [PATCH 062/248] backport #436 to v2 --- program/src/processor.rs | 109 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 103 insertions(+), 6 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index 4d7c070..466b644 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -421,8 +421,7 @@ impl Processor { let dest_account_info = next_account_info(account_info_iter)?; let owner_info = next_account_info(account_info_iter)?; - let mut dest_account_data = dest_account_info.data.borrow_mut(); - Account::unpack_mut(&mut dest_account_data, &mut |dest_account: &mut Account| { + let mut dest_account = Account::unpack(&dest_account_info.data.borrow())?; if dest_account.is_frozen() { return Err(TokenError::AccountFrozen.into()); } @@ -434,8 +433,7 @@ impl Processor { return Err(TokenError::MintMismatch.into()); } - let mut mint_data = mint_info.data.borrow_mut(); - Mint::unpack_mut(&mut mint_data, &mut |mint: &mut Mint| { + let mut mint = Mint::unpack(&mint_info.data.borrow())?; if let Some(expected_decimals) = expected_decimals { if expected_decimals != mint.decimals { return Err(TokenError::MintDecimalsMismatch.into()); @@ -462,9 +460,10 @@ impl Processor { .checked_add(amount) .ok_or(TokenError::Overflow)?; + Account::pack(dest_account, &mut dest_account_info.data.borrow_mut())?; + Mint::pack(mint, &mut mint_info.data.borrow_mut())?; + Ok(()) - }) - }) } /// Processes a [Burn](enum.TokenInstruction.html) instruction. @@ -2524,6 +2523,104 @@ mod tests { ); } + #[test] + fn test_mint_to_dups() { + let program_id = pubkey_rand(); + let account1_key = pubkey_rand(); + let mut account1_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into(); + let mint_key = pubkey_rand(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mint_info: AccountInfo = (&mint_key, true, &mut mint_account).into(); + let rent_key = rent::id(); + let mut rent_sysvar = rent_sysvar(); + let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); + + // create mint + do_process_instruction_dups( + initialize_mint(&program_id, &mint_key, &mint_key, None, 2).unwrap(), + vec![mint_info.clone(), rent_info.clone()], + ) + .unwrap(); + + // create account + do_process_instruction_dups( + initialize_account(&program_id, &account1_key, &mint_key, &owner_key).unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + owner_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + + // mint_to when mint_authority is self + do_process_instruction_dups( + mint_to(&program_id, &mint_key, &account1_key, &mint_key, &[], 42).unwrap(), + vec![mint_info.clone(), account1_info.clone(), mint_info.clone()], + ) + .unwrap(); + + // mint_to2 when mint_authority is self + do_process_instruction_dups( + mint_to2(&program_id, &mint_key, &account1_key, &mint_key, &[], 42, 2).unwrap(), + vec![mint_info.clone(), account1_info.clone(), mint_info.clone()], + ) + .unwrap(); + + // mint_to when mint_authority is account owner + Mint::unpack_unchecked_mut(&mut mint_info.data.borrow_mut(), &mut |mint: &mut Mint| { + mint.mint_authority = COption::Some(account1_key); + Ok(()) + }) + .unwrap(); + do_process_instruction_dups( + mint_to( + &program_id, + &mint_key, + &account1_key, + &account1_key, + &[], + 42, + ) + .unwrap(), + vec![ + mint_info.clone(), + account1_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // mint_to2 when mint_authority is account owner + do_process_instruction_dups( + mint_to( + &program_id, + &mint_key, + &account1_key, + &account1_key, + &[], + 42, + ) + .unwrap(), + vec![ + mint_info.clone(), + account1_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + } + #[test] fn test_mint_to() { let program_id = pubkey_rand(); From 97490a0b50be6fdccc01f79ec5523cd252b15920 Mon Sep 17 00:00:00 2001 From: Jack May Date: Mon, 21 Sep 2020 12:48:58 -0700 Subject: [PATCH 063/248] backport #435 to v2 --- program/src/processor.rs | 121 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 113 insertions(+), 8 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index 466b644..5ef4395 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -327,8 +327,8 @@ impl Processor { let authority_info = next_account_info(account_info_iter)?; if account_info.data_len() == Account::get_packed_len() { - let mut account_data = account_info.data.borrow_mut(); - Account::unpack_mut(&mut account_data, &mut |account: &mut Account| { + let mut account = Account::unpack(&account_info.data.borrow())?; + if account.is_frozen() { return Err(TokenError::AccountFrozen.into()); } @@ -362,11 +362,9 @@ impl Processor { return Err(TokenError::AuthorityTypeNotSupported.into()); } } - Ok(()) - })?; + Account::pack(account, &mut account_info.data.borrow_mut())?; } else if account_info.data_len() == Mint::get_packed_len() { - let mut mint_data = account_info.data.borrow_mut(); - Mint::unpack_mut(&mut mint_data, &mut |mint: &mut Mint| { + let mut mint = Mint::unpack(&account_info.data.borrow())?; match authority_type { AuthorityType::MintTokens => { // Once a mint's supply is fixed, it cannot be undone by setting a new @@ -400,8 +398,7 @@ impl Processor { return Err(TokenError::AuthorityTypeNotSupported.into()); } } - Ok(()) - })?; + Mint::pack(mint, &mut account_info.data.borrow_mut())?; } else { return Err(ProgramError::InvalidArgument); } @@ -2184,6 +2181,114 @@ mod tests { .unwrap(); } + #[test] + fn test_set_authority_dups() { + let program_id = pubkey_rand(); + let account1_key = pubkey_rand(); + let mut account1_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); + let owner_key = pubkey_rand(); + let mint_key = pubkey_rand(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mint_info: AccountInfo = (&mint_key, true, &mut mint_account).into(); + let rent_key = rent::id(); + let mut rent_sysvar = rent_sysvar(); + let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); + + // create mint + do_process_instruction_dups( + initialize_mint(&program_id, &mint_key, &mint_key, Some(&mint_key), 2).unwrap(), + vec![mint_info.clone(), rent_info.clone()], + ) + .unwrap(); + + // create account + do_process_instruction_dups( + initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + + // set mint_authority when currently self + do_process_instruction_dups( + set_authority( + &program_id, + &mint_key, + Some(&owner_key), + AuthorityType::MintTokens, + &mint_key, + &[], + ) + .unwrap(), + vec![mint_info.clone(), mint_info.clone()], + ) + .unwrap(); + + // set freeze_authority when currently self + do_process_instruction_dups( + set_authority( + &program_id, + &mint_key, + Some(&owner_key), + AuthorityType::FreezeAccount, + &mint_key, + &[], + ) + .unwrap(), + vec![mint_info.clone(), mint_info.clone()], + ) + .unwrap(); + + // set account owner when currently self + do_process_instruction_dups( + set_authority( + &program_id, + &account1_key, + Some(&owner_key), + AuthorityType::AccountOwner, + &account1_key, + &[], + ) + .unwrap(), + vec![account1_info.clone(), account1_info.clone()], + ) + .unwrap(); + + // set close_authority when currently self + Account::unpack_unchecked_mut( + &mut account1_info.data.borrow_mut(), + &mut |account: &mut Account| { + account.close_authority = COption::Some(account1_key); + Ok(()) + }, + ) + .unwrap(); + + do_process_instruction_dups( + set_authority( + &program_id, + &account1_key, + Some(&owner_key), + AuthorityType::CloseAccount, + &account1_key, + &[], + ) + .unwrap(), + vec![account1_info.clone(), account1_info.clone()], + ) + .unwrap(); + } + #[test] fn test_set_authority() { let program_id = pubkey_rand(); From 76f71adcbe8a221d9ef0687ee832b03fd3d980e3 Mon Sep 17 00:00:00 2001 From: Jack May Date: Mon, 21 Sep 2020 12:49:57 -0700 Subject: [PATCH 064/248] backport #434 to v2 --- program/src/processor.rs | 225 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 219 insertions(+), 6 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index 5ef4395..1508a02 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -257,8 +257,8 @@ impl Processor { let delegate_info = next_account_info(account_info_iter)?; let owner_info = next_account_info(account_info_iter)?; - let mut source_data = source_account_info.data.borrow_mut(); - Account::unpack_mut(&mut source_data, &mut |source_account: &mut Account| { + let mut source_account = Account::unpack(&source_account_info.data.borrow())?; + if source_account.is_frozen() { return Err(TokenError::AccountFrozen.into()); } @@ -284,8 +284,9 @@ impl Processor { source_account.delegate = COption::Some(*delegate_info.key); source_account.delegated_amount = amount; + Account::pack(source_account, &mut source_account_info.data.borrow_mut())?; + Ok(()) - }) } /// Processes an [Revoke](enum.TokenInstruction.html) instruction. @@ -293,8 +294,8 @@ impl Processor { let account_info_iter = &mut accounts.iter(); let source_account_info = next_account_info(account_info_iter)?; - let mut source_data = source_account_info.data.borrow_mut(); - Account::unpack_mut(&mut source_data, &mut |source_account: &mut Account| { + let mut source_account = Account::unpack(&source_account_info.data.borrow())?; + let owner_info = next_account_info(account_info_iter)?; if source_account.is_frozen() { @@ -311,8 +312,9 @@ impl Processor { source_account.delegate = COption::None; source_account.delegated_amount = 0; + Account::pack(source_account, &mut source_account_info.data.borrow_mut())?; + Ok(()) - }) } /// Processes a [SetAuthority](enum.TokenInstruction.html) instruction. @@ -1976,6 +1978,217 @@ mod tests { .unwrap(); } + #[test] + fn test_approve_dups() { + let program_id = pubkey_rand(); + let account1_key = pubkey_rand(); + let mut account1_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); + let account2_key = pubkey_rand(); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account2_info: AccountInfo = (&account2_key, false, &mut account2_account).into(); + let account3_key = pubkey_rand(); + let mut account3_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account3_info: AccountInfo = (&account3_key, true, &mut account3_account).into(); + let multisig_key = pubkey_rand(); + let mut multisig_account = SolanaAccount::new( + multisig_minimum_balance(), + Multisig::get_packed_len(), + &program_id, + ); + let multisig_info: AccountInfo = (&multisig_key, true, &mut multisig_account).into(); + let owner_key = pubkey_rand(); + let mut owner_account = SolanaAccount::default(); + let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into(); + let mint_key = pubkey_rand(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mint_info: AccountInfo = (&mint_key, false, &mut mint_account).into(); + let rent_key = rent::id(); + let mut rent_sysvar = rent_sysvar(); + let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); + + // create mint + do_process_instruction_dups( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![mint_info.clone(), rent_info.clone()], + ) + .unwrap(); + + // create account + do_process_instruction_dups( + initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account1_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + + // create another account + do_process_instruction_dups( + initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), + vec![ + account2_info.clone(), + mint_info.clone(), + owner_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + + // mint to account + do_process_instruction_dups( + mint_to(&program_id, &mint_key, &account1_key, &owner_key, &[], 1000).unwrap(), + vec![mint_info.clone(), account1_info.clone(), owner_info.clone()], + ) + .unwrap(); + + // source-owner approve + do_process_instruction_dups( + approve( + &program_id, + &account1_key, + &account2_key, + &account1_key, + &[], + 500, + ) + .unwrap(), + vec![ + account1_info.clone(), + account2_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // source-owner approve2 + do_process_instruction_dups( + approve2( + &program_id, + &account1_key, + &mint_key, + &account2_key, + &account1_key, + &[], + 500, + 2, + ) + .unwrap(), + vec![ + account1_info.clone(), + mint_info.clone(), + account2_info.clone(), + account1_info.clone(), + ], + ) + .unwrap(); + + // source-owner revoke + do_process_instruction_dups( + revoke(&program_id, &account1_key, &account1_key, &[]).unwrap(), + vec![account1_info.clone(), account1_info.clone()], + ) + .unwrap(); + + // test source-multisig signer + do_process_instruction_dups( + initialize_multisig(&program_id, &multisig_key, &[&account3_key], 1).unwrap(), + vec![ + multisig_info.clone(), + rent_info.clone(), + account3_info.clone(), + ], + ) + .unwrap(); + + do_process_instruction_dups( + initialize_account(&program_id, &account3_key, &mint_key, &multisig_key).unwrap(), + vec![ + account3_info.clone(), + mint_info.clone(), + multisig_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + + do_process_instruction_dups( + mint_to(&program_id, &mint_key, &account3_key, &owner_key, &[], 1000).unwrap(), + vec![mint_info.clone(), account3_info.clone(), owner_info.clone()], + ) + .unwrap(); + + // source-multisig-signer approve + do_process_instruction_dups( + approve( + &program_id, + &account3_key, + &account2_key, + &multisig_key, + &[&account3_key], + 500, + ) + .unwrap(), + vec![ + account3_info.clone(), + account2_info.clone(), + multisig_info.clone(), + account3_info.clone(), + ], + ) + .unwrap(); + + // source-multisig-signer approve2 + do_process_instruction_dups( + approve2( + &program_id, + &account3_key, + &mint_key, + &account2_key, + &multisig_key, + &[&account3_key], + 500, + 2, + ) + .unwrap(), + vec![ + account3_info.clone(), + mint_info.clone(), + account2_info.clone(), + multisig_info.clone(), + account3_info.clone(), + ], + ) + .unwrap(); + + // source-owner multisig-signer + do_process_instruction_dups( + revoke(&program_id, &account3_key, &multisig_key, &[&account3_key]).unwrap(), + vec![ + account3_info.clone(), + multisig_info.clone(), + account3_info.clone(), + ], + ) + .unwrap(); + } + #[test] fn test_approve() { let program_id = pubkey_rand(); From 88054797609d2d25aa3ea6cbdc1a4f64ba2662e2 Mon Sep 17 00:00:00 2001 From: Jack May Date: Mon, 21 Sep 2020 12:55:45 -0700 Subject: [PATCH 065/248] backport #443 to v2 --- program/src/pack.rs | 27 --- program/src/processor.rs | 371 +++++++++++---------------------------- 2 files changed, 101 insertions(+), 297 deletions(-) diff --git a/program/src/pack.rs b/program/src/pack.rs index 7a86cb9..106b51b 100644 --- a/program/src/pack.rs +++ b/program/src/pack.rs @@ -47,33 +47,6 @@ pub trait Pack: Sealed { Ok(Self::unpack_from_slice(input)?) } - /// Borrow `Self` from `input` for the duration of the call to `f`, but first check that `Self` - /// is initialized - #[inline(never)] - fn unpack_mut(input: &mut [u8], f: &mut F) -> Result - where - F: FnMut(&mut Self) -> Result, - Self: IsInitialized, - { - let mut t = Self::unpack(input)?; - let u = f(&mut t)?; - Self::pack(t, input)?; - Ok(u) - } - - /// Borrow `Self` from `input` for the duration of the call to `f`, without checking that - /// `Self` has been initialized - #[inline(never)] - fn unpack_unchecked_mut(input: &mut [u8], f: &mut F) -> Result - where - F: FnMut(&mut Self) -> Result, - { - let mut t = Self::unpack_unchecked(input)?; - let u = f(&mut t)?; - Self::pack(t, input)?; - Ok(u) - } - /// Pack into slice fn pack(src: Self, dst: &mut [u8]) -> Result<(), ProgramError> { if dst.len() != Self::LEN { diff --git a/program/src/processor.rs b/program/src/processor.rs index 1508a02..79b93d9 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -35,8 +35,7 @@ impl Processor { let mint_data_len = mint_info.data_len(); let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?; - let mut mint_data = mint_info.data.borrow_mut(); - Mint::unpack_unchecked_mut(&mut mint_data, &mut |mint: &mut Mint| { + let mut mint = Mint::unpack_unchecked(&mint_info.data.borrow())?; if mint.is_initialized { return Err(TokenError::AlreadyInUse.into()); } @@ -50,8 +49,9 @@ impl Processor { mint.is_initialized = true; mint.freeze_authority = freeze_authority; + Mint::pack(mint, &mut mint_info.data.borrow_mut())?; + Ok(()) - }) } /// Processes an [InitializeAccount](enum.TokenInstruction.html) instruction. @@ -63,8 +63,7 @@ impl Processor { let new_account_info_data_len = new_account_info.data_len(); let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?; - let mut new_account_data = new_account_info.data.borrow_mut(); - Account::unpack_unchecked_mut(&mut new_account_data, &mut |account: &mut Account| { + let mut account = Account::unpack_unchecked(&new_account_info.data.borrow())?; if account.is_initialized() { return Err(TokenError::AlreadyInUse.into()); } @@ -95,8 +94,9 @@ impl Processor { account.amount = 0; }; + Account::pack(account, &mut new_account_info.data.borrow_mut())?; + Ok(()) - }) } /// Processes a [DangerInitializeMultisig](enum.TokenInstruction.html) instruction. @@ -106,10 +106,7 @@ impl Processor { let multisig_info_data_len = multisig_info.data_len(); let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?; - let mut multisig_account_data = multisig_info.data.borrow_mut(); - Multisig::unpack_unchecked_mut( - &mut multisig_account_data, - &mut |multisig: &mut Multisig| { + let mut multisig = Multisig::unpack_unchecked(&multisig_info.data.borrow())?; if multisig.is_initialized { return Err(TokenError::AlreadyInUse.into()); } @@ -132,9 +129,9 @@ impl Processor { } multisig.is_initialized = true; + Multisig::pack(multisig, &mut multisig_info.data.borrow_mut())?; + Ok(()) - }, - ) } /// Processes a [Transfer](enum.TokenInstruction.html) instruction. @@ -715,8 +712,7 @@ impl Processor { if program_id == owner_account_info.owner && owner_account_info.data_len() == Multisig::get_packed_len() { - let mut owner_data = owner_account_info.data.borrow_mut(); - Multisig::unpack_mut(&mut owner_data, &mut |multisig: &mut Multisig| { + let multisig = Multisig::unpack(&owner_account_info.data.borrow())?; let mut num_signers = 0; for signer in signers.iter() { if multisig.signers[0..multisig.n as usize].contains(signer.key) { @@ -729,8 +725,7 @@ impl Processor { if num_signers < multisig.m { return Err(ProgramError::MissingRequiredSignature); } - Ok(()) - })?; + return Ok(()); } else if !owner_account_info.is_signer { return Err(ProgramError::MissingRequiredSignature); } @@ -1011,11 +1006,8 @@ mod tests { vec![&mut mint2_account, &mut rent_sysvar], ) .unwrap(); - Mint::unpack_unchecked_mut(&mut mint2_account.data, &mut |mint: &mut Mint| { + let mint = Mint::unpack_unchecked(&mint2_account.data).unwrap(); assert_eq!(mint.freeze_authority, COption::Some(owner_key)); - Ok(()) - }) - .unwrap(); } #[test] @@ -1223,17 +1215,12 @@ mod tests { .unwrap(); // source-delegate transfer - Account::unpack_unchecked_mut( - &mut account1_info.data.borrow_mut(), - &mut |account: &mut Account| { + let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); account.amount = 1000; account.delegated_amount = 1000; account.delegate = COption::Some(account1_key); account.owner = owner_key; - Ok(()) - }, - ) - .unwrap(); + Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); do_process_instruction_dups( transfer( @@ -1499,11 +1486,9 @@ mod tests { ], ) .unwrap(); - Account::unpack_unchecked_mut(&mut mismatch_account.data, &mut |account: &mut Account| { + let mut account = Account::unpack_unchecked(&mismatch_account.data).unwrap(); account.mint = mint2_key; - Ok(()) - }) - .unwrap(); + Account::pack(account, &mut mismatch_account.data).unwrap(); // mint to account do_process_instruction( @@ -1729,11 +1714,8 @@ mod tests { ) .unwrap() } - Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.amount, 1000); - Ok(()) - }) - .unwrap(); // insufficient funds assert_eq!( @@ -1890,9 +1872,9 @@ mod tests { vec![&mut mint_account, &mut rent_sysvar], ) .unwrap(); - Mint::unpack_unchecked_mut(&mut mint_account.data, &mut |mint: &mut Mint| { + let mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); assert_eq!( - *mint, + mint, Mint { mint_authority: COption::Some(owner_key), supply: 0, @@ -1901,9 +1883,6 @@ mod tests { freeze_authority: COption::None, } ); - Ok(()) - }) - .unwrap(); // create account do_process_instruction( @@ -1924,11 +1903,8 @@ mod tests { ) .unwrap(); let _ = Mint::unpack(&mut mint_account.data).unwrap(); - Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.amount, 42); - Ok(()) - }) - .unwrap(); // mint to 2, with incorrect decimals assert_eq!( @@ -1949,11 +1925,8 @@ mod tests { ); let _ = Mint::unpack(&mut mint_account.data).unwrap(); - Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.amount, 42); - Ok(()) - }) - .unwrap(); // mint to 2 do_process_instruction( @@ -1971,11 +1944,8 @@ mod tests { ) .unwrap(); let _ = Mint::unpack(&mut mint_account.data).unwrap(); - Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.amount, 84); - Ok(()) - }) - .unwrap(); } #[test] @@ -2478,14 +2448,9 @@ mod tests { .unwrap(); // set close_authority when currently self - Account::unpack_unchecked_mut( - &mut account1_info.data.borrow_mut(), - &mut |account: &mut Account| { + let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); account.close_authority = COption::Some(account1_key); - Ok(()) - }, - ) - .unwrap(); + Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); do_process_instruction_dups( set_authority( @@ -2896,11 +2861,9 @@ mod tests { .unwrap(); // mint_to when mint_authority is account owner - Mint::unpack_unchecked_mut(&mut mint_info.data.borrow_mut(), &mut |mint: &mut Mint| { + let mut mint = Mint::unpack_unchecked(&mint_info.data.borrow()).unwrap(); mint.mint_authority = COption::Some(account1_key); - Ok(()) - }) - .unwrap(); + Mint::pack(mint, &mut mint_info.data.borrow_mut()).unwrap(); do_process_instruction_dups( mint_to( &program_id, @@ -3036,11 +2999,9 @@ mod tests { ], ) .unwrap(); - Account::unpack_unchecked_mut(&mut mismatch_account.data, &mut |account: &mut Account| { + let mut account = Account::unpack_unchecked(&mismatch_account.data).unwrap(); account.mint = mint2_key; - Ok(()) - }) - .unwrap(); + Account::pack(account, &mut mismatch_account.data).unwrap(); // mint to do_process_instruction( @@ -3049,16 +3010,10 @@ mod tests { ) .unwrap(); - Mint::unpack_unchecked_mut(&mut mint_account.data, &mut |mint: &mut Mint| { + let mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); assert_eq!(mint.supply, 42); - Ok(()) - }) - .unwrap(); - Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.amount, 42); - Ok(()) - }) - .unwrap(); // mint to another account to test supply accumulation do_process_instruction( @@ -3067,16 +3022,10 @@ mod tests { ) .unwrap(); - Mint::unpack_unchecked_mut(&mut mint_account.data, &mut |mint: &mut Mint| { + let mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); assert_eq!(mint.supply, 84); - Ok(()) - }) - .unwrap(); - Account::unpack_unchecked_mut(&mut account2_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account2_account.data).unwrap(); assert_eq!(account.amount, 42); - Ok(()) - }) - .unwrap(); // missing signer let mut instruction = @@ -3248,14 +3197,9 @@ mod tests { vec![mint_info.clone(), account1_info.clone(), owner_info.clone()], ) .unwrap(); - Account::unpack_unchecked_mut( - &mut account1_info.data.borrow_mut(), - &mut |account: &mut Account| { + let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); account.owner = mint_key; - Ok(()) - }, - ) - .unwrap(); + Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); do_process_instruction_dups( burn(&program_id, &account1_key, &mint_key, &mint_key, &[], 500).unwrap(), vec![account1_info.clone(), mint_info.clone(), mint_info.clone()], @@ -3284,16 +3228,11 @@ mod tests { vec![mint_info.clone(), account1_info.clone(), owner_info.clone()], ) .unwrap(); - Account::unpack_unchecked_mut( - &mut account1_info.data.borrow_mut(), - &mut |account: &mut Account| { + let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); account.delegated_amount = 1000; account.delegate = COption::Some(account1_key); account.owner = owner_key; - Ok(()) - }, - ) - .unwrap(); + Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); do_process_instruction_dups( burn( &program_id, @@ -3338,16 +3277,11 @@ mod tests { vec![mint_info.clone(), account1_info.clone(), owner_info.clone()], ) .unwrap(); - Account::unpack_unchecked_mut( - &mut account1_info.data.borrow_mut(), - &mut |account: &mut Account| { + let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); account.delegated_amount = 1000; account.delegate = COption::Some(mint_key); account.owner = owner_key; - Ok(()) - }, - ) - .unwrap(); + Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); do_process_instruction_dups( burn(&program_id, &account1_key, &mint_key, &mint_key, &[], 500).unwrap(), vec![account1_info.clone(), mint_info.clone(), mint_info.clone()], @@ -3464,11 +3398,9 @@ mod tests { ], ) .unwrap(); - Account::unpack_unchecked_mut(&mut mismatch_account.data, &mut |account: &mut Account| { + let mut account = Account::unpack_unchecked(&mismatch_account.data).unwrap(); account.mint = mint2_key; - Ok(()) - }) - .unwrap(); + Account::pack(account, &mut mismatch_account.data).unwrap(); // mint to account do_process_instruction( @@ -3534,17 +3466,10 @@ mod tests { ) .unwrap(); - Mint::unpack_unchecked_mut(&mut mint_account.data, &mut |mint: &mut Mint| { + let mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); assert_eq!(mint.supply, 1000 - 42); - - Ok(()) - }) - .unwrap(); - Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.amount, 1000 - 42); - Ok(()) - }) - .unwrap(); // insufficient funds assert_eq!( @@ -3611,16 +3536,10 @@ mod tests { .unwrap(); // match - Mint::unpack_unchecked_mut(&mut mint_account.data, &mut |mint: &mut Mint| { + let mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); assert_eq!(mint.supply, 1000 - 42 - 84); - Ok(()) - }) - .unwrap(); - Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.amount, 1000 - 42 - 84); - Ok(()) - }) - .unwrap(); // insufficient funds approved via delegate assert_eq!( @@ -4077,14 +3996,12 @@ mod tests { } let mut lamports = 0; let mut data = vec![0; Multisig::get_packed_len()]; - Multisig::unpack_unchecked_mut(&mut data, &mut |multisig: &mut Multisig| { + let mut multisig = Multisig::unpack_unchecked(&data).unwrap(); multisig.m = MAX_SIGNERS as u8; multisig.n = MAX_SIGNERS as u8; multisig.signers = signer_keys; multisig.is_initialized = true; - Ok(()) - }) - .unwrap(); + Multisig::pack(multisig, &mut data).unwrap(); let owner_account_info = AccountInfo::new( &owner_key, false, @@ -4101,24 +4018,20 @@ mod tests { // 1 of 11 { - let mut data_ref_mut = owner_account_info.data.borrow_mut(); - Multisig::unpack_unchecked_mut(&mut data_ref_mut, &mut |multisig: &mut Multisig| { + let mut multisig = + Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap(); multisig.m = 1; - Ok(()) - }) - .unwrap(); + Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap(); } Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap(); // 2:1 { - let mut data_ref_mut = owner_account_info.data.borrow_mut(); - Multisig::unpack_unchecked_mut(&mut data_ref_mut, &mut |multisig: &mut Multisig| { + let mut multisig = + Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap(); multisig.m = 2; multisig.n = 1; - Ok(()) - }) - .unwrap(); + Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap(); } assert_eq!( Err(ProgramError::MissingRequiredSignature), @@ -4127,25 +4040,21 @@ mod tests { // 0:11 { - let mut data_ref_mut = owner_account_info.data.borrow_mut(); - Multisig::unpack_unchecked_mut(&mut data_ref_mut, &mut |multisig: &mut Multisig| { + let mut multisig = + Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap(); multisig.m = 0; multisig.n = 11; - Ok(()) - }) - .unwrap(); + Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap(); } Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap(); // 2:11 but 0 provided { - let mut data_ref_mut = owner_account_info.data.borrow_mut(); - Multisig::unpack_unchecked_mut(&mut data_ref_mut, &mut |multisig: &mut Multisig| { + let mut multisig = + Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap(); multisig.m = 2; multisig.n = 11; - Ok(()) - }) - .unwrap(); + Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap(); } assert_eq!( Err(ProgramError::MissingRequiredSignature), @@ -4153,13 +4062,11 @@ mod tests { ); // 2:11 but 1 provided { - let mut data_ref_mut = owner_account_info.data.borrow_mut(); - Multisig::unpack_unchecked_mut(&mut data_ref_mut, &mut |multisig: &mut Multisig| { + let mut multisig = + Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap(); multisig.m = 2; multisig.n = 11; - Ok(()) - }) - .unwrap(); + Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap(); } assert_eq!( Err(ProgramError::MissingRequiredSignature), @@ -4168,26 +4075,22 @@ mod tests { // 2:11, 2 from middle provided { - let mut data_ref_mut = owner_account_info.data.borrow_mut(); - Multisig::unpack_unchecked_mut(&mut data_ref_mut, &mut |multisig: &mut Multisig| { + let mut multisig = + Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap(); multisig.m = 2; multisig.n = 11; - Ok(()) - }) - .unwrap(); + Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap(); } Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers[5..7]) .unwrap(); // 11:11, one is not a signer { - let mut data_ref_mut = owner_account_info.data.borrow_mut(); - Multisig::unpack_unchecked_mut(&mut data_ref_mut, &mut |multisig: &mut Multisig| { + let mut multisig = + Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap(); multisig.m = 2; // TODO 11? multisig.n = 11; - Ok(()) - }) - .unwrap(); + Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap(); } signers[5].is_signer = false; assert_eq!( @@ -4261,15 +4164,10 @@ mod tests { .unwrap(); // source-close-authority close - Account::unpack_unchecked_mut( - &mut account1_info.data.borrow_mut(), - &mut |account: &mut Account| { + let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); account.close_authority = COption::Some(account1_key); account.owner = owner_key; - Ok(()) - }, - ) - .unwrap(); + Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); do_process_instruction_dups( close_account( &program_id, @@ -4357,11 +4255,8 @@ mod tests { ], ) .unwrap(); - Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.amount, 42); - Ok(()) - }) - .unwrap(); // initialize native account do_process_instruction( @@ -4380,12 +4275,9 @@ mod tests { ], ) .unwrap(); - Account::unpack_unchecked_mut(&mut account2_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account2_account.data).unwrap(); assert!(account.is_native()); assert_eq!(account.amount, 42); - Ok(()) - }) - .unwrap(); // close non-native account with balance assert_eq!( @@ -4433,11 +4325,8 @@ mod tests { .unwrap(); assert_eq!(account_account.lamports, 0); assert_eq!(account3_account.lamports, 2 * account_minimum_balance()); - Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.amount, 0); - Ok(()) - }) - .unwrap(); // fund and initialize new non-native account to test close authority let account_key = pubkey_rand(); @@ -4503,11 +4392,8 @@ mod tests { .unwrap(); assert_eq!(account_account.lamports, 0); assert_eq!(account3_account.lamports, 2 * account_minimum_balance() + 2); - Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.amount, 0); - Ok(()) - }) - .unwrap(); // close native account do_process_instruction( @@ -4519,7 +4405,7 @@ mod tests { ], ) .unwrap(); - Account::unpack_unchecked_mut(&mut account2_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account2_account.data).unwrap(); assert!(account.is_native()); assert_eq!(account_account.lamports, 0); assert_eq!(account.amount, 0); @@ -4527,9 +4413,6 @@ mod tests { account3_account.lamports, 3 * account_minimum_balance() + 2 + 42 ); - Ok(()) - }) - .unwrap(); } #[test] @@ -4572,12 +4455,9 @@ mod tests { ], ) .unwrap(); - Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert!(account.is_native()); assert_eq!(account.amount, 40); - Ok(()) - }) - .unwrap(); // initialize native account do_process_instruction( @@ -4596,12 +4476,9 @@ mod tests { ], ) .unwrap(); - Account::unpack_unchecked_mut(&mut account2_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account2_account.data).unwrap(); assert!(account.is_native()); assert_eq!(account.amount, 0); - Ok(()) - }) - .unwrap(); // mint_to unsupported assert_eq!( @@ -4690,19 +4567,13 @@ mod tests { ) .unwrap(); assert_eq!(account_account.lamports, account_minimum_balance()); - Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert!(account.is_native()); assert_eq!(account.amount, 0); - Ok(()) - }) - .unwrap(); assert_eq!(account2_account.lamports, account_minimum_balance() + 40); - Account::unpack_unchecked_mut(&mut account2_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account2_account.data).unwrap(); assert!(account.is_native()); assert_eq!(account.amount, 40); - Ok(()) - }) - .unwrap(); // close native account do_process_instruction( @@ -4716,12 +4587,9 @@ mod tests { .unwrap(); assert_eq!(account_account.lamports, 0); assert_eq!(account3_account.lamports, 2 * account_minimum_balance()); - Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert!(account.is_native()); assert_eq!(account.amount, 0); - Ok(()) - }) - .unwrap(); } #[test] @@ -4799,11 +4667,8 @@ mod tests { ], ) .unwrap(); - Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.amount, u64::MAX); - Ok(()) - }) - .unwrap(); // attempt to mint one more to account assert_eq!( @@ -4825,11 +4690,8 @@ mod tests { ], ) ); - Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.amount, u64::MAX); - Ok(()) - }) - .unwrap(); // atttempt to mint one more to the other account assert_eq!( @@ -4858,11 +4720,8 @@ mod tests { vec![&mut account_account, &mut mint_account, &mut owner_account], ) .unwrap(); - Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.amount, u64::MAX - 100); - Ok(()) - }) - .unwrap(); do_process_instruction( mint_to( @@ -4881,18 +4740,13 @@ mod tests { ], ) .unwrap(); - Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.amount, u64::MAX); - Ok(()) - }) - .unwrap(); // manipulate account balance to attempt overflow transfer - Account::unpack_unchecked_mut(&mut account2_account.data, &mut |account: &mut Account| { + let mut account = Account::unpack_unchecked(&account2_account.data).unwrap(); account.amount = 1; - Ok(()) - }) - .unwrap(); + Account::pack(account, &mut account2_account.data).unwrap(); assert_eq!( Err(TokenError::Overflow.into()), @@ -4976,11 +4830,9 @@ mod tests { .unwrap(); // no transfer if either account is frozen - Account::unpack_unchecked_mut(&mut account2_account.data, &mut |account: &mut Account| { + let mut account = Account::unpack_unchecked(&account2_account.data).unwrap(); account.state = AccountState::Frozen; - Ok(()) - }) - .unwrap(); + Account::pack(account, &mut account2_account.data).unwrap(); assert_eq!( Err(TokenError::AccountFrozen.into()), do_process_instruction( @@ -5001,16 +4853,12 @@ mod tests { ) ); - Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + let mut account = Account::unpack_unchecked(&account_account.data).unwrap(); account.state = AccountState::Initialized; - Ok(()) - }) - .unwrap(); - Account::unpack_unchecked_mut(&mut account2_account.data, &mut |account: &mut Account| { + Account::pack(account, &mut account_account.data).unwrap(); + let mut account = Account::unpack_unchecked(&account2_account.data).unwrap(); account.state = AccountState::Frozen; - Ok(()) - }) - .unwrap(); + Account::pack(account, &mut account2_account.data).unwrap(); assert_eq!( Err(TokenError::AccountFrozen.into()), do_process_instruction( @@ -5032,11 +4880,9 @@ mod tests { ); // no approve if account is frozen - Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + let mut account = Account::unpack_unchecked(&account_account.data).unwrap(); account.state = AccountState::Frozen; - Ok(()) - }) - .unwrap(); + Account::pack(account, &mut account_account.data).unwrap(); let delegate_key = pubkey_rand(); let mut delegate_account = SolanaAccount::default(); assert_eq!( @@ -5060,12 +4906,10 @@ mod tests { ); // no revoke if account is frozen - Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + let mut account = Account::unpack_unchecked(&account_account.data).unwrap(); account.delegate = COption::Some(delegate_key); account.delegated_amount = 100; - Ok(()) - }) - .unwrap(); + Account::pack(account, &mut account_account.data).unwrap(); assert_eq!( Err(TokenError::AccountFrozen.into()), do_process_instruction( @@ -5161,14 +5005,9 @@ mod tests { .unwrap(); // thaw where mint freeze_authority is account - Account::unpack_unchecked_mut( - &mut account1_info.data.borrow_mut(), - &mut |account: &mut Account| { + let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); account.state = AccountState::Frozen; - Ok(()) - }, - ) - .unwrap(); + Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); do_process_instruction_dups( thaw_account(&program_id, &account1_key, &mint_key, &account1_key, &[]).unwrap(), vec![ @@ -5236,11 +5075,9 @@ mod tests { ); // missing freeze_authority - Mint::unpack_unchecked_mut(&mut mint_account.data, &mut |mint: &mut Mint| { + let mut mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); mint.freeze_authority = COption::Some(owner_key); - Ok(()) - }) - .unwrap(); + Mint::pack(mint, &mut mint_account.data).unwrap(); assert_eq!( Err(TokenError::OwnerMismatch.into()), do_process_instruction( @@ -5264,11 +5101,8 @@ mod tests { vec![&mut account_account, &mut mint_account, &mut owner_account], ) .unwrap(); - Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.state, AccountState::Frozen); - Ok(()) - }) - .unwrap(); // check explicit freeze assert_eq!( @@ -5294,10 +5128,7 @@ mod tests { vec![&mut account_account, &mut mint_account, &mut owner_account], ) .unwrap(); - Account::unpack_unchecked_mut(&mut account_account.data, &mut |account: &mut Account| { + let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.state, AccountState::Initialized); - Ok(()) - }) - .unwrap(); } } From 989f06d639c3d718767c8aca424a0c2289937f19 Mon Sep 17 00:00:00 2001 From: Jack May Date: Mon, 21 Sep 2020 12:58:26 -0700 Subject: [PATCH 066/248] backport #448 to v2 --- program/src/error.rs | 3 --- program/src/pack.rs | 3 +-- program/src/processor.rs | 7 +++---- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/program/src/error.rs b/program/src/error.rs index 32cd9d2..e56fde6 100644 --- a/program/src/error.rs +++ b/program/src/error.rs @@ -34,9 +34,6 @@ pub enum TokenError { /// Invalid number of required signers. #[error("Invalid number of required signers")] InvalidNumberOfRequiredSigners, - /// State is uninitialized. - #[error("State is unititialized")] - UninitializedState, /// Instruction does not support native tokens #[error("Instruction does not support native tokens")] NativeNotSupported, diff --git a/program/src/pack.rs b/program/src/pack.rs index 106b51b..7c0f355 100644 --- a/program/src/pack.rs +++ b/program/src/pack.rs @@ -1,6 +1,5 @@ //! State transition types -use crate::error::TokenError; use solana_sdk::program_error::ProgramError; /// Check is a token state is initialized @@ -35,7 +34,7 @@ pub trait Pack: Sealed { if value.is_initialized() { Ok(value) } else { - Err(TokenError::UninitializedState.into()) + Err(ProgramError::UninitializedAccount) } } diff --git a/program/src/processor.rs b/program/src/processor.rs index 79b93d9..7633636 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -754,7 +754,6 @@ impl PrintProgramError for TokenError { TokenError::InvalidNumberOfRequiredSigners => { info!("Error: Invalid number of required signers") } - TokenError::UninitializedState => info!("Error: State is uninitialized"), TokenError::NativeNotSupported => { info!("Error: Instruction does not support native tokens") } @@ -2511,7 +2510,7 @@ mod tests { // invalid account assert_eq!( - Err(TokenError::UninitializedState.into()), + Err(ProgramError::UninitializedAccount), do_process_instruction( set_authority( &program_id, @@ -3063,7 +3062,7 @@ mod tests { // uninitialized destination account assert_eq!( - Err(TokenError::UninitializedState.into()), + Err(ProgramError::UninitializedAccount), do_process_instruction( mint_to( &program_id, @@ -4218,7 +4217,7 @@ mod tests { // uninitialized assert_eq!( - Err(TokenError::UninitializedState.into()), + Err(ProgramError::UninitializedAccount), do_process_instruction( close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(), vec![ From ba9deef3e44cd32968f4f6fbf1591bd59deac6a7 Mon Sep 17 00:00:00 2001 From: Jack May Date: Mon, 21 Sep 2020 13:08:17 -0700 Subject: [PATCH 067/248] backport #479 to v2 --- program/src/instruction.rs | 19 ++++++------- program/src/processor.rs | 55 +++++++++++++++++++++++++++++--------- 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 2e2d990..5c8e1b0 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -56,14 +56,11 @@ pub enum TokenInstruction { InitializeAccount, /// Initializes a multisignature account with N provided signers. /// - /// Warning, this instruction is compromised: - /// https://github.com/solana-labs/solana-program-library/issues/477 - /// /// Multisignature accounts can used in place of any single owner/delegate accounts in any /// token instruction that require an owner/delegate to be present. The variant field represents the /// number of signers (M) required to validate this multisignature account. /// - /// The `DangerInitializeMultisig` instruction requires no signers and MUST be included within + /// The `InitializeMultisig` instruction requires no signers and MUST be included within /// the same Transaction as the system program's `CreateInstruction` that creates the account /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. /// @@ -72,7 +69,7 @@ pub enum TokenInstruction { /// 0. `[writable]` The multisignature account to initialize. /// 1. `[]` Rent sysvar /// 2. ..2+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. - DangerInitializeMultisig { + InitializeMultisig { /// The number of signers (M) required to validate this multisignature account. m: u8, }, @@ -353,7 +350,7 @@ impl TokenInstruction { 1 => Self::InitializeAccount, 2 => { let &m = rest.get(0).ok_or(InvalidInstruction)?; - Self::DangerInitializeMultisig { m } + Self::InitializeMultisig { m } } 3 | 4 | 7 | 8 => { let amount = rest @@ -449,7 +446,7 @@ impl TokenInstruction { Self::pack_pubkey_option(freeze_authority, &mut buf); } Self::InitializeAccount => buf.push(1), - &Self::DangerInitializeMultisig { m } => { + &Self::InitializeMultisig { m } => { buf.push(2); buf.push(m); } @@ -624,8 +621,8 @@ pub fn initialize_account( }) } -/// Creates a `DangerInitializeMultisig` instruction. -pub fn danger_initialize_multisig( +/// Creates a `InitializeMultisig` instruction. +pub fn initialize_multisig( token_program_id: &Pubkey, multisig_pubkey: &Pubkey, signer_pubkeys: &[&Pubkey], @@ -637,7 +634,7 @@ pub fn danger_initialize_multisig( { return Err(ProgramError::MissingRequiredSignature); } - let data = TokenInstruction::DangerInitializeMultisig { m }.pack(); + let data = TokenInstruction::InitializeMultisig { m }.pack(); let mut accounts = Vec::with_capacity(1 + 1 + signer_pubkeys.len()); accounts.push(AccountMeta::new(*multisig_pubkey, false)); @@ -1083,7 +1080,7 @@ mod test { let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); - let check = TokenInstruction::DangerInitializeMultisig { m: 1 }; + let check = TokenInstruction::InitializeMultisig { m: 1 }; let packed = check.pack(); let expect = Vec::from([2u8, 1]); assert_eq!(packed, expect); diff --git a/program/src/processor.rs b/program/src/processor.rs index 7633636..a4ce7a0 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -4,7 +4,7 @@ use crate::{ error::TokenError, - instruction::{is_valid_signer_index, AuthorityType, TokenInstruction}, + instruction::{is_valid_signer_index, AuthorityType, TokenInstruction, MAX_SIGNERS}, option::COption, pack::{IsInitialized, Pack}, state::{Account, AccountState, Mint, Multisig}, @@ -99,8 +99,8 @@ impl Processor { Ok(()) } - /// Processes a [DangerInitializeMultisig](enum.TokenInstruction.html) instruction. - pub fn process_danger_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult { + /// Processes a [InitializeMultisig](enum.TokenInstruction.html) instruction. + pub fn process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let multisig_info = next_account_info(account_info_iter)?; let multisig_info_data_len = multisig_info.data_len(); @@ -637,9 +637,9 @@ impl Processor { info!("Instruction: InitializeAccount"); Self::process_initialize_account(accounts) } - TokenInstruction::DangerInitializeMultisig { m } => { - info!("Instruction: DangerInitializeMultisig"); - Self::process_danger_initialize_multisig(accounts, m) + TokenInstruction::InitializeMultisig { m } => { + info!("Instruction: InitializeMultisig"); + Self::process_initialize_multisig(accounts, m) } TokenInstruction::Transfer { amount } => { info!("Instruction: Transfer"); @@ -714,14 +714,18 @@ impl Processor { { let multisig = Multisig::unpack(&owner_account_info.data.borrow())?; let mut num_signers = 0; + let mut matched = [false; MAX_SIGNERS]; for signer in signers.iter() { - if multisig.signers[0..multisig.n as usize].contains(signer.key) { + for (position, key) in multisig.signers[0..multisig.n as usize].iter().enumerate() { + if key == signer.key && !matched[position] { if !signer.is_signer { return Err(ProgramError::MissingRequiredSignature); } + matched[position] = true; num_signers += 1; } } + } if num_signers < multisig.m { return Err(ProgramError::MissingRequiredSignature); } @@ -3600,8 +3604,7 @@ mod tests { assert_eq!( Err(TokenError::NotRentExempt.into()), do_process_instruction( - danger_initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1) - .unwrap(), + initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(), vec![ &mut multisig_account, &mut rent_sysvar, @@ -3615,7 +3618,7 @@ mod tests { // single signer let account_info_iter = &mut signer_accounts.iter_mut(); do_process_instruction( - danger_initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(), + initialize_multisig(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(), vec![ &mut multisig_account, &mut rent_sysvar, @@ -3627,7 +3630,7 @@ mod tests { // multiple signer let account_info_iter = &mut signer_accounts.iter_mut(); do_process_instruction( - danger_initialize_multisig( + initialize_multisig( &program_id, &multisig_delegate_key, &signer_key_refs, @@ -4087,7 +4090,7 @@ mod tests { { let mut multisig = Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap(); - multisig.m = 2; // TODO 11? + multisig.m = 11; multisig.n = 11; Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap(); } @@ -4097,6 +4100,34 @@ mod tests { Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers) ); signers[5].is_signer = true; + + // 11:11, single signer signs multiple times + { + let mut signer_lamports = 0; + let mut signer_data = vec![]; + let signers = vec![ + AccountInfo::new( + &signer_keys[5], + true, + false, + &mut signer_lamports, + &mut signer_data, + &program_id, + false, + Epoch::default(), + ); + MAX_SIGNERS + 1 + ]; + let mut multisig = + Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap(); + multisig.m = 11; + multisig.n = 11; + Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap(); + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers) + ); + } } #[test] From bdf4185a4ddaaf04ac580d292997184f664e053a Mon Sep 17 00:00:00 2001 From: Jack May Date: Mon, 21 Sep 2020 13:13:56 -0700 Subject: [PATCH 068/248] backport #484 to v2 --- program/Cargo.toml | 2 +- program/src/instruction.rs | 3 +- program/src/lib.rs | 2 - program/src/option.rs | 980 ------------------------------------- program/src/pack.rs | 57 --- program/src/processor.rs | 4 +- program/src/state.rs | 15 +- 7 files changed, 12 insertions(+), 1051 deletions(-) delete mode 100644 program/src/option.rs delete mode 100644 program/src/pack.rs diff --git a/program/Cargo.toml b/program/Cargo.toml index 16e174f..9be3b68 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -21,7 +21,7 @@ default = ["solana-sdk/default"] num-derive = "0.3" num-traits = "0.2" remove_dir_all = "=0.5.0" -solana-sdk = { version = "1.3.11", default-features = false, optional = true } +solana-sdk = { version = "1.3.12", default-features = false, optional = true } thiserror = "1.0" arrayref = "0.3.6" num_enum = "0.5.1" diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 5c8e1b0..ae01523 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -1,9 +1,10 @@ //! Instruction types -use crate::{error::TokenError, option::COption}; +use crate::error::TokenError; use solana_sdk::{ instruction::{AccountMeta, Instruction}, program_error::ProgramError, + program_option::COption, pubkey::Pubkey, sysvar, }; diff --git a/program/src/lib.rs b/program/src/lib.rs index 52dc77f..6e7108f 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -7,8 +7,6 @@ pub mod entrypoint; pub mod error; pub mod instruction; pub mod native_mint; -pub mod option; -pub mod pack; pub mod processor; pub mod state; diff --git a/program/src/option.rs b/program/src/option.rs deleted file mode 100644 index 01ba5df..0000000 --- a/program/src/option.rs +++ /dev/null @@ -1,980 +0,0 @@ -//! A C representation of Rust's `std::option::Option` used accross the FFI -//! boundary for Solana program interfaces -//! -//! This implementation mostly matches `std::option` except iterators since the iteration -//! trait requires returning `std::option::Option` - -use std::{ - convert, mem, - ops::{Deref, DerefMut}, -}; - -/// A C representation of Rust's `std::option::Option` -#[repr(C)] -#[derive(Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] -pub enum COption { - /// No value - None, - /// Some value `T` - Some(T), -} - -///////////////////////////////////////////////////////////////////////////// -// Type implementation -///////////////////////////////////////////////////////////////////////////// - -impl COption { - ///////////////////////////////////////////////////////////////////////// - // Querying the contained values - ///////////////////////////////////////////////////////////////////////// - - /// Returns `true` if the option is a [`COption::Some`] value. - /// - /// # Examples - /// - /// ```ignore - /// let x: COption = COption::Some(2); - /// assert_eq!(x.is_some(), true); - /// - /// let x: COption = COption::None; - /// assert_eq!(x.is_some(), false); - /// ``` - /// - /// [`COption::Some`]: #variant.COption::Some - #[must_use = "if you intended to assert that this has a value, consider `.unwrap()` instead"] - #[inline] - pub fn is_some(&self) -> bool { - match *self { - COption::Some(_) => true, - COption::None => false, - } - } - - /// Returns `true` if the option is a [`COption::None`] value. - /// - /// # Examples - /// - /// ```ignore - /// let x: COption = COption::Some(2); - /// assert_eq!(x.is_none(), false); - /// - /// let x: COption = COption::None; - /// assert_eq!(x.is_none(), true); - /// ``` - /// - /// [`COption::None`]: #variant.COption::None - #[must_use = "if you intended to assert that this doesn't have a value, consider \ - `.and_then(|| panic!(\"`COption` had a value when expected `COption::None`\"))` instead"] - #[inline] - pub fn is_none(&self) -> bool { - !self.is_some() - } - - /// Returns `true` if the option is a [`COption::Some`] value containing the given value. - /// - /// # Examples - /// - /// ```ignore - /// #![feature(option_result_contains)] - /// - /// let x: COption = COption::Some(2); - /// assert_eq!(x.contains(&2), true); - /// - /// let x: COption = COption::Some(3); - /// assert_eq!(x.contains(&2), false); - /// - /// let x: COption = COption::None; - /// assert_eq!(x.contains(&2), false); - /// ``` - #[must_use] - #[inline] - pub fn contains(&self, x: &U) -> bool - where - U: PartialEq, - { - match self { - COption::Some(y) => x == y, - COption::None => false, - } - } - - ///////////////////////////////////////////////////////////////////////// - // Adapter for working with references - ///////////////////////////////////////////////////////////////////////// - - /// Converts from `&COption` to `COption<&T>`. - /// - /// # Examples - /// - /// Converts an `COption<`[`String`]`>` into an `COption<`[`usize`]`>`, preserving the original. - /// The [`map`] method takes the `self` argument by value, consuming the original, - /// so this technique uses `as_ref` to first take an `COption` to a reference - /// to the value inside the original. - /// - /// [`map`]: enum.COption.html#method.map - /// [`String`]: ../../std/string/struct.String.html - /// [`usize`]: ../../std/primitive.usize.html - /// - /// ```ignore - /// let text: COption = COption::Some("Hello, world!".to_string()); - /// // First, cast `COption` to `COption<&String>` with `as_ref`, - /// // then consume *that* with `map`, leaving `text` on the stack. - /// let text_length: COption = text.as_ref().map(|s| s.len()); - /// println!("still can print text: {:?}", text); - /// ``` - #[inline] - pub fn as_ref(&self) -> COption<&T> { - match *self { - COption::Some(ref x) => COption::Some(x), - COption::None => COption::None, - } - } - - /// Converts from `&mut COption` to `COption<&mut T>`. - /// - /// # Examples - /// - /// ```ignore - /// let mut x = COption::Some(2); - /// match x.as_mut() { - /// COption::Some(v) => *v = 42, - /// COption::None => {}, - /// } - /// assert_eq!(x, COption::Some(42)); - /// ``` - #[inline] - pub fn as_mut(&mut self) -> COption<&mut T> { - match *self { - COption::Some(ref mut x) => COption::Some(x), - COption::None => COption::None, - } - } - - ///////////////////////////////////////////////////////////////////////// - // Getting to contained values - ///////////////////////////////////////////////////////////////////////// - - /// Unwraps an option, yielding the content of a [`COption::Some`]. - /// - /// # Panics - /// - /// Panics if the value is a [`COption::None`] with a custom panic message provided by - /// `msg`. - /// - /// [`COption::Some`]: #variant.COption::Some - /// [`COption::None`]: #variant.COption::None - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some("value"); - /// assert_eq!(x.expect("the world is ending"), "value"); - /// ``` - /// - /// ```ignore{.should_panic} - /// let x: COption<&str> = COption::None; - /// x.expect("the world is ending"); // panics with `the world is ending` - /// ``` - #[inline] - pub fn expect(self, msg: &str) -> T { - match self { - COption::Some(val) => val, - COption::None => expect_failed(msg), - } - } - - /// Moves the value `v` out of the `COption` if it is [`COption::Some(v)`]. - /// - /// In general, because this function may panic, its use is discouraged. - /// Instead, prefer to use pattern matching and handle the [`COption::None`] - /// case explicitly. - /// - /// # Panics - /// - /// Panics if the self value equals [`COption::None`]. - /// - /// [`COption::Some(v)`]: #variant.COption::Some - /// [`COption::None`]: #variant.COption::None - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some("air"); - /// assert_eq!(x.unwrap(), "air"); - /// ``` - /// - /// ```ignore{.should_panic} - /// let x: COption<&str> = COption::None; - /// assert_eq!(x.unwrap(), "air"); // fails - /// ``` - #[inline] - pub fn unwrap(self) -> T { - match self { - COption::Some(val) => val, - COption::None => panic!("called `COption::unwrap()` on a `COption::None` value"), - } - } - - /// Returns the contained value or a default. - /// - /// Arguments passed to `unwrap_or` are eagerly evaluated; if you are passing - /// the result of a function call, it is recommended to use [`unwrap_or_else`], - /// which is lazily evaluated. - /// - /// [`unwrap_or_else`]: #method.unwrap_or_else - /// - /// # Examples - /// - /// ```ignore - /// assert_eq!(COption::Some("car").unwrap_or("bike"), "car"); - /// assert_eq!(COption::None.unwrap_or("bike"), "bike"); - /// ``` - #[inline] - pub fn unwrap_or(self, def: T) -> T { - match self { - COption::Some(x) => x, - COption::None => def, - } - } - - /// Returns the contained value or computes it from a closure. - /// - /// # Examples - /// - /// ```ignore - /// let k = 10; - /// assert_eq!(COption::Some(4).unwrap_or_else(|| 2 * k), 4); - /// assert_eq!(COption::None.unwrap_or_else(|| 2 * k), 20); - /// ``` - #[inline] - pub fn unwrap_or_else T>(self, f: F) -> T { - match self { - COption::Some(x) => x, - COption::None => f(), - } - } - - ///////////////////////////////////////////////////////////////////////// - // Transforming contained values - ///////////////////////////////////////////////////////////////////////// - - /// Maps an `COption` to `COption` by applying a function to a contained value. - /// - /// # Examples - /// - /// Converts an `COption<`[`String`]`>` into an `COption<`[`usize`]`>`, consuming the original: - /// - /// [`String`]: ../../std/string/struct.String.html - /// [`usize`]: ../../std/primitive.usize.html - /// - /// ```ignore - /// let maybe_some_string = COption::Some(String::from("Hello, World!")); - /// // `COption::map` takes self *by value*, consuming `maybe_some_string` - /// let maybe_some_len = maybe_some_string.map(|s| s.len()); - /// - /// assert_eq!(maybe_some_len, COption::Some(13)); - /// ``` - #[inline] - pub fn map U>(self, f: F) -> COption { - match self { - COption::Some(x) => COption::Some(f(x)), - COption::None => COption::None, - } - } - - /// Applies a function to the contained value (if any), - /// or returns the provided default (if not). - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some("foo"); - /// assert_eq!(x.map_or(42, |v| v.len()), 3); - /// - /// let x: COption<&str> = COption::None; - /// assert_eq!(x.map_or(42, |v| v.len()), 42); - /// ``` - #[inline] - pub fn map_or U>(self, default: U, f: F) -> U { - match self { - COption::Some(t) => f(t), - COption::None => default, - } - } - - /// Applies a function to the contained value (if any), - /// or computes a default (if not). - /// - /// # Examples - /// - /// ```ignore - /// let k = 21; - /// - /// let x = COption::Some("foo"); - /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 3); - /// - /// let x: COption<&str> = COption::None; - /// assert_eq!(x.map_or_else(|| 2 * k, |v| v.len()), 42); - /// ``` - #[inline] - pub fn map_or_else U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U { - match self { - COption::Some(t) => f(t), - COption::None => default(), - } - } - - /// Transforms the `COption` into a [`Result`], mapping [`COption::Some(v)`] to - /// [`Ok(v)`] and [`COption::None`] to [`Err(err)`]. - /// - /// Arguments passed to `ok_or` are eagerly evaluated; if you are passing the - /// result of a function call, it is recommended to use [`ok_or_else`], which is - /// lazily evaluated. - /// - /// [`Result`]: ../../std/result/enum.Result.html - /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok - /// [`Err(err)`]: ../../std/result/enum.Result.html#variant.Err - /// [`COption::None`]: #variant.COption::None - /// [`COption::Some(v)`]: #variant.COption::Some - /// [`ok_or_else`]: #method.ok_or_else - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some("foo"); - /// assert_eq!(x.ok_or(0), Ok("foo")); - /// - /// let x: COption<&str> = COption::None; - /// assert_eq!(x.ok_or(0), Err(0)); - /// ``` - #[inline] - pub fn ok_or(self, err: E) -> Result { - match self { - COption::Some(v) => Ok(v), - COption::None => Err(err), - } - } - - /// Transforms the `COption` into a [`Result`], mapping [`COption::Some(v)`] to - /// [`Ok(v)`] and [`COption::None`] to [`Err(err())`]. - /// - /// [`Result`]: ../../std/result/enum.Result.html - /// [`Ok(v)`]: ../../std/result/enum.Result.html#variant.Ok - /// [`Err(err())`]: ../../std/result/enum.Result.html#variant.Err - /// [`COption::None`]: #variant.COption::None - /// [`COption::Some(v)`]: #variant.COption::Some - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some("foo"); - /// assert_eq!(x.ok_or_else(|| 0), Ok("foo")); - /// - /// let x: COption<&str> = COption::None; - /// assert_eq!(x.ok_or_else(|| 0), Err(0)); - /// ``` - #[inline] - pub fn ok_or_else E>(self, err: F) -> Result { - match self { - COption::Some(v) => Ok(v), - COption::None => Err(err()), - } - } - - ///////////////////////////////////////////////////////////////////////// - // Boolean operations on the values, eager and lazy - ///////////////////////////////////////////////////////////////////////// - - /// Returns [`COption::None`] if the option is [`COption::None`], otherwise returns `optb`. - /// - /// [`COption::None`]: #variant.COption::None - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some(2); - /// let y: COption<&str> = COption::None; - /// assert_eq!(x.and(y), COption::None); - /// - /// let x: COption = COption::None; - /// let y = COption::Some("foo"); - /// assert_eq!(x.and(y), COption::None); - /// - /// let x = COption::Some(2); - /// let y = COption::Some("foo"); - /// assert_eq!(x.and(y), COption::Some("foo")); - /// - /// let x: COption = COption::None; - /// let y: COption<&str> = COption::None; - /// assert_eq!(x.and(y), COption::None); - /// ``` - #[inline] - pub fn and(self, optb: COption) -> COption { - match self { - COption::Some(_) => optb, - COption::None => COption::None, - } - } - - /// Returns [`COption::None`] if the option is [`COption::None`], otherwise calls `f` with the - /// wrapped value and returns the result. - /// - /// COption::Some languages call this operation flatmap. - /// - /// [`COption::None`]: #variant.COption::None - /// - /// # Examples - /// - /// ```ignore - /// fn sq(x: u32) -> COption { COption::Some(x * x) } - /// fn nope(_: u32) -> COption { COption::None } - /// - /// assert_eq!(COption::Some(2).and_then(sq).and_then(sq), COption::Some(16)); - /// assert_eq!(COption::Some(2).and_then(sq).and_then(nope), COption::None); - /// assert_eq!(COption::Some(2).and_then(nope).and_then(sq), COption::None); - /// assert_eq!(COption::None.and_then(sq).and_then(sq), COption::None); - /// ``` - #[inline] - pub fn and_then COption>(self, f: F) -> COption { - match self { - COption::Some(x) => f(x), - COption::None => COption::None, - } - } - - /// Returns [`COption::None`] if the option is [`COption::None`], otherwise calls `predicate` - /// with the wrapped value and returns: - /// - /// - [`COption::Some(t)`] if `predicate` returns `true` (where `t` is the wrapped - /// value), and - /// - [`COption::None`] if `predicate` returns `false`. - /// - /// This function works similar to [`Iterator::filter()`]. You can imagine - /// the `COption` being an iterator over one or zero elements. `filter()` - /// lets you decide which elements to keep. - /// - /// # Examples - /// - /// ```ignore - /// fn is_even(n: &i32) -> bool { - /// n % 2 == 0 - /// } - /// - /// assert_eq!(COption::None.filter(is_even), COption::None); - /// assert_eq!(COption::Some(3).filter(is_even), COption::None); - /// assert_eq!(COption::Some(4).filter(is_even), COption::Some(4)); - /// ``` - /// - /// [`COption::None`]: #variant.COption::None - /// [`COption::Some(t)`]: #variant.COption::Some - /// [`Iterator::filter()`]: ../../std/iter/trait.Iterator.html#method.filter - #[inline] - pub fn filter bool>(self, predicate: P) -> Self { - if let COption::Some(x) = self { - if predicate(&x) { - return COption::Some(x); - } - } - COption::None - } - - /// Returns the option if it contains a value, otherwise returns `optb`. - /// - /// Arguments passed to `or` are eagerly evaluated; if you are passing the - /// result of a function call, it is recommended to use [`or_else`], which is - /// lazily evaluated. - /// - /// [`or_else`]: #method.or_else - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some(2); - /// let y = COption::None; - /// assert_eq!(x.or(y), COption::Some(2)); - /// - /// let x = COption::None; - /// let y = COption::Some(100); - /// assert_eq!(x.or(y), COption::Some(100)); - /// - /// let x = COption::Some(2); - /// let y = COption::Some(100); - /// assert_eq!(x.or(y), COption::Some(2)); - /// - /// let x: COption = COption::None; - /// let y = COption::None; - /// assert_eq!(x.or(y), COption::None); - /// ``` - #[inline] - pub fn or(self, optb: COption) -> COption { - match self { - COption::Some(_) => self, - COption::None => optb, - } - } - - /// Returns the option if it contains a value, otherwise calls `f` and - /// returns the result. - /// - /// # Examples - /// - /// ```ignore - /// fn nobody() -> COption<&'static str> { COption::None } - /// fn vikings() -> COption<&'static str> { COption::Some("vikings") } - /// - /// assert_eq!(COption::Some("barbarians").or_else(vikings), COption::Some("barbarians")); - /// assert_eq!(COption::None.or_else(vikings), COption::Some("vikings")); - /// assert_eq!(COption::None.or_else(nobody), COption::None); - /// ``` - #[inline] - pub fn or_else COption>(self, f: F) -> COption { - match self { - COption::Some(_) => self, - COption::None => f(), - } - } - - /// Returns [`COption::Some`] if exactly one of `self`, `optb` is [`COption::Some`], otherwise returns [`COption::None`]. - /// - /// [`COption::Some`]: #variant.COption::Some - /// [`COption::None`]: #variant.COption::None - /// - /// # Examples - /// - /// ```ignore - /// let x = COption::Some(2); - /// let y: COption = COption::None; - /// assert_eq!(x.xor(y), COption::Some(2)); - /// - /// let x: COption = COption::None; - /// let y = COption::Some(2); - /// assert_eq!(x.xor(y), COption::Some(2)); - /// - /// let x = COption::Some(2); - /// let y = COption::Some(2); - /// assert_eq!(x.xor(y), COption::None); - /// - /// let x: COption = COption::None; - /// let y: COption = COption::None; - /// assert_eq!(x.xor(y), COption::None); - /// ``` - #[inline] - pub fn xor(self, optb: COption) -> COption { - match (self, optb) { - (COption::Some(a), COption::None) => COption::Some(a), - (COption::None, COption::Some(b)) => COption::Some(b), - _ => COption::None, - } - } - - ///////////////////////////////////////////////////////////////////////// - // Entry-like operations to insert if COption::None and return a reference - ///////////////////////////////////////////////////////////////////////// - - /// Inserts `v` into the option if it is [`COption::None`], then - /// returns a mutable reference to the contained value. - /// - /// [`COption::None`]: #variant.COption::None - /// - /// # Examples - /// - /// ```ignore - /// let mut x = COption::None; - /// - /// { - /// let y: &mut u32 = x.get_or_insert(5); - /// assert_eq!(y, &5); - /// - /// *y = 7; - /// } - /// - /// assert_eq!(x, COption::Some(7)); - /// ``` - #[inline] - pub fn get_or_insert(&mut self, v: T) -> &mut T { - self.get_or_insert_with(|| v) - } - - /// Inserts a value computed from `f` into the option if it is [`COption::None`], then - /// returns a mutable reference to the contained value. - /// - /// [`COption::None`]: #variant.COption::None - /// - /// # Examples - /// - /// ```ignore - /// let mut x = COption::None; - /// - /// { - /// let y: &mut u32 = x.get_or_insert_with(|| 5); - /// assert_eq!(y, &5); - /// - /// *y = 7; - /// } - /// - /// assert_eq!(x, COption::Some(7)); - /// ``` - #[inline] - pub fn get_or_insert_with T>(&mut self, f: F) -> &mut T { - if let COption::None = *self { - *self = COption::Some(f()) - } - - match *self { - COption::Some(ref mut v) => v, - COption::None => unreachable!(), - } - } - - ///////////////////////////////////////////////////////////////////////// - // Misc - ///////////////////////////////////////////////////////////////////////// - - /// Replaces the actual value in the option by the value given in parameter, - /// returning the old value if present, - /// leaving a [`COption::Some`] in its place without deinitializing either one. - /// - /// [`COption::Some`]: #variant.COption::Some - /// - /// # Examples - /// - /// ```ignore - /// let mut x = COption::Some(2); - /// let old = x.replace(5); - /// assert_eq!(x, COption::Some(5)); - /// assert_eq!(old, COption::Some(2)); - /// - /// let mut x = COption::None; - /// let old = x.replace(3); - /// assert_eq!(x, COption::Some(3)); - /// assert_eq!(old, COption::None); - /// ``` - #[inline] - pub fn replace(&mut self, value: T) -> COption { - mem::replace(self, COption::Some(value)) - } -} - -impl COption<&T> { - /// Maps an `COption<&T>` to an `COption` by copying the contents of the - /// option. - /// - /// # Examples - /// - /// ```ignore - /// let x = 12; - /// let opt_x = COption::Some(&x); - /// assert_eq!(opt_x, COption::Some(&12)); - /// let copied = opt_x.copied(); - /// assert_eq!(copied, COption::Some(12)); - /// ``` - pub fn copied(self) -> COption { - self.map(|&t| t) - } -} - -impl COption<&mut T> { - /// Maps an `COption<&mut T>` to an `COption` by copying the contents of the - /// option. - /// - /// # Examples - /// - /// ```ignore - /// let mut x = 12; - /// let opt_x = COption::Some(&mut x); - /// assert_eq!(opt_x, COption::Some(&mut 12)); - /// let copied = opt_x.copied(); - /// assert_eq!(copied, COption::Some(12)); - /// ``` - pub fn copied(self) -> COption { - self.map(|&mut t| t) - } -} - -impl COption<&T> { - /// Maps an `COption<&T>` to an `COption` by cloning the contents of the - /// option. - /// - /// # Examples - /// - /// ```ignore - /// let x = 12; - /// let opt_x = COption::Some(&x); - /// assert_eq!(opt_x, COption::Some(&12)); - /// let cloned = opt_x.cloned(); - /// assert_eq!(cloned, COption::Some(12)); - /// ``` - pub fn cloned(self) -> COption { - self.map(|t| t.clone()) - } -} - -impl COption<&mut T> { - /// Maps an `COption<&mut T>` to an `COption` by cloning the contents of the - /// option. - /// - /// # Examples - /// - /// ```ignore - /// let mut x = 12; - /// let opt_x = COption::Some(&mut x); - /// assert_eq!(opt_x, COption::Some(&mut 12)); - /// let cloned = opt_x.cloned(); - /// assert_eq!(cloned, COption::Some(12)); - /// ``` - pub fn cloned(self) -> COption { - self.map(|t| t.clone()) - } -} - -impl COption { - /// Returns the contained value or a default - /// - /// Consumes the `self` argument then, if [`COption::Some`], returns the contained - /// value, otherwise if [`COption::None`], returns the [default value] for that - /// type. - /// - /// # Examples - /// - /// Converts a string to an integer, turning poorly-formed strings - /// into 0 (the default value for integers). [`parse`] converts - /// a string to any other type that implements [`FromStr`], returning - /// [`COption::None`] on error. - /// - /// ```ignore - /// let good_year_from_input = "1909"; - /// let bad_year_from_input = "190blarg"; - /// let good_year = good_year_from_input.parse().ok().unwrap_or_default(); - /// let bad_year = bad_year_from_input.parse().ok().unwrap_or_default(); - /// - /// assert_eq!(1909, good_year); - /// assert_eq!(0, bad_year); - /// ``` - /// - /// [`COption::Some`]: #variant.COption::Some - /// [`COption::None`]: #variant.COption::None - /// [default value]: ../default/trait.Default.html#tymethod.default - /// [`parse`]: ../../std/primitive.str.html#method.parse - /// [`FromStr`]: ../../std/str/trait.FromStr.html - #[inline] - pub fn unwrap_or_default(self) -> T { - match self { - COption::Some(x) => x, - COption::None => Default::default(), - } - } -} - -impl COption { - /// Converts from `COption` (or `&COption`) to `COption<&T::Target>`. - /// - /// Leaves the original COption in-place, creating a new one with a reference - /// to the original one, additionally coercing the contents via [`Deref`]. - /// - /// [`Deref`]: ../../std/ops/trait.Deref.html - /// - /// # Examples - /// - /// ```ignore - /// #![feature(inner_deref)] - /// - /// let x: COption = COption::Some("hey".to_owned()); - /// assert_eq!(x.as_deref(), COption::Some("hey")); - /// - /// let x: COption = COption::None; - /// assert_eq!(x.as_deref(), COption::None); - /// ``` - pub fn as_deref(&self) -> COption<&T::Target> { - self.as_ref().map(|t| t.deref()) - } -} - -impl COption { - /// Converts from `COption` (or `&mut COption`) to `COption<&mut T::Target>`. - /// - /// Leaves the original `COption` in-place, creating a new one containing a mutable reference to - /// the inner type's `Deref::Target` type. - /// - /// # Examples - /// - /// ```ignore - /// #![feature(inner_deref)] - /// - /// let mut x: COption = COption::Some("hey".to_owned()); - /// assert_eq!(x.as_deref_mut().map(|x| { - /// x.make_ascii_uppercase(); - /// x - /// }), COption::Some("HEY".to_owned().as_mut_str())); - /// ``` - pub fn as_deref_mut(&mut self) -> COption<&mut T::Target> { - self.as_mut().map(|t| t.deref_mut()) - } -} - -impl COption> { - /// Transposes an `COption` of a [`Result`] into a [`Result`] of an `COption`. - /// - /// [`COption::None`] will be mapped to [`Ok`]`(`[`COption::None`]`)`. - /// [`COption::Some`]`(`[`Ok`]`(_))` and [`COption::Some`]`(`[`Err`]`(_))` will be mapped to - /// [`Ok`]`(`[`COption::Some`]`(_))` and [`Err`]`(_)`. - /// - /// [`COption::None`]: #variant.COption::None - /// [`Ok`]: ../../std/result/enum.Result.html#variant.Ok - /// [`COption::Some`]: #variant.COption::Some - /// [`Err`]: ../../std/result/enum.Result.html#variant.Err - /// - /// # Examples - /// - /// ```ignore - /// #[derive(Debug, Eq, PartialEq)] - /// struct COption::SomeErr; - /// - /// let x: Result, COption::SomeErr> = Ok(COption::Some(5)); - /// let y: COption> = COption::Some(Ok(5)); - /// assert_eq!(x, y.transpose()); - /// ``` - #[inline] - pub fn transpose(self) -> Result, E> { - match self { - COption::Some(Ok(x)) => Ok(COption::Some(x)), - COption::Some(Err(e)) => Err(e), - COption::None => Ok(COption::None), - } - } -} - -// This is a separate function to reduce the code size of .expect() itself. -#[inline(never)] -#[cold] -fn expect_failed(msg: &str) -> ! { - panic!("{}", msg) -} - -// // This is a separate function to reduce the code size of .expect_none() itself. -// #[inline(never)] -// #[cold] -// fn expect_none_failed(msg: &str, value: &dyn fmt::Debug) -> ! { -// panic!("{}: {:?}", msg, value) -// } - -///////////////////////////////////////////////////////////////////////////// -// Trait implementations -///////////////////////////////////////////////////////////////////////////// - -impl Clone for COption { - #[inline] - fn clone(&self) -> Self { - match self { - COption::Some(x) => COption::Some(x.clone()), - COption::None => COption::None, - } - } - - #[inline] - fn clone_from(&mut self, source: &Self) { - match (self, source) { - (COption::Some(to), COption::Some(from)) => to.clone_from(from), - (to, from) => *to = from.clone(), - } - } -} - -impl Default for COption { - /// Returns [`COption::None`][COption::COption::None]. - /// - /// # Examples - /// - /// ```ignore - /// let opt: COption = COption::default(); - /// assert!(opt.is_none()); - /// ``` - #[inline] - fn default() -> COption { - COption::None - } -} - -impl From for COption { - fn from(val: T) -> COption { - COption::Some(val) - } -} - -impl<'a, T> From<&'a COption> for COption<&'a T> { - fn from(o: &'a COption) -> COption<&'a T> { - o.as_ref() - } -} - -impl<'a, T> From<&'a mut COption> for COption<&'a mut T> { - fn from(o: &'a mut COption) -> COption<&'a mut T> { - o.as_mut() - } -} - -impl COption> { - /// Converts from `COption>` to `COption` - /// - /// # Examples - /// Basic usage: - /// ```ignore - /// #![feature(option_flattening)] - /// let x: COption> = COption::Some(COption::Some(6)); - /// assert_eq!(COption::Some(6), x.flatten()); - /// - /// let x: COption> = COption::Some(COption::None); - /// assert_eq!(COption::None, x.flatten()); - /// - /// let x: COption> = COption::None; - /// assert_eq!(COption::None, x.flatten()); - /// ``` - /// Flattening once only removes one level of nesting: - /// ```ignore - /// #![feature(option_flattening)] - /// let x: COption>> = COption::Some(COption::Some(COption::Some(6))); - /// assert_eq!(COption::Some(COption::Some(6)), x.flatten()); - /// assert_eq!(COption::Some(6), x.flatten().flatten()); - /// ``` - #[inline] - pub fn flatten(self) -> COption { - self.and_then(convert::identity) - } -} - -impl From> for COption { - fn from(option: Option) -> Self { - match option { - Some(value) => COption::Some(value), - None => COption::None, - } - } -} - -impl Into> for COption { - fn into(self) -> Option { - match self { - COption::Some(value) => Some(value), - COption::None => None, - } - } -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn test_from_rust_option() { - let option = Some(99u64); - let c_option: COption = option.into(); - assert_eq!(c_option, COption::Some(99u64)); - let expected = c_option.into(); - assert_eq!(option, expected); - - let option = None; - let c_option: COption = option.into(); - assert_eq!(c_option, COption::None); - let expected = c_option.into(); - assert_eq!(option, expected); - } -} diff --git a/program/src/pack.rs b/program/src/pack.rs deleted file mode 100644 index 7c0f355..0000000 --- a/program/src/pack.rs +++ /dev/null @@ -1,57 +0,0 @@ -//! State transition types - -use solana_sdk::program_error::ProgramError; - -/// Check is a token state is initialized -pub trait IsInitialized { - /// Is initialized - fn is_initialized(&self) -> bool; -} - -/// Depends on Sized -pub trait Sealed: Sized {} - -/// Safely and efficiently (de)serialize account state -pub trait Pack: Sealed { - /// The length, in bytes, of the packed representation - const LEN: usize; - #[doc(hidden)] - fn pack_into_slice(&self, dst: &mut [u8]); - #[doc(hidden)] - fn unpack_from_slice(src: &[u8]) -> Result; - - /// Get the packed length - fn get_packed_len() -> usize { - Self::LEN - } - - /// Unpack from slice and check if initialized - fn unpack(input: &[u8]) -> Result - where - Self: IsInitialized, - { - let value = Self::unpack_unchecked(input)?; - if value.is_initialized() { - Ok(value) - } else { - Err(ProgramError::UninitializedAccount) - } - } - - /// Unpack from slice without checking if initialized - fn unpack_unchecked(input: &[u8]) -> Result { - if input.len() != Self::LEN { - return Err(ProgramError::InvalidAccountData); - } - Ok(Self::unpack_from_slice(input)?) - } - - /// Pack into slice - fn pack(src: Self, dst: &mut [u8]) -> Result<(), ProgramError> { - if dst.len() != Self::LEN { - return Err(ProgramError::InvalidAccountData); - } - src.pack_into_slice(dst); - Ok(()) - } -} diff --git a/program/src/processor.rs b/program/src/processor.rs index a4ce7a0..4ab5727 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -5,8 +5,6 @@ use crate::{ error::TokenError, instruction::{is_valid_signer_index, AuthorityType, TokenInstruction, MAX_SIGNERS}, - option::COption, - pack::{IsInitialized, Pack}, state::{Account, AccountState, Mint, Multisig}, }; use num_traits::FromPrimitive; @@ -16,6 +14,8 @@ use solana_sdk::{ entrypoint::ProgramResult, info, program_error::{PrintProgramError, ProgramError}, + program_option::COption, + program_pack::{IsInitialized, Pack}, pubkey::Pubkey, sysvar::{rent::Rent, Sysvar}, }; diff --git a/program/src/state.rs b/program/src/state.rs index 5d14af7..3622471 100644 --- a/program/src/state.rs +++ b/program/src/state.rs @@ -1,15 +1,14 @@ //! State transition types -use crate::{ - instruction::MAX_SIGNERS, - option::COption, - pack::{IsInitialized, Pack, Sealed}, -}; +use crate::instruction::MAX_SIGNERS; use arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs}; use num_enum::TryFromPrimitive; -use solana_sdk::{program_error::ProgramError, pubkey::Pubkey}; - -impl Sealed for Option {} +use solana_sdk::{ + program_error::ProgramError, + program_option::COption, + program_pack::{IsInitialized, Pack, Sealed}, + pubkey::Pubkey, +}; /// Mint data. #[repr(C)] From 7fa6d0e9eb202834abd5d0e9a2bb5d28c1d2531b Mon Sep 17 00:00:00 2001 From: Jack May Date: Mon, 21 Sep 2020 13:14:13 -0700 Subject: [PATCH 069/248] cargo fmt --- program/src/processor.rs | 874 +++++++++++++++++++-------------------- 1 file changed, 437 insertions(+), 437 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index 4ab5727..dcc7c7d 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -36,22 +36,22 @@ impl Processor { let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?; let mut mint = Mint::unpack_unchecked(&mint_info.data.borrow())?; - if mint.is_initialized { - return Err(TokenError::AlreadyInUse.into()); - } + if mint.is_initialized { + return Err(TokenError::AlreadyInUse.into()); + } - if !rent.is_exempt(mint_info.lamports(), mint_data_len) { - return Err(TokenError::NotRentExempt.into()); - } + if !rent.is_exempt(mint_info.lamports(), mint_data_len) { + return Err(TokenError::NotRentExempt.into()); + } - mint.mint_authority = COption::Some(mint_authority); - mint.decimals = decimals; - mint.is_initialized = true; - mint.freeze_authority = freeze_authority; + mint.mint_authority = COption::Some(mint_authority); + mint.decimals = decimals; + mint.is_initialized = true; + mint.freeze_authority = freeze_authority; Mint::pack(mint, &mut mint_info.data.borrow_mut())?; - Ok(()) + Ok(()) } /// Processes an [InitializeAccount](enum.TokenInstruction.html) instruction. @@ -64,39 +64,39 @@ impl Processor { let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?; let mut account = Account::unpack_unchecked(&new_account_info.data.borrow())?; - if account.is_initialized() { - return Err(TokenError::AlreadyInUse.into()); - } + if account.is_initialized() { + return Err(TokenError::AlreadyInUse.into()); + } - if !rent.is_exempt(new_account_info.lamports(), new_account_info_data_len) { - return Err(TokenError::NotRentExempt.into()); - } + if !rent.is_exempt(new_account_info.lamports(), new_account_info_data_len) { + return Err(TokenError::NotRentExempt.into()); + } - if *mint_info.key != crate::native_mint::id() { - let _ = Mint::unpack(&mint_info.data.borrow_mut()) - .map_err(|_| Into::::into(TokenError::InvalidMint))?; - } + if *mint_info.key != crate::native_mint::id() { + let _ = Mint::unpack(&mint_info.data.borrow_mut()) + .map_err(|_| Into::::into(TokenError::InvalidMint))?; + } - account.mint = *mint_info.key; - account.owner = *owner_info.key; - account.delegate = COption::None; - account.delegated_amount = 0; - account.state = AccountState::Initialized; - if *mint_info.key == crate::native_mint::id() { - let rent_exempt_reserve = rent.minimum_balance(new_account_info_data_len); - account.is_native = COption::Some(rent_exempt_reserve); - account.amount = new_account_info - .lamports() - .checked_sub(rent_exempt_reserve) - .ok_or(TokenError::Overflow)?; - } else { - account.is_native = COption::None; - account.amount = 0; - }; + account.mint = *mint_info.key; + account.owner = *owner_info.key; + account.delegate = COption::None; + account.delegated_amount = 0; + account.state = AccountState::Initialized; + if *mint_info.key == crate::native_mint::id() { + let rent_exempt_reserve = rent.minimum_balance(new_account_info_data_len); + account.is_native = COption::Some(rent_exempt_reserve); + account.amount = new_account_info + .lamports() + .checked_sub(rent_exempt_reserve) + .ok_or(TokenError::Overflow)?; + } else { + account.is_native = COption::None; + account.amount = 0; + }; Account::pack(account, &mut new_account_info.data.borrow_mut())?; - Ok(()) + Ok(()) } /// Processes a [InitializeMultisig](enum.TokenInstruction.html) instruction. @@ -107,31 +107,31 @@ impl Processor { let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?; let mut multisig = Multisig::unpack_unchecked(&multisig_info.data.borrow())?; - if multisig.is_initialized { - return Err(TokenError::AlreadyInUse.into()); - } + if multisig.is_initialized { + return Err(TokenError::AlreadyInUse.into()); + } - if !rent.is_exempt(multisig_info.lamports(), multisig_info_data_len) { - return Err(TokenError::NotRentExempt.into()); - } + if !rent.is_exempt(multisig_info.lamports(), multisig_info_data_len) { + return Err(TokenError::NotRentExempt.into()); + } - let signer_infos = account_info_iter.as_slice(); - multisig.m = m; - multisig.n = signer_infos.len() as u8; - if !is_valid_signer_index(multisig.n as usize) { - return Err(TokenError::InvalidNumberOfProvidedSigners.into()); - } - if !is_valid_signer_index(multisig.m as usize) { - return Err(TokenError::InvalidNumberOfRequiredSigners.into()); - } - for (i, signer_info) in signer_infos.iter().enumerate() { - multisig.signers[i] = *signer_info.key; - } - multisig.is_initialized = true; + let signer_infos = account_info_iter.as_slice(); + multisig.m = m; + multisig.n = signer_infos.len() as u8; + if !is_valid_signer_index(multisig.n as usize) { + return Err(TokenError::InvalidNumberOfProvidedSigners.into()); + } + if !is_valid_signer_index(multisig.m as usize) { + return Err(TokenError::InvalidNumberOfRequiredSigners.into()); + } + for (i, signer_info) in signer_infos.iter().enumerate() { + multisig.signers[i] = *signer_info.key; + } + multisig.is_initialized = true; Multisig::pack(multisig, &mut multisig_info.data.borrow_mut())?; - Ok(()) + Ok(()) } /// Processes a [Transfer](enum.TokenInstruction.html) instruction. @@ -161,79 +161,79 @@ impl Processor { let mut source_account = Account::unpack(&source_account_info.data.borrow())?; let mut dest_account = Account::unpack(&dest_account_info.data.borrow())?; - if source_account.amount < amount { - return Err(TokenError::InsufficientFunds.into()); - } - if source_account.is_frozen() || dest_account.is_frozen() { - return Err(TokenError::AccountFrozen.into()); - } + if source_account.amount < amount { + return Err(TokenError::InsufficientFunds.into()); + } + if source_account.is_frozen() || dest_account.is_frozen() { + return Err(TokenError::AccountFrozen.into()); + } if source_account.mint != dest_account.mint { return Err(TokenError::MintMismatch.into()); } - if let Some((mint_info, expected_decimals)) = expected_mint_info { - if source_account.mint != *mint_info.key { - return Err(TokenError::MintMismatch.into()); - } - - let mint = Mint::unpack(&mint_info.data.borrow_mut())?; - if expected_decimals != mint.decimals { - return Err(TokenError::MintDecimalsMismatch.into()); - } - } + if let Some((mint_info, expected_decimals)) = expected_mint_info { + if source_account.mint != *mint_info.key { + return Err(TokenError::MintMismatch.into()); + } - match source_account.delegate { - COption::Some(ref delegate) if authority_info.key == delegate => { - Self::validate_owner( - program_id, - delegate, - authority_info, - account_info_iter.as_slice(), - )?; - if source_account.delegated_amount < amount { - return Err(TokenError::InsufficientFunds.into()); - } - source_account.delegated_amount = source_account - .delegated_amount - .checked_sub(amount) - .ok_or(TokenError::Overflow)?; - if source_account.delegated_amount == 0 { - source_account.delegate = COption::None; - } - } - _ => Self::validate_owner( - program_id, - &source_account.owner, - authority_info, - account_info_iter.as_slice(), - )?, - }; + let mint = Mint::unpack(&mint_info.data.borrow_mut())?; + if expected_decimals != mint.decimals { + return Err(TokenError::MintDecimalsMismatch.into()); + } + } - source_account.amount = source_account - .amount + match source_account.delegate { + COption::Some(ref delegate) if authority_info.key == delegate => { + Self::validate_owner( + program_id, + delegate, + authority_info, + account_info_iter.as_slice(), + )?; + if source_account.delegated_amount < amount { + return Err(TokenError::InsufficientFunds.into()); + } + source_account.delegated_amount = source_account + .delegated_amount .checked_sub(amount) .ok_or(TokenError::Overflow)?; - dest_account.amount = dest_account - .amount - .checked_add(amount) - .ok_or(TokenError::Overflow)?; + if source_account.delegated_amount == 0 { + source_account.delegate = COption::None; + } + } + _ => Self::validate_owner( + program_id, + &source_account.owner, + authority_info, + account_info_iter.as_slice(), + )?, + }; - if source_account.is_native() { - let source_starting_lamports = source_account_info.lamports(); - **source_account_info.lamports.borrow_mut() = source_starting_lamports - .checked_sub(amount) - .ok_or(TokenError::Overflow)?; + source_account.amount = source_account + .amount + .checked_sub(amount) + .ok_or(TokenError::Overflow)?; + dest_account.amount = dest_account + .amount + .checked_add(amount) + .ok_or(TokenError::Overflow)?; + + if source_account.is_native() { + let source_starting_lamports = source_account_info.lamports(); + **source_account_info.lamports.borrow_mut() = source_starting_lamports + .checked_sub(amount) + .ok_or(TokenError::Overflow)?; - let dest_starting_lamports = dest_account_info.lamports(); - **dest_account_info.lamports.borrow_mut() = dest_starting_lamports - .checked_add(amount) - .ok_or(TokenError::Overflow)?; - } + let dest_starting_lamports = dest_account_info.lamports(); + **dest_account_info.lamports.borrow_mut() = dest_starting_lamports + .checked_add(amount) + .ok_or(TokenError::Overflow)?; + } Account::pack(source_account, &mut source_account_info.data.borrow_mut())?; Account::pack(dest_account, &mut dest_account_info.data.borrow_mut())?; - Ok(()) + Ok(()) } /// Processes an [Approve](enum.TokenInstruction.html) instruction. @@ -256,34 +256,34 @@ impl Processor { let mut source_account = Account::unpack(&source_account_info.data.borrow())?; - if source_account.is_frozen() { - return Err(TokenError::AccountFrozen.into()); - } + if source_account.is_frozen() { + return Err(TokenError::AccountFrozen.into()); + } - if let Some((mint_info, expected_decimals)) = expected_mint_info { - if source_account.mint != *mint_info.key { - return Err(TokenError::MintMismatch.into()); - } + if let Some((mint_info, expected_decimals)) = expected_mint_info { + if source_account.mint != *mint_info.key { + return Err(TokenError::MintMismatch.into()); + } - let mint = Mint::unpack(&mint_info.data.borrow_mut())?; - if expected_decimals != mint.decimals { - return Err(TokenError::MintDecimalsMismatch.into()); - } + let mint = Mint::unpack(&mint_info.data.borrow_mut())?; + if expected_decimals != mint.decimals { + return Err(TokenError::MintDecimalsMismatch.into()); } + } - Self::validate_owner( - program_id, - &source_account.owner, - owner_info, - account_info_iter.as_slice(), - )?; + Self::validate_owner( + program_id, + &source_account.owner, + owner_info, + account_info_iter.as_slice(), + )?; - source_account.delegate = COption::Some(*delegate_info.key); - source_account.delegated_amount = amount; + source_account.delegate = COption::Some(*delegate_info.key); + source_account.delegated_amount = amount; Account::pack(source_account, &mut source_account_info.data.borrow_mut())?; - Ok(()) + Ok(()) } /// Processes an [Revoke](enum.TokenInstruction.html) instruction. @@ -293,25 +293,25 @@ impl Processor { let mut source_account = Account::unpack(&source_account_info.data.borrow())?; - let owner_info = next_account_info(account_info_iter)?; + let owner_info = next_account_info(account_info_iter)?; - if source_account.is_frozen() { - return Err(TokenError::AccountFrozen.into()); - } + if source_account.is_frozen() { + return Err(TokenError::AccountFrozen.into()); + } - Self::validate_owner( - program_id, - &source_account.owner, - owner_info, - account_info_iter.as_slice(), - )?; + Self::validate_owner( + program_id, + &source_account.owner, + owner_info, + account_info_iter.as_slice(), + )?; - source_account.delegate = COption::None; - source_account.delegated_amount = 0; + source_account.delegate = COption::None; + source_account.delegated_amount = 0; Account::pack(source_account, &mut source_account_info.data.borrow_mut())?; - Ok(()) + Ok(()) } /// Processes a [SetAuthority](enum.TokenInstruction.html) instruction. @@ -328,75 +328,75 @@ impl Processor { if account_info.data_len() == Account::get_packed_len() { let mut account = Account::unpack(&account_info.data.borrow())?; - if account.is_frozen() { - return Err(TokenError::AccountFrozen.into()); - } + if account.is_frozen() { + return Err(TokenError::AccountFrozen.into()); + } - match authority_type { - AuthorityType::AccountOwner => { - Self::validate_owner( - program_id, - &account.owner, - authority_info, - account_info_iter.as_slice(), - )?; - - if let COption::Some(authority) = new_authority { - account.owner = authority; - } else { - return Err(TokenError::InvalidInstruction.into()); - } - } - AuthorityType::CloseAccount => { - let authority = account.close_authority.unwrap_or(account.owner); - Self::validate_owner( - program_id, - &authority, - authority_info, - account_info_iter.as_slice(), - )?; - account.close_authority = new_authority; - } - _ => { - return Err(TokenError::AuthorityTypeNotSupported.into()); + match authority_type { + AuthorityType::AccountOwner => { + Self::validate_owner( + program_id, + &account.owner, + authority_info, + account_info_iter.as_slice(), + )?; + + if let COption::Some(authority) = new_authority { + account.owner = authority; + } else { + return Err(TokenError::InvalidInstruction.into()); } } + AuthorityType::CloseAccount => { + let authority = account.close_authority.unwrap_or(account.owner); + Self::validate_owner( + program_id, + &authority, + authority_info, + account_info_iter.as_slice(), + )?; + account.close_authority = new_authority; + } + _ => { + return Err(TokenError::AuthorityTypeNotSupported.into()); + } + } Account::pack(account, &mut account_info.data.borrow_mut())?; } else if account_info.data_len() == Mint::get_packed_len() { let mut mint = Mint::unpack(&account_info.data.borrow())?; - match authority_type { - AuthorityType::MintTokens => { - // Once a mint's supply is fixed, it cannot be undone by setting a new - // mint_authority - let mint_authority = mint - .mint_authority - .ok_or(Into::::into(TokenError::FixedSupply))?; - Self::validate_owner( - program_id, - &mint_authority, - authority_info, - account_info_iter.as_slice(), - )?; - mint.mint_authority = new_authority; - } - AuthorityType::FreezeAccount => { - // Once a mint's freeze authority is disabled, it cannot be re-enabled by - // setting a new freeze_authority - let freeze_authority = mint - .freeze_authority - .ok_or(Into::::into(TokenError::MintCannotFreeze))?; - Self::validate_owner( - program_id, - &freeze_authority, - authority_info, - account_info_iter.as_slice(), - )?; - mint.freeze_authority = new_authority; - } - _ => { - return Err(TokenError::AuthorityTypeNotSupported.into()); - } + match authority_type { + AuthorityType::MintTokens => { + // Once a mint's supply is fixed, it cannot be undone by setting a new + // mint_authority + let mint_authority = mint + .mint_authority + .ok_or(Into::::into(TokenError::FixedSupply))?; + Self::validate_owner( + program_id, + &mint_authority, + authority_info, + account_info_iter.as_slice(), + )?; + mint.mint_authority = new_authority; + } + AuthorityType::FreezeAccount => { + // Once a mint's freeze authority is disabled, it cannot be re-enabled by + // setting a new freeze_authority + let freeze_authority = mint + .freeze_authority + .ok_or(Into::::into(TokenError::MintCannotFreeze))?; + Self::validate_owner( + program_id, + &freeze_authority, + authority_info, + account_info_iter.as_slice(), + )?; + mint.freeze_authority = new_authority; + } + _ => { + return Err(TokenError::AuthorityTypeNotSupported.into()); } + } Mint::pack(mint, &mut account_info.data.borrow_mut())?; } else { return Err(ProgramError::InvalidArgument); @@ -418,48 +418,48 @@ impl Processor { let owner_info = next_account_info(account_info_iter)?; let mut dest_account = Account::unpack(&dest_account_info.data.borrow())?; - if dest_account.is_frozen() { - return Err(TokenError::AccountFrozen.into()); - } + if dest_account.is_frozen() { + return Err(TokenError::AccountFrozen.into()); + } - if dest_account.is_native() { - return Err(TokenError::NativeNotSupported.into()); - } - if mint_info.key != &dest_account.mint { - return Err(TokenError::MintMismatch.into()); - } + if dest_account.is_native() { + return Err(TokenError::NativeNotSupported.into()); + } + if mint_info.key != &dest_account.mint { + return Err(TokenError::MintMismatch.into()); + } let mut mint = Mint::unpack(&mint_info.data.borrow())?; - if let Some(expected_decimals) = expected_decimals { - if expected_decimals != mint.decimals { - return Err(TokenError::MintDecimalsMismatch.into()); - } - } + if let Some(expected_decimals) = expected_decimals { + if expected_decimals != mint.decimals { + return Err(TokenError::MintDecimalsMismatch.into()); + } + } - match mint.mint_authority { - COption::Some(mint_authority) => Self::validate_owner( - program_id, - &mint_authority, - owner_info, - account_info_iter.as_slice(), - )?, - COption::None => return Err(TokenError::FixedSupply.into()), - } + match mint.mint_authority { + COption::Some(mint_authority) => Self::validate_owner( + program_id, + &mint_authority, + owner_info, + account_info_iter.as_slice(), + )?, + COption::None => return Err(TokenError::FixedSupply.into()), + } - dest_account.amount = dest_account - .amount - .checked_add(amount) - .ok_or(TokenError::Overflow)?; + dest_account.amount = dest_account + .amount + .checked_add(amount) + .ok_or(TokenError::Overflow)?; - mint.supply = mint - .supply - .checked_add(amount) - .ok_or(TokenError::Overflow)?; + mint.supply = mint + .supply + .checked_add(amount) + .ok_or(TokenError::Overflow)?; Account::pack(dest_account, &mut dest_account_info.data.borrow_mut())?; Mint::pack(mint, &mut mint_info.data.borrow_mut())?; - Ok(()) + Ok(()) } /// Processes a [Burn](enum.TokenInstruction.html) instruction. @@ -478,66 +478,66 @@ impl Processor { let mut source_account = Account::unpack(&source_account_info.data.borrow())?; let mut mint = Mint::unpack(&mint_info.data.borrow())?; - if source_account.is_native() { - return Err(TokenError::NativeNotSupported.into()); - } - if mint_info.key != &source_account.mint { - return Err(TokenError::MintMismatch.into()); - } - if source_account.amount < amount { - return Err(TokenError::InsufficientFunds.into()); - } - if source_account.is_frozen() { - return Err(TokenError::AccountFrozen.into()); - } + if source_account.is_native() { + return Err(TokenError::NativeNotSupported.into()); + } + if mint_info.key != &source_account.mint { + return Err(TokenError::MintMismatch.into()); + } + if source_account.amount < amount { + return Err(TokenError::InsufficientFunds.into()); + } + if source_account.is_frozen() { + return Err(TokenError::AccountFrozen.into()); + } - if let Some(expected_decimals) = expected_decimals { - if expected_decimals != mint.decimals { - return Err(TokenError::MintDecimalsMismatch.into()); - } - } + if let Some(expected_decimals) = expected_decimals { + if expected_decimals != mint.decimals { + return Err(TokenError::MintDecimalsMismatch.into()); + } + } - match source_account.delegate { - COption::Some(ref delegate) if authority_info.key == delegate => { - Self::validate_owner( - program_id, - delegate, - authority_info, - account_info_iter.as_slice(), - )?; - - if source_account.delegated_amount < amount { - return Err(TokenError::InsufficientFunds.into()); - } - source_account.delegated_amount = source_account - .delegated_amount - .checked_sub(amount) - .ok_or(TokenError::Overflow)?; - if source_account.delegated_amount == 0 { - source_account.delegate = COption::None; - } - } - _ => Self::validate_owner( - program_id, - &source_account.owner, - authority_info, - account_info_iter.as_slice(), - )?, - } + match source_account.delegate { + COption::Some(ref delegate) if authority_info.key == delegate => { + Self::validate_owner( + program_id, + delegate, + authority_info, + account_info_iter.as_slice(), + )?; - source_account.amount = source_account - .amount - .checked_sub(amount) - .ok_or(TokenError::Overflow)?; - mint.supply = mint - .supply + if source_account.delegated_amount < amount { + return Err(TokenError::InsufficientFunds.into()); + } + source_account.delegated_amount = source_account + .delegated_amount .checked_sub(amount) .ok_or(TokenError::Overflow)?; + if source_account.delegated_amount == 0 { + source_account.delegate = COption::None; + } + } + _ => Self::validate_owner( + program_id, + &source_account.owner, + authority_info, + account_info_iter.as_slice(), + )?, + } + + source_account.amount = source_account + .amount + .checked_sub(amount) + .ok_or(TokenError::Overflow)?; + mint.supply = mint + .supply + .checked_sub(amount) + .ok_or(TokenError::Overflow)?; Account::pack(source_account, &mut source_account_info.data.borrow_mut())?; Mint::pack(mint, &mut mint_info.data.borrow_mut())?; - Ok(()) + Ok(()) } /// Processes a [CloseAccount](enum.TokenInstruction.html) instruction. @@ -548,31 +548,31 @@ impl Processor { let authority_info = next_account_info(account_info_iter)?; let mut source_account = Account::unpack(&source_account_info.data.borrow())?; - if !source_account.is_native() && source_account.amount != 0 { - return Err(TokenError::NonNativeHasBalance.into()); - } + if !source_account.is_native() && source_account.amount != 0 { + return Err(TokenError::NonNativeHasBalance.into()); + } - let authority = source_account - .close_authority - .unwrap_or(source_account.owner); - Self::validate_owner( - program_id, - &authority, - authority_info, - account_info_iter.as_slice(), - )?; + let authority = source_account + .close_authority + .unwrap_or(source_account.owner); + Self::validate_owner( + program_id, + &authority, + authority_info, + account_info_iter.as_slice(), + )?; - let dest_starting_lamports = dest_account_info.lamports(); - **dest_account_info.lamports.borrow_mut() = dest_starting_lamports - .checked_add(source_account_info.lamports()) - .ok_or(TokenError::Overflow)?; + let dest_starting_lamports = dest_account_info.lamports(); + **dest_account_info.lamports.borrow_mut() = dest_starting_lamports + .checked_add(source_account_info.lamports()) + .ok_or(TokenError::Overflow)?; - **source_account_info.lamports.borrow_mut() = 0; - source_account.amount = 0; + **source_account_info.lamports.borrow_mut() = 0; + source_account.amount = 0; Account::pack(source_account, &mut source_account_info.data.borrow_mut())?; - Ok(()) + Ok(()) } /// Processes a [FreezeAccount](enum.TokenInstruction.html) or a @@ -588,36 +588,36 @@ impl Processor { let authority_info = next_account_info(account_info_iter)?; let mut source_account = Account::unpack(&source_account_info.data.borrow())?; - if source_account.is_native() { - return Err(TokenError::NativeNotSupported.into()); - } - if mint_info.key != &source_account.mint { - return Err(TokenError::MintMismatch.into()); - } - if freeze && source_account.is_frozen() || !freeze && !source_account.is_frozen() { - return Err(TokenError::InvalidState.into()); - } + if source_account.is_native() { + return Err(TokenError::NativeNotSupported.into()); + } + if mint_info.key != &source_account.mint { + return Err(TokenError::MintMismatch.into()); + } + if freeze && source_account.is_frozen() || !freeze && !source_account.is_frozen() { + return Err(TokenError::InvalidState.into()); + } - let mint = Mint::unpack(&mint_info.data.borrow_mut())?; - match mint.freeze_authority { - COption::Some(authority) => Self::validate_owner( - program_id, - &authority, - authority_info, - account_info_iter.as_slice(), - ), - COption::None => Err(TokenError::MintCannotFreeze.into()), - }?; + let mint = Mint::unpack(&mint_info.data.borrow_mut())?; + match mint.freeze_authority { + COption::Some(authority) => Self::validate_owner( + program_id, + &authority, + authority_info, + account_info_iter.as_slice(), + ), + COption::None => Err(TokenError::MintCannotFreeze.into()), + }?; - source_account.state = if freeze { - AccountState::Frozen - } else { - AccountState::Initialized - }; + source_account.state = if freeze { + AccountState::Frozen + } else { + AccountState::Initialized + }; Account::pack(source_account, &mut source_account_info.data.borrow_mut())?; - Ok(()) + Ok(()) } /// Processes an [Instruction](enum.Instruction.html). @@ -713,9 +713,9 @@ impl Processor { && owner_account_info.data_len() == Multisig::get_packed_len() { let multisig = Multisig::unpack(&owner_account_info.data.borrow())?; - let mut num_signers = 0; + let mut num_signers = 0; let mut matched = [false; MAX_SIGNERS]; - for signer in signers.iter() { + for signer in signers.iter() { for (position, key) in multisig.signers[0..multisig.n as usize].iter().enumerate() { if key == signer.key && !matched[position] { if !signer.is_signer { @@ -726,9 +726,9 @@ impl Processor { } } } - if num_signers < multisig.m { - return Err(ProgramError::MissingRequiredSignature); - } + if num_signers < multisig.m { + return Err(ProgramError::MissingRequiredSignature); + } return Ok(()); } else if !owner_account_info.is_signer { return Err(ProgramError::MissingRequiredSignature); @@ -1010,7 +1010,7 @@ mod tests { ) .unwrap(); let mint = Mint::unpack_unchecked(&mint2_account.data).unwrap(); - assert_eq!(mint.freeze_authority, COption::Some(owner_key)); + assert_eq!(mint.freeze_authority, COption::Some(owner_key)); } #[test] @@ -1219,10 +1219,10 @@ mod tests { // source-delegate transfer let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); - account.amount = 1000; - account.delegated_amount = 1000; - account.delegate = COption::Some(account1_key); - account.owner = owner_key; + account.amount = 1000; + account.delegated_amount = 1000; + account.delegate = COption::Some(account1_key); + account.owner = owner_key; Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); do_process_instruction_dups( @@ -1490,7 +1490,7 @@ mod tests { ) .unwrap(); let mut account = Account::unpack_unchecked(&mismatch_account.data).unwrap(); - account.mint = mint2_key; + account.mint = mint2_key; Account::pack(account, &mut mismatch_account.data).unwrap(); // mint to account @@ -1718,7 +1718,7 @@ mod tests { .unwrap() } let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, 1000); + assert_eq!(account.amount, 1000); // insufficient funds assert_eq!( @@ -1876,16 +1876,16 @@ mod tests { ) .unwrap(); let mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); - assert_eq!( + assert_eq!( mint, - Mint { - mint_authority: COption::Some(owner_key), - supply: 0, - decimals, - is_initialized: true, - freeze_authority: COption::None, - } - ); + Mint { + mint_authority: COption::Some(owner_key), + supply: 0, + decimals, + is_initialized: true, + freeze_authority: COption::None, + } + ); // create account do_process_instruction( @@ -1907,7 +1907,7 @@ mod tests { .unwrap(); let _ = Mint::unpack(&mut mint_account.data).unwrap(); let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, 42); + assert_eq!(account.amount, 42); // mint to 2, with incorrect decimals assert_eq!( @@ -1929,7 +1929,7 @@ mod tests { let _ = Mint::unpack(&mut mint_account.data).unwrap(); let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, 42); + assert_eq!(account.amount, 42); // mint to 2 do_process_instruction( @@ -1948,7 +1948,7 @@ mod tests { .unwrap(); let _ = Mint::unpack(&mut mint_account.data).unwrap(); let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, 84); + assert_eq!(account.amount, 84); } #[test] @@ -2452,7 +2452,7 @@ mod tests { // set close_authority when currently self let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); - account.close_authority = COption::Some(account1_key); + account.close_authority = COption::Some(account1_key); Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); do_process_instruction_dups( @@ -2865,7 +2865,7 @@ mod tests { // mint_to when mint_authority is account owner let mut mint = Mint::unpack_unchecked(&mint_info.data.borrow()).unwrap(); - mint.mint_authority = COption::Some(account1_key); + mint.mint_authority = COption::Some(account1_key); Mint::pack(mint, &mut mint_info.data.borrow_mut()).unwrap(); do_process_instruction_dups( mint_to( @@ -3003,7 +3003,7 @@ mod tests { ) .unwrap(); let mut account = Account::unpack_unchecked(&mismatch_account.data).unwrap(); - account.mint = mint2_key; + account.mint = mint2_key; Account::pack(account, &mut mismatch_account.data).unwrap(); // mint to @@ -3014,9 +3014,9 @@ mod tests { .unwrap(); let mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); - assert_eq!(mint.supply, 42); + assert_eq!(mint.supply, 42); let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, 42); + assert_eq!(account.amount, 42); // mint to another account to test supply accumulation do_process_instruction( @@ -3026,9 +3026,9 @@ mod tests { .unwrap(); let mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); - assert_eq!(mint.supply, 84); + assert_eq!(mint.supply, 84); let account = Account::unpack_unchecked(&account2_account.data).unwrap(); - assert_eq!(account.amount, 42); + assert_eq!(account.amount, 42); // missing signer let mut instruction = @@ -3201,7 +3201,7 @@ mod tests { ) .unwrap(); let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); - account.owner = mint_key; + account.owner = mint_key; Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); do_process_instruction_dups( burn(&program_id, &account1_key, &mint_key, &mint_key, &[], 500).unwrap(), @@ -3232,9 +3232,9 @@ mod tests { ) .unwrap(); let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); - account.delegated_amount = 1000; - account.delegate = COption::Some(account1_key); - account.owner = owner_key; + account.delegated_amount = 1000; + account.delegate = COption::Some(account1_key); + account.owner = owner_key; Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); do_process_instruction_dups( burn( @@ -3281,9 +3281,9 @@ mod tests { ) .unwrap(); let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); - account.delegated_amount = 1000; - account.delegate = COption::Some(mint_key); - account.owner = owner_key; + account.delegated_amount = 1000; + account.delegate = COption::Some(mint_key); + account.owner = owner_key; Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); do_process_instruction_dups( burn(&program_id, &account1_key, &mint_key, &mint_key, &[], 500).unwrap(), @@ -3402,7 +3402,7 @@ mod tests { ) .unwrap(); let mut account = Account::unpack_unchecked(&mismatch_account.data).unwrap(); - account.mint = mint2_key; + account.mint = mint2_key; Account::pack(account, &mut mismatch_account.data).unwrap(); // mint to account @@ -3470,9 +3470,9 @@ mod tests { .unwrap(); let mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); - assert_eq!(mint.supply, 1000 - 42); + assert_eq!(mint.supply, 1000 - 42); let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, 1000 - 42); + assert_eq!(account.amount, 1000 - 42); // insufficient funds assert_eq!( @@ -3540,9 +3540,9 @@ mod tests { // match let mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); - assert_eq!(mint.supply, 1000 - 42 - 84); + assert_eq!(mint.supply, 1000 - 42 - 84); let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, 1000 - 42 - 84); + assert_eq!(account.amount, 1000 - 42 - 84); // insufficient funds approved via delegate assert_eq!( @@ -3999,10 +3999,10 @@ mod tests { let mut lamports = 0; let mut data = vec![0; Multisig::get_packed_len()]; let mut multisig = Multisig::unpack_unchecked(&data).unwrap(); - multisig.m = MAX_SIGNERS as u8; - multisig.n = MAX_SIGNERS as u8; - multisig.signers = signer_keys; - multisig.is_initialized = true; + multisig.m = MAX_SIGNERS as u8; + multisig.n = MAX_SIGNERS as u8; + multisig.signers = signer_keys; + multisig.is_initialized = true; Multisig::pack(multisig, &mut data).unwrap(); let owner_account_info = AccountInfo::new( &owner_key, @@ -4022,7 +4022,7 @@ mod tests { { let mut multisig = Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap(); - multisig.m = 1; + multisig.m = 1; Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap(); } Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap(); @@ -4031,8 +4031,8 @@ mod tests { { let mut multisig = Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap(); - multisig.m = 2; - multisig.n = 1; + multisig.m = 2; + multisig.n = 1; Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap(); } assert_eq!( @@ -4044,8 +4044,8 @@ mod tests { { let mut multisig = Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap(); - multisig.m = 0; - multisig.n = 11; + multisig.m = 0; + multisig.n = 11; Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap(); } Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap(); @@ -4054,8 +4054,8 @@ mod tests { { let mut multisig = Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap(); - multisig.m = 2; - multisig.n = 11; + multisig.m = 2; + multisig.n = 11; Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap(); } assert_eq!( @@ -4066,8 +4066,8 @@ mod tests { { let mut multisig = Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap(); - multisig.m = 2; - multisig.n = 11; + multisig.m = 2; + multisig.n = 11; Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap(); } assert_eq!( @@ -4079,8 +4079,8 @@ mod tests { { let mut multisig = Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap(); - multisig.m = 2; - multisig.n = 11; + multisig.m = 2; + multisig.n = 11; Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap(); } Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers[5..7]) @@ -4091,7 +4091,7 @@ mod tests { let mut multisig = Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap(); multisig.m = 11; - multisig.n = 11; + multisig.n = 11; Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap(); } signers[5].is_signer = false; @@ -4195,8 +4195,8 @@ mod tests { // source-close-authority close let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); - account.close_authority = COption::Some(account1_key); - account.owner = owner_key; + account.close_authority = COption::Some(account1_key); + account.owner = owner_key; Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); do_process_instruction_dups( close_account( @@ -4286,7 +4286,7 @@ mod tests { ) .unwrap(); let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, 42); + assert_eq!(account.amount, 42); // initialize native account do_process_instruction( @@ -4306,8 +4306,8 @@ mod tests { ) .unwrap(); let account = Account::unpack_unchecked(&account2_account.data).unwrap(); - assert!(account.is_native()); - assert_eq!(account.amount, 42); + assert!(account.is_native()); + assert_eq!(account.amount, 42); // close non-native account with balance assert_eq!( @@ -4356,7 +4356,7 @@ mod tests { assert_eq!(account_account.lamports, 0); assert_eq!(account3_account.lamports, 2 * account_minimum_balance()); let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, 0); + assert_eq!(account.amount, 0); // fund and initialize new non-native account to test close authority let account_key = pubkey_rand(); @@ -4423,7 +4423,7 @@ mod tests { assert_eq!(account_account.lamports, 0); assert_eq!(account3_account.lamports, 2 * account_minimum_balance() + 2); let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, 0); + assert_eq!(account.amount, 0); // close native account do_process_instruction( @@ -4436,13 +4436,13 @@ mod tests { ) .unwrap(); let account = Account::unpack_unchecked(&account2_account.data).unwrap(); - assert!(account.is_native()); - assert_eq!(account_account.lamports, 0); - assert_eq!(account.amount, 0); - assert_eq!( - account3_account.lamports, - 3 * account_minimum_balance() + 2 + 42 - ); + assert!(account.is_native()); + assert_eq!(account_account.lamports, 0); + assert_eq!(account.amount, 0); + assert_eq!( + account3_account.lamports, + 3 * account_minimum_balance() + 2 + 42 + ); } #[test] @@ -4486,8 +4486,8 @@ mod tests { ) .unwrap(); let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert!(account.is_native()); - assert_eq!(account.amount, 40); + assert!(account.is_native()); + assert_eq!(account.amount, 40); // initialize native account do_process_instruction( @@ -4507,8 +4507,8 @@ mod tests { ) .unwrap(); let account = Account::unpack_unchecked(&account2_account.data).unwrap(); - assert!(account.is_native()); - assert_eq!(account.amount, 0); + assert!(account.is_native()); + assert_eq!(account.amount, 0); // mint_to unsupported assert_eq!( @@ -4598,12 +4598,12 @@ mod tests { .unwrap(); assert_eq!(account_account.lamports, account_minimum_balance()); let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert!(account.is_native()); - assert_eq!(account.amount, 0); + assert!(account.is_native()); + assert_eq!(account.amount, 0); assert_eq!(account2_account.lamports, account_minimum_balance() + 40); let account = Account::unpack_unchecked(&account2_account.data).unwrap(); - assert!(account.is_native()); - assert_eq!(account.amount, 40); + assert!(account.is_native()); + assert_eq!(account.amount, 40); // close native account do_process_instruction( @@ -4618,8 +4618,8 @@ mod tests { assert_eq!(account_account.lamports, 0); assert_eq!(account3_account.lamports, 2 * account_minimum_balance()); let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert!(account.is_native()); - assert_eq!(account.amount, 0); + assert!(account.is_native()); + assert_eq!(account.amount, 0); } #[test] @@ -4698,7 +4698,7 @@ mod tests { ) .unwrap(); let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, u64::MAX); + assert_eq!(account.amount, u64::MAX); // attempt to mint one more to account assert_eq!( @@ -4721,7 +4721,7 @@ mod tests { ) ); let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, u64::MAX); + assert_eq!(account.amount, u64::MAX); // atttempt to mint one more to the other account assert_eq!( @@ -4751,7 +4751,7 @@ mod tests { ) .unwrap(); let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, u64::MAX - 100); + assert_eq!(account.amount, u64::MAX - 100); do_process_instruction( mint_to( @@ -4771,11 +4771,11 @@ mod tests { ) .unwrap(); let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, u64::MAX); + assert_eq!(account.amount, u64::MAX); // manipulate account balance to attempt overflow transfer let mut account = Account::unpack_unchecked(&account2_account.data).unwrap(); - account.amount = 1; + account.amount = 1; Account::pack(account, &mut account2_account.data).unwrap(); assert_eq!( @@ -4861,7 +4861,7 @@ mod tests { // no transfer if either account is frozen let mut account = Account::unpack_unchecked(&account2_account.data).unwrap(); - account.state = AccountState::Frozen; + account.state = AccountState::Frozen; Account::pack(account, &mut account2_account.data).unwrap(); assert_eq!( Err(TokenError::AccountFrozen.into()), @@ -4884,10 +4884,10 @@ mod tests { ); let mut account = Account::unpack_unchecked(&account_account.data).unwrap(); - account.state = AccountState::Initialized; + account.state = AccountState::Initialized; Account::pack(account, &mut account_account.data).unwrap(); let mut account = Account::unpack_unchecked(&account2_account.data).unwrap(); - account.state = AccountState::Frozen; + account.state = AccountState::Frozen; Account::pack(account, &mut account2_account.data).unwrap(); assert_eq!( Err(TokenError::AccountFrozen.into()), @@ -4911,7 +4911,7 @@ mod tests { // no approve if account is frozen let mut account = Account::unpack_unchecked(&account_account.data).unwrap(); - account.state = AccountState::Frozen; + account.state = AccountState::Frozen; Account::pack(account, &mut account_account.data).unwrap(); let delegate_key = pubkey_rand(); let mut delegate_account = SolanaAccount::default(); @@ -4937,8 +4937,8 @@ mod tests { // no revoke if account is frozen let mut account = Account::unpack_unchecked(&account_account.data).unwrap(); - account.delegate = COption::Some(delegate_key); - account.delegated_amount = 100; + account.delegate = COption::Some(delegate_key); + account.delegated_amount = 100; Account::pack(account, &mut account_account.data).unwrap(); assert_eq!( Err(TokenError::AccountFrozen.into()), @@ -5036,7 +5036,7 @@ mod tests { // thaw where mint freeze_authority is account let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); - account.state = AccountState::Frozen; + account.state = AccountState::Frozen; Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); do_process_instruction_dups( thaw_account(&program_id, &account1_key, &mint_key, &account1_key, &[]).unwrap(), @@ -5106,7 +5106,7 @@ mod tests { // missing freeze_authority let mut mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); - mint.freeze_authority = COption::Some(owner_key); + mint.freeze_authority = COption::Some(owner_key); Mint::pack(mint, &mut mint_account.data).unwrap(); assert_eq!( Err(TokenError::OwnerMismatch.into()), @@ -5132,7 +5132,7 @@ mod tests { ) .unwrap(); let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.state, AccountState::Frozen); + assert_eq!(account.state, AccountState::Frozen); // check explicit freeze assert_eq!( @@ -5159,6 +5159,6 @@ mod tests { ) .unwrap(); let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.state, AccountState::Initialized); + assert_eq!(account.state, AccountState::Initialized); } } From 40ce0812f08ba88a0c56e9df5140f826e1f054f8 Mon Sep 17 00:00:00 2001 From: Jack May Date: Mon, 21 Sep 2020 14:49:34 -0700 Subject: [PATCH 070/248] retain v2 error values --- program/src/error.rs | 3 +++ program/src/processor.rs | 1 + 2 files changed, 4 insertions(+) diff --git a/program/src/error.rs b/program/src/error.rs index e56fde6..32cd9d2 100644 --- a/program/src/error.rs +++ b/program/src/error.rs @@ -34,6 +34,9 @@ pub enum TokenError { /// Invalid number of required signers. #[error("Invalid number of required signers")] InvalidNumberOfRequiredSigners, + /// State is uninitialized. + #[error("State is unititialized")] + UninitializedState, /// Instruction does not support native tokens #[error("Instruction does not support native tokens")] NativeNotSupported, diff --git a/program/src/processor.rs b/program/src/processor.rs index dcc7c7d..baa1e33 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -758,6 +758,7 @@ impl PrintProgramError for TokenError { TokenError::InvalidNumberOfRequiredSigners => { info!("Error: Invalid number of required signers") } + TokenError::UninitializedState => info!("Error: State is uninitialized"), TokenError::NativeNotSupported => { info!("Error: Instruction does not support native tokens") } From d9137a46aa0ba4ac2cfb626b42d6c021ec859cd9 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Mon, 21 Sep 2020 20:28:24 -0600 Subject: [PATCH 071/248] Standardize failure case ordering (#509) * Standardize processor failure-case ordering * Fix token-swap test --- program/src/processor.rs | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index baa1e33..7c32213 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -161,12 +161,12 @@ impl Processor { let mut source_account = Account::unpack(&source_account_info.data.borrow())?; let mut dest_account = Account::unpack(&dest_account_info.data.borrow())?; - if source_account.amount < amount { - return Err(TokenError::InsufficientFunds.into()); - } if source_account.is_frozen() || dest_account.is_frozen() { return Err(TokenError::AccountFrozen.into()); } + if source_account.amount < amount { + return Err(TokenError::InsufficientFunds.into()); + } if source_account.mint != dest_account.mint { return Err(TokenError::MintMismatch.into()); } @@ -478,17 +478,17 @@ impl Processor { let mut source_account = Account::unpack(&source_account_info.data.borrow())?; let mut mint = Mint::unpack(&mint_info.data.borrow())?; + if source_account.is_frozen() { + return Err(TokenError::AccountFrozen.into()); + } if source_account.is_native() { return Err(TokenError::NativeNotSupported.into()); } - if mint_info.key != &source_account.mint { - return Err(TokenError::MintMismatch.into()); - } if source_account.amount < amount { return Err(TokenError::InsufficientFunds.into()); } - if source_account.is_frozen() { - return Err(TokenError::AccountFrozen.into()); + if mint_info.key != &source_account.mint { + return Err(TokenError::MintMismatch.into()); } if let Some(expected_decimals) = expected_decimals { @@ -588,15 +588,15 @@ impl Processor { let authority_info = next_account_info(account_info_iter)?; let mut source_account = Account::unpack(&source_account_info.data.borrow())?; + if freeze && source_account.is_frozen() || !freeze && !source_account.is_frozen() { + return Err(TokenError::InvalidState.into()); + } if source_account.is_native() { return Err(TokenError::NativeNotSupported.into()); } if mint_info.key != &source_account.mint { return Err(TokenError::MintMismatch.into()); } - if freeze && source_account.is_frozen() || !freeze && !source_account.is_frozen() { - return Err(TokenError::InvalidState.into()); - } let mint = Mint::unpack(&mint_info.data.borrow_mut())?; match mint.freeze_authority { @@ -3402,9 +3402,6 @@ mod tests { ], ) .unwrap(); - let mut account = Account::unpack_unchecked(&mismatch_account.data).unwrap(); - account.mint = mint2_key; - Account::pack(account, &mut mismatch_account.data).unwrap(); // mint to account do_process_instruction( @@ -3413,6 +3410,16 @@ mod tests { ) .unwrap(); + // mint to mismatch account and change mint key + do_process_instruction( + mint_to(&program_id, &mint_key, &mismatch_key, &owner_key, &[], 1000).unwrap(), + vec![&mut mint_account, &mut mismatch_account, &mut owner_account], + ) + .unwrap(); + let mut account = Account::unpack_unchecked(&mismatch_account.data).unwrap(); + account.mint = mint2_key; + Account::pack(account, &mut mismatch_account.data).unwrap(); + // missing signer let mut instruction = burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 42).unwrap(); @@ -3471,7 +3478,7 @@ mod tests { .unwrap(); let mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); - assert_eq!(mint.supply, 1000 - 42); + assert_eq!(mint.supply, 2000 - 42); let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.amount, 1000 - 42); @@ -3541,7 +3548,7 @@ mod tests { // match let mint = Mint::unpack_unchecked(&mint_account.data).unwrap(); - assert_eq!(mint.supply, 1000 - 42 - 84); + assert_eq!(mint.supply, 2000 - 42 - 84); let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.amount, 1000 - 42 - 84); From 23047f6694d4f76d43fe170dd48481cc37693ccd Mon Sep 17 00:00:00 2001 From: Jack May Date: Tue, 22 Sep 2020 00:55:59 -0700 Subject: [PATCH 072/248] Rename x2 instructions to xChecked --- program/src/instruction.rs | 66 ++++++++++++++--------------- program/src/processor.rs | 86 +++++++++++++++++++------------------- 2 files changed, 76 insertions(+), 76 deletions(-) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index ae01523..de75b73 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -233,7 +233,7 @@ pub enum TokenInstruction { /// transferred to the destination account. /// /// This instruction differs from Transfer in that the token mint and decimals value is - /// asserted by the caller. This may be useful when creating transactions offline or within a + /// checked by the caller. This may be useful when creating transactions offline or within a /// hardware wallet. /// /// Accounts expected by this instruction: @@ -250,7 +250,7 @@ pub enum TokenInstruction { /// 2. `[writable]` The destination account. /// 3. `[]` The source account's multisignature owner/delegate. /// 4. ..4+M `[signer]` M signer accounts. - Transfer2 { + TransferChecked { /// The amount of tokens to transfer. amount: u64, /// Expected number of base 10 digits to the right of the decimal place. @@ -259,7 +259,7 @@ pub enum TokenInstruction { /// Approves a delegate. A delegate is given the authority over /// tokens on behalf of the source account's owner. /// - /// This instruction differs from Approve in that the token mint and decimals value is asserted + /// This instruction differs from Approve in that the token mint and decimals value is checked /// by the caller. This may be useful when creating transactions offline or within a hardware /// wallet. /// @@ -277,7 +277,7 @@ pub enum TokenInstruction { /// 2. `[]` The delegate. /// 3. `[]` The source account's multisignature owner. /// 4. ..4+M `[signer]` M signer accounts - Approve2 { + ApproveChecked { /// The amount of tokens the delegate is approved for. amount: u64, /// Expected number of base 10 digits to the right of the decimal place. @@ -285,7 +285,7 @@ pub enum TokenInstruction { }, /// Mints new tokens to an account. The native mint does not support minting. /// - /// This instruction differs from MintTo in that the decimals value is asserted by the + /// This instruction differs from MintTo in that the decimals value is checked by the /// caller. This may be useful when creating transactions offline or within a hardware wallet. /// /// Accounts expected by this instruction: @@ -300,16 +300,16 @@ pub enum TokenInstruction { /// 1. `[writable]` The account to mint tokens to. /// 2. `[]` The mint's multisignature mint-tokens authority. /// 3. ..3+M `[signer]` M signer accounts. - MintTo2 { + MintToChecked { /// The amount of new tokens to mint. amount: u64, /// Expected number of base 10 digits to the right of the decimal place. decimals: u8, }, - /// Burns tokens by removing them from an account. `Burn2` does not support accounts + /// Burns tokens by removing them from an account. `BurnChecked` does not support accounts /// associated with the native mint, use `CloseAccount` instead. /// - /// This instruction differs from Burn in that the decimals value is asserted by the caller. + /// This instruction differs from Burn in that the decimals value is checked by the caller. /// This may be useful when creating transactions offline or within a hardware wallet. /// /// Accounts expected by this instruction: @@ -324,7 +324,7 @@ pub enum TokenInstruction { /// 1. `[writable]` The token mint. /// 2. `[]` The account's multisignature owner/delegate. /// 3. ..3+M `[signer]` M signer accounts. - Burn2 { + BurnChecked { /// The amount of tokens to burn. amount: u64, /// Expected number of base 10 digits to the right of the decimal place. @@ -392,7 +392,7 @@ impl TokenInstruction { .ok_or(InvalidInstruction)?; let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?; - Self::Transfer2 { amount, decimals } + Self::TransferChecked { amount, decimals } } 13 => { let (amount, rest) = rest.split_at(8); @@ -403,7 +403,7 @@ impl TokenInstruction { .ok_or(InvalidInstruction)?; let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?; - Self::Approve2 { amount, decimals } + Self::ApproveChecked { amount, decimals } } 14 => { let (amount, rest) = rest.split_at(8); @@ -414,7 +414,7 @@ impl TokenInstruction { .ok_or(InvalidInstruction)?; let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?; - Self::MintTo2 { amount, decimals } + Self::MintToChecked { amount, decimals } } 15 => { let (amount, rest) = rest.split_at(8); @@ -425,7 +425,7 @@ impl TokenInstruction { .ok_or(InvalidInstruction)?; let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?; - Self::Burn2 { amount, decimals } + Self::BurnChecked { amount, decimals } } _ => return Err(TokenError::InvalidInstruction.into()), @@ -479,22 +479,22 @@ impl TokenInstruction { Self::CloseAccount => buf.push(9), Self::FreezeAccount => buf.push(10), Self::ThawAccount => buf.push(11), - &Self::Transfer2 { amount, decimals } => { + &Self::TransferChecked { amount, decimals } => { buf.push(12); buf.extend_from_slice(&amount.to_le_bytes()); buf.push(decimals); } - &Self::Approve2 { amount, decimals } => { + &Self::ApproveChecked { amount, decimals } => { buf.push(13); buf.extend_from_slice(&amount.to_le_bytes()); buf.push(decimals); } - &Self::MintTo2 { amount, decimals } => { + &Self::MintToChecked { amount, decimals } => { buf.push(14); buf.extend_from_slice(&amount.to_le_bytes()); buf.push(decimals); } - &Self::Burn2 { amount, decimals } => { + &Self::BurnChecked { amount, decimals } => { buf.push(15); buf.extend_from_slice(&amount.to_le_bytes()); buf.push(decimals); @@ -910,9 +910,9 @@ pub fn thaw_account( }) } -/// Creates a `Transfer2` instruction. +/// Creates a `TransferChecked` instruction. #[allow(clippy::too_many_arguments)] -pub fn transfer2( +pub fn transfer_checked( token_program_id: &Pubkey, source_pubkey: &Pubkey, mint_pubkey: &Pubkey, @@ -922,7 +922,7 @@ pub fn transfer2( amount: u64, decimals: u8, ) -> Result { - let data = TokenInstruction::Transfer2 { amount, decimals }.pack(); + let data = TokenInstruction::TransferChecked { amount, decimals }.pack(); let mut accounts = Vec::with_capacity(4 + signer_pubkeys.len()); accounts.push(AccountMeta::new(*source_pubkey, false)); @@ -943,9 +943,9 @@ pub fn transfer2( }) } -/// Creates an `Approve2` instruction. +/// Creates an `ApproveChecked` instruction. #[allow(clippy::too_many_arguments)] -pub fn approve2( +pub fn approve_checked( token_program_id: &Pubkey, source_pubkey: &Pubkey, mint_pubkey: &Pubkey, @@ -955,7 +955,7 @@ pub fn approve2( amount: u64, decimals: u8, ) -> Result { - let data = TokenInstruction::Approve2 { amount, decimals }.pack(); + let data = TokenInstruction::ApproveChecked { amount, decimals }.pack(); let mut accounts = Vec::with_capacity(4 + signer_pubkeys.len()); accounts.push(AccountMeta::new(*source_pubkey, false)); @@ -976,8 +976,8 @@ pub fn approve2( }) } -/// Creates a `MintTo2` instruction. -pub fn mint_to2( +/// Creates a `MintToChecked` instruction. +pub fn mint_to_checked( token_program_id: &Pubkey, mint_pubkey: &Pubkey, account_pubkey: &Pubkey, @@ -986,7 +986,7 @@ pub fn mint_to2( amount: u64, decimals: u8, ) -> Result { - let data = TokenInstruction::MintTo2 { amount, decimals }.pack(); + let data = TokenInstruction::MintToChecked { amount, decimals }.pack(); let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); accounts.push(AccountMeta::new(*mint_pubkey, false)); @@ -1006,8 +1006,8 @@ pub fn mint_to2( }) } -/// Creates a `Burn2` instruction. -pub fn burn2( +/// Creates a `BurnChecked` instruction. +pub fn burn_checked( token_program_id: &Pubkey, account_pubkey: &Pubkey, mint_pubkey: &Pubkey, @@ -1016,7 +1016,7 @@ pub fn burn2( amount: u64, decimals: u8, ) -> Result { - let data = TokenInstruction::Burn2 { amount, decimals }.pack(); + let data = TokenInstruction::BurnChecked { amount, decimals }.pack(); let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); accounts.push(AccountMeta::new(*account_pubkey, false)); @@ -1156,7 +1156,7 @@ mod test { let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); - let check = TokenInstruction::Transfer2 { + let check = TokenInstruction::TransferChecked { amount: 1, decimals: 2, }; @@ -1166,7 +1166,7 @@ mod test { let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); - let check = TokenInstruction::Approve2 { + let check = TokenInstruction::ApproveChecked { amount: 1, decimals: 2, }; @@ -1176,7 +1176,7 @@ mod test { let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); - let check = TokenInstruction::MintTo2 { + let check = TokenInstruction::MintToChecked { amount: 1, decimals: 2, }; @@ -1186,7 +1186,7 @@ mod test { let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); - let check = TokenInstruction::Burn2 { + let check = TokenInstruction::BurnChecked { amount: 1, decimals: 2, }; diff --git a/program/src/processor.rs b/program/src/processor.rs index 7c32213..87bdb41 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -680,20 +680,20 @@ impl Processor { info!("Instruction: FreezeAccount"); Self::process_toggle_freeze_account(program_id, accounts, false) } - TokenInstruction::Transfer2 { amount, decimals } => { - info!("Instruction: Transfer"); + TokenInstruction::TransferChecked { amount, decimals } => { + info!("Instruction: TransferChecked"); Self::process_transfer(program_id, accounts, amount, Some(decimals)) } - TokenInstruction::Approve2 { amount, decimals } => { - info!("Instruction: Approve"); + TokenInstruction::ApproveChecked { amount, decimals } => { + info!("Instruction: ApproveChecked"); Self::process_approve(program_id, accounts, amount, Some(decimals)) } - TokenInstruction::MintTo2 { amount, decimals } => { - info!("Instruction: MintTo"); + TokenInstruction::MintToChecked { amount, decimals } => { + info!("Instruction: MintToChecked"); Self::process_mint_to(program_id, accounts, amount, Some(decimals)) } - TokenInstruction::Burn2 { amount, decimals } => { - info!("Instruction: Burn"); + TokenInstruction::BurnChecked { amount, decimals } => { + info!("Instruction: BurnChecked"); Self::process_burn(program_id, accounts, amount, Some(decimals)) } } @@ -1196,9 +1196,9 @@ mod tests { ) .unwrap(); - // source-owner transfer2 + // source-owner TransferChecked do_process_instruction_dups( - transfer2( + transfer_checked( &program_id, &account1_key, &mint_key, @@ -1244,9 +1244,9 @@ mod tests { ) .unwrap(); - // source-delegate transfer2 + // source-delegate TransferChecked do_process_instruction_dups( - transfer2( + transfer_checked( &program_id, &account1_key, &mint_key, @@ -1303,9 +1303,9 @@ mod tests { ) .unwrap(); - // destination-owner transfer2 + // destination-owner TransferChecked do_process_instruction_dups( - transfer2( + transfer_checked( &program_id, &account3_key, &mint_key, @@ -1373,9 +1373,9 @@ mod tests { ) .unwrap(); - // source-multisig-signer transfer2 + // source-multisig-signer TransferChecked do_process_instruction_dups( - transfer2( + transfer_checked( &program_id, &account4_key, &mint_key, @@ -1621,7 +1621,7 @@ mod tests { assert_eq!( Err(TokenError::MintDecimalsMismatch.into()), do_process_instruction( - transfer2( + transfer_checked( &program_id, &account2_key, &mint_key, @@ -1645,7 +1645,7 @@ mod tests { assert_eq!( Err(TokenError::MintMismatch.into()), do_process_instruction( - transfer2( + transfer_checked( &program_id, &account2_key, &account3_key, // <-- incorrect mint @@ -1666,7 +1666,7 @@ mod tests { ); // transfer rest with explicit decimals do_process_instruction( - transfer2( + transfer_checked( &program_id, &account2_key, &mint_key, @@ -1914,7 +1914,7 @@ mod tests { assert_eq!( Err(TokenError::MintDecimalsMismatch.into()), do_process_instruction( - mint_to2( + mint_to_checked( &program_id, &mint_key, &account_key, @@ -1934,7 +1934,7 @@ mod tests { // mint to 2 do_process_instruction( - mint_to2( + mint_to_checked( &program_id, &mint_key, &account_key, @@ -2051,9 +2051,9 @@ mod tests { ) .unwrap(); - // source-owner approve2 + // source-owner approve_checked do_process_instruction_dups( - approve2( + approve_checked( &program_id, &account1_key, &mint_key, @@ -2128,9 +2128,9 @@ mod tests { ) .unwrap(); - // source-multisig-signer approve2 + // source-multisig-signer approve_checked do_process_instruction_dups( - approve2( + approve_checked( &program_id, &account3_key, &mint_key, @@ -2294,7 +2294,7 @@ mod tests { assert_eq!( Err(TokenError::MintDecimalsMismatch.into()), do_process_instruction( - approve2( + approve_checked( &program_id, &account_key, &mint_key, @@ -2318,7 +2318,7 @@ mod tests { assert_eq!( Err(TokenError::MintMismatch.into()), do_process_instruction( - approve2( + approve_checked( &program_id, &account_key, &account2_key, // <-- bad mint @@ -2340,7 +2340,7 @@ mod tests { // approve delegate 2 do_process_instruction( - approve2( + approve_checked( &program_id, &account_key, &mint_key, @@ -2857,9 +2857,9 @@ mod tests { ) .unwrap(); - // mint_to2 when mint_authority is self + // mint_to_checked when mint_authority is self do_process_instruction_dups( - mint_to2(&program_id, &mint_key, &account1_key, &mint_key, &[], 42, 2).unwrap(), + mint_to_checked(&program_id, &mint_key, &account1_key, &mint_key, &[], 42, 2).unwrap(), vec![mint_info.clone(), account1_info.clone(), mint_info.clone()], ) .unwrap(); @@ -2886,7 +2886,7 @@ mod tests { ) .unwrap(); - // mint_to2 when mint_authority is account owner + // mint_to_checked when mint_authority is account owner do_process_instruction_dups( mint_to( &program_id, @@ -3175,9 +3175,9 @@ mod tests { ) .unwrap(); - // source-owner burn2 + // source-owner burn_checked do_process_instruction_dups( - burn2( + burn_checked( &program_id, &account1_key, &mint_key, @@ -3210,9 +3210,9 @@ mod tests { ) .unwrap(); - // mint-owner burn2 + // mint-owner burn_checked do_process_instruction_dups( - burn2( + burn_checked( &program_id, &account1_key, &mint_key, @@ -3255,9 +3255,9 @@ mod tests { ) .unwrap(); - // source-delegate burn2 + // source-delegate burn_checked do_process_instruction_dups( - burn2( + burn_checked( &program_id, &account1_key, &mint_key, @@ -3292,9 +3292,9 @@ mod tests { ) .unwrap(); - // mint-delegate burn2 + // mint-delegate burn_checked do_process_instruction_dups( - burn2( + burn_checked( &program_id, &account1_key, &mint_key, @@ -3461,18 +3461,18 @@ mod tests { ) .unwrap(); - // burn2, with incorrect decimals + // burn_checked, with incorrect decimals assert_eq!( Err(TokenError::MintDecimalsMismatch.into()), do_process_instruction( - burn2(&program_id, &account_key, &mint_key, &owner_key, &[], 21, 3).unwrap(), + burn_checked(&program_id, &account_key, &mint_key, &owner_key, &[], 21, 3).unwrap(), vec![&mut account_account, &mut mint_account, &mut owner_account], ) ); - // burn2 + // burn_checked do_process_instruction( - burn2(&program_id, &account_key, &mint_key, &owner_key, &[], 21, 2).unwrap(), + burn_checked(&program_id, &account_key, &mint_key, &owner_key, &[], 21, 2).unwrap(), vec![&mut account_account, &mut mint_account, &mut owner_account], ) .unwrap(); From 072bd4c101ba03c90a25f1150f6410d8a275afb6 Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Fri, 25 Sep 2020 18:14:37 -0600 Subject: [PATCH 073/248] token: Bump solana crates to 1.3.14 --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 9be3b68..fb39770 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -21,7 +21,7 @@ default = ["solana-sdk/default"] num-derive = "0.3" num-traits = "0.2" remove_dir_all = "=0.5.0" -solana-sdk = { version = "1.3.12", default-features = false, optional = true } +solana-sdk = { version = "1.3.14", default-features = false, optional = true } thiserror = "1.0" arrayref = "0.3.6" num_enum = "0.5.1" From b15582aed143cb3f8b5e40abc9779dac09cbb2f4 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Mon, 5 Oct 2020 21:12:45 -0600 Subject: [PATCH 074/248] Bump token to published version --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index fb39770..e3857fb 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "spl-token" -version = "2.0.5" +version = "2.0.6" description = "Solana Program Library Token" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" From 24d75bd3ac238654855086e559a00b93cb211a28 Mon Sep 17 00:00:00 2001 From: Jack May Date: Mon, 5 Oct 2020 22:06:24 -0700 Subject: [PATCH 075/248] solana sdk does not need skip-no-mangle --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index e3857fb..281550b 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -13,7 +13,7 @@ exclude = ["js/**"] [features] no-entrypoint = [] -skip-no-mangle = ["solana-sdk/skip-no-mangle"] +skip-no-mangle = [] program = ["solana-sdk/program"] default = ["solana-sdk/default"] From d85334b20432e10e197d62a7cdb222a9e4739192 Mon Sep 17 00:00:00 2001 From: Jack May Date: Tue, 6 Oct 2020 13:45:20 -0700 Subject: [PATCH 076/248] Remove skip-no-mangle entirely --- program/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 281550b..d2b429e 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -13,7 +13,6 @@ exclude = ["js/**"] [features] no-entrypoint = [] -skip-no-mangle = [] program = ["solana-sdk/program"] default = ["solana-sdk/default"] From 61088134b0b4d3f4cdb834b190e275a2bc9c41c7 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Thu, 15 Oct 2020 12:20:23 -0600 Subject: [PATCH 077/248] Bump memo and token versions (#627) --- program/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index d2b429e..d22324e 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -3,7 +3,7 @@ [package] name = "spl-token" -version = "2.0.6" +version = "2.0.8" description = "Solana Program Library Token" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" @@ -20,7 +20,7 @@ default = ["solana-sdk/default"] num-derive = "0.3" num-traits = "0.2" remove_dir_all = "=0.5.0" -solana-sdk = { version = "1.3.14", default-features = false, optional = true } +solana-sdk = { version = "1.3.17", default-features = false, optional = true } thiserror = "1.0" arrayref = "0.3.6" num_enum = "0.5.1" From b217df8eb7309d94d7ba6b059ca14d4904127223 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Mon, 19 Oct 2020 08:48:37 -0700 Subject: [PATCH 078/248] Add additional transfer test coverage --- program/src/processor.rs | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/program/src/processor.rs b/program/src/processor.rs index 87bdb41..ac83ca8 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -1721,6 +1721,49 @@ mod tests { let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.amount, 1000); + // bogus transfer to self using system accounts. + // + // Transfer will succeed if the source and destination accounts have the same address, + // regardless of whether it is a valid token account. + // + // This is probably wrong but transactions in the wild have been observed to do this so + // this behavior is now part of the token ABI + { + let system_account_key = pubkey_rand(); + let mut system_account = SolanaAccount::new(1, 0, &Pubkey::default()); + + let instruction = transfer( + &program_id, + &system_account_key, + &system_account_key, + &owner_key, + &[], + 500, + ) + .unwrap(); + + let account_account_info = AccountInfo::from(( + &instruction.accounts[0].pubkey, + instruction.accounts[0].is_signer, + &mut system_account, + )); + let owner_account_info = AccountInfo::from(( + &instruction.accounts[2].pubkey, + instruction.accounts[2].is_signer, + &mut owner_account, + )); + Processor::process( + &instruction.program_id, + &[ + account_account_info.clone(), + account_account_info, + owner_account_info, + ], + &instruction.data, + ) + .unwrap() + } + // insufficient funds assert_eq!( Err(TokenError::InsufficientFunds.into()), From 5a74034ec0a8b9e8f565b2356dbb99e8ec4aee08 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Thu, 22 Oct 2020 21:44:27 -0700 Subject: [PATCH 079/248] Port SPL to solana-program and `cargo build-bpf` --- program/Cargo.toml | 10 +- program/src/entrypoint.rs | 5 +- program/src/error.rs | 2 +- program/src/instruction.rs | 2 +- program/src/lib.rs | 11 +- program/src/native_mint.rs | 4 +- program/src/processor.rs | 306 ++++++++++++++++++------------------- program/src/state.rs | 2 +- 8 files changed, 163 insertions(+), 179 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index d22324e..6e20a1a 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -1,6 +1,3 @@ - -# Note: This crate must be built using do.sh - [package] name = "spl-token" version = "2.0.8" @@ -12,21 +9,18 @@ edition = "2018" exclude = ["js/**"] [features] -no-entrypoint = [] -program = ["solana-sdk/program"] -default = ["solana-sdk/default"] +exclude_entrypoint = [] [dependencies] num-derive = "0.3" num-traits = "0.2" remove_dir_all = "=0.5.0" -solana-sdk = { version = "1.3.17", default-features = false, optional = true } +solana-program = "1.4.3" thiserror = "1.0" arrayref = "0.3.6" num_enum = "0.5.1" [dev-dependencies] -rand = { version = "0.7.0"} [lib] crate-type = ["cdylib", "lib"] diff --git a/program/src/entrypoint.rs b/program/src/entrypoint.rs index b5594ca..b255513 100644 --- a/program/src/entrypoint.rs +++ b/program/src/entrypoint.rs @@ -1,10 +1,7 @@ //! Program entrypoint -#![cfg(feature = "program")] -#![cfg(not(feature = "no-entrypoint"))] - use crate::{error::TokenError, processor::Processor}; -use solana_sdk::{ +use solana_program::{ account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, program_error::PrintProgramError, pubkey::Pubkey, }; diff --git a/program/src/error.rs b/program/src/error.rs index 32cd9d2..8305eaa 100644 --- a/program/src/error.rs +++ b/program/src/error.rs @@ -1,7 +1,7 @@ //! Error types use num_derive::FromPrimitive; -use solana_sdk::{decode_error::DecodeError, program_error::ProgramError}; +use solana_program::{decode_error::DecodeError, program_error::ProgramError}; use thiserror::Error; /// Errors that may be returned by the Token program. diff --git a/program/src/instruction.rs b/program/src/instruction.rs index de75b73..1d26643 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -1,7 +1,7 @@ //! Instruction types use crate::error::TokenError; -use solana_sdk::{ +use solana_program::{ instruction::{AccountMeta, Instruction}, program_error::ProgramError, program_option::COption, diff --git a/program/src/lib.rs b/program/src/lib.rs index 6e7108f..fc9707d 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -3,16 +3,17 @@ //! An ERC20-like Token program for the Solana blockchain -pub mod entrypoint; pub mod error; pub mod instruction; pub mod native_mint; pub mod processor; pub mod state; -// Export current solana-sdk types for downstream users who may also be building with a different -// solana-sdk version -pub use solana_sdk; +#[cfg(not(feature = "exclude_entrypoint"))] +mod entrypoint; + +// Export current sdk types for downstream users building with a different sdk version +pub use solana_program; /// Convert the UI representation of a token amount (using the decimals field defined in its mint) /// to the raw amount @@ -25,4 +26,4 @@ pub fn amount_to_ui_amount(amount: u64, decimals: u8) -> f64 { amount as f64 / 10_usize.pow(decimals as u32) as f64 } -solana_sdk::declare_id!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); +solana_program::declare_id!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); diff --git a/program/src/native_mint.rs b/program/src/native_mint.rs index 740b8ab..0502f9f 100644 --- a/program/src/native_mint.rs +++ b/program/src/native_mint.rs @@ -4,12 +4,12 @@ pub const DECIMALS: u8 = 9; // The Mint for native SOL Token accounts -solana_sdk::declare_id!("So11111111111111111111111111111111111111112"); +solana_program::declare_id!("So11111111111111111111111111111111111111112"); #[cfg(test)] mod tests { use super::*; - use solana_sdk::native_token::*; + use solana_program::native_token::*; #[test] fn test_decimals() { diff --git a/program/src/processor.rs b/program/src/processor.rs index ac83ca8..c9b5e10 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -1,6 +1,6 @@ //! Program state processor -#![cfg(feature = "program")] +//#![cfg(feature = "program")] use crate::{ error::TokenError, @@ -8,7 +8,7 @@ use crate::{ state::{Account, AccountState, Mint, Multisig}, }; use num_traits::FromPrimitive; -use solana_sdk::{ +use solana_program::{ account_info::{next_account_info, AccountInfo}, decode_error::DecodeError, entrypoint::ProgramResult, @@ -780,23 +780,15 @@ impl PrintProgramError for TokenError { } } -// Pull in syscall stubs when building for non-BPF targets -#[cfg(not(target_arch = "bpf"))] -solana_sdk::program_stubs!(); - #[cfg(test)] mod tests { use super::*; use crate::instruction::*; - use solana_sdk::{ + use solana_program::{ account::Account as SolanaAccount, account_info::create_is_signer_account_infos, clock::Epoch, instruction::Instruction, sysvar::rent, }; - fn pubkey_rand() -> Pubkey { - Pubkey::new(&rand::random::<[u8; 32]>()) - } - fn do_process_instruction( instruction: Instruction, accounts: Vec<&mut SolanaAccount>, @@ -968,11 +960,11 @@ mod tests { #[test] fn test_initialize_mint() { - let program_id = pubkey_rand(); - let owner_key = pubkey_rand(); - let mint_key = pubkey_rand(); + let program_id = Pubkey::new_unique(); + let owner_key = Pubkey::new_unique(); + let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(42, Mint::get_packed_len(), &program_id); - let mint2_key = pubkey_rand(); + let mint2_key = Pubkey::new_unique(); let mut mint2_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mut rent_sysvar = rent_sysvar(); @@ -1016,12 +1008,12 @@ mod tests { #[test] fn test_initialize_mint_account() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); + let program_id = Pubkey::new_unique(); + let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new(42, Account::get_packed_len(), &program_id); - let owner_key = pubkey_rand(); + let owner_key = Pubkey::new_unique(); let mut owner_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); + let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mut rent_sysvar = rent_sysvar(); @@ -1092,46 +1084,46 @@ mod tests { #[test] fn test_transfer_dups() { - let program_id = pubkey_rand(); - let account1_key = pubkey_rand(); + let program_id = Pubkey::new_unique(); + let account1_key = Pubkey::new_unique(); let mut account1_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); let mut account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); - let account2_key = pubkey_rand(); + let account2_key = Pubkey::new_unique(); let mut account2_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); let mut account2_info: AccountInfo = (&account2_key, false, &mut account2_account).into(); - let account3_key = pubkey_rand(); + let account3_key = Pubkey::new_unique(); let mut account3_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); let account3_info: AccountInfo = (&account3_key, false, &mut account3_account).into(); - let account4_key = pubkey_rand(); + let account4_key = Pubkey::new_unique(); let mut account4_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); let account4_info: AccountInfo = (&account4_key, true, &mut account4_account).into(); - let multisig_key = pubkey_rand(); + let multisig_key = Pubkey::new_unique(); let mut multisig_account = SolanaAccount::new( multisig_minimum_balance(), Multisig::get_packed_len(), &program_id, ); let multisig_info: AccountInfo = (&multisig_key, true, &mut multisig_account).into(); - let owner_key = pubkey_rand(); + let owner_key = Pubkey::new_unique(); let mut owner_account = SolanaAccount::default(); let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into(); - let mint_key = pubkey_rand(); + let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mint_info: AccountInfo = (&mint_key, false, &mut mint_account).into(); @@ -1399,41 +1391,41 @@ mod tests { #[test] fn test_transfer() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); + let program_id = Pubkey::new_unique(); + let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let account2_key = pubkey_rand(); + let account2_key = Pubkey::new_unique(); let mut account2_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let account3_key = pubkey_rand(); + let account3_key = Pubkey::new_unique(); let mut account3_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let delegate_key = pubkey_rand(); + let delegate_key = Pubkey::new_unique(); let mut delegate_account = SolanaAccount::default(); - let mismatch_key = pubkey_rand(); + let mismatch_key = Pubkey::new_unique(); let mut mismatch_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let owner_key = pubkey_rand(); + let owner_key = Pubkey::new_unique(); let mut owner_account = SolanaAccount::default(); - let owner2_key = pubkey_rand(); + let owner2_key = Pubkey::new_unique(); let mut owner2_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); + let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mint2_key = pubkey_rand(); + let mint2_key = Pubkey::new_unique(); let mut rent_sysvar = rent_sysvar(); // create mint @@ -1729,7 +1721,7 @@ mod tests { // This is probably wrong but transactions in the wild have been observed to do this so // this behavior is now part of the token ABI { - let system_account_key = pubkey_rand(); + let system_account_key = Pubkey::new_unique(); let mut system_account = SolanaAccount::new(1, 0, &Pubkey::default()); let instruction = transfer( @@ -1898,16 +1890,16 @@ mod tests { #[test] fn test_mintable_token_with_zero_supply() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); + let program_id = Pubkey::new_unique(); + let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let owner_key = pubkey_rand(); + let owner_key = Pubkey::new_unique(); let mut owner_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); + let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mut rent_sysvar = rent_sysvar(); @@ -1949,7 +1941,7 @@ mod tests { vec![&mut mint_account, &mut account_account, &mut owner_account], ) .unwrap(); - let _ = Mint::unpack(&mut mint_account.data).unwrap(); + let _ = Mint::unpack(&mint_account.data).unwrap(); let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.amount, 42); @@ -1971,7 +1963,7 @@ mod tests { ) ); - let _ = Mint::unpack(&mut mint_account.data).unwrap(); + let _ = Mint::unpack(&mint_account.data).unwrap(); let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.amount, 42); @@ -1990,46 +1982,46 @@ mod tests { vec![&mut mint_account, &mut account_account, &mut owner_account], ) .unwrap(); - let _ = Mint::unpack(&mut mint_account.data).unwrap(); + let _ = Mint::unpack(&mint_account.data).unwrap(); let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.amount, 84); } #[test] fn test_approve_dups() { - let program_id = pubkey_rand(); - let account1_key = pubkey_rand(); + let program_id = Pubkey::new_unique(); + let account1_key = Pubkey::new_unique(); let mut account1_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); - let account2_key = pubkey_rand(); + let account2_key = Pubkey::new_unique(); let mut account2_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); let account2_info: AccountInfo = (&account2_key, false, &mut account2_account).into(); - let account3_key = pubkey_rand(); + let account3_key = Pubkey::new_unique(); let mut account3_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); let account3_info: AccountInfo = (&account3_key, true, &mut account3_account).into(); - let multisig_key = pubkey_rand(); + let multisig_key = Pubkey::new_unique(); let mut multisig_account = SolanaAccount::new( multisig_minimum_balance(), Multisig::get_packed_len(), &program_id, ); let multisig_info: AccountInfo = (&multisig_key, true, &mut multisig_account).into(); - let owner_key = pubkey_rand(); + let owner_key = Pubkey::new_unique(); let mut owner_account = SolanaAccount::default(); let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into(); - let mint_key = pubkey_rand(); + let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mint_info: AccountInfo = (&mint_key, false, &mut mint_account).into(); @@ -2208,26 +2200,26 @@ mod tests { #[test] fn test_approve() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); + let program_id = Pubkey::new_unique(); + let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let account2_key = pubkey_rand(); + let account2_key = Pubkey::new_unique(); let mut account2_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let delegate_key = pubkey_rand(); + let delegate_key = Pubkey::new_unique(); let mut delegate_account = SolanaAccount::default(); - let owner_key = pubkey_rand(); + let owner_key = Pubkey::new_unique(); let mut owner_account = SolanaAccount::default(); - let owner2_key = pubkey_rand(); + let owner2_key = Pubkey::new_unique(); let mut owner2_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); + let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mut rent_sysvar = rent_sysvar(); @@ -2413,16 +2405,16 @@ mod tests { #[test] fn test_set_authority_dups() { - let program_id = pubkey_rand(); - let account1_key = pubkey_rand(); + let program_id = Pubkey::new_unique(); + let account1_key = Pubkey::new_unique(); let mut account1_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); - let owner_key = pubkey_rand(); - let mint_key = pubkey_rand(); + let owner_key = Pubkey::new_unique(); + let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mint_info: AccountInfo = (&mint_key, true, &mut mint_account).into(); @@ -2516,28 +2508,28 @@ mod tests { #[test] fn test_set_authority() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); + let program_id = Pubkey::new_unique(); + let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let account2_key = pubkey_rand(); + let account2_key = Pubkey::new_unique(); let mut account2_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let owner_key = pubkey_rand(); + let owner_key = Pubkey::new_unique(); let mut owner_account = SolanaAccount::default(); - let owner2_key = pubkey_rand(); + let owner2_key = Pubkey::new_unique(); let mut owner2_account = SolanaAccount::default(); - let owner3_key = pubkey_rand(); - let mint_key = pubkey_rand(); + let owner3_key = Pubkey::new_unique(); + let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mint2_key = pubkey_rand(); + let mint2_key = Pubkey::new_unique(); let mut mint2_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mut rent_sysvar = rent_sysvar(); @@ -2855,18 +2847,18 @@ mod tests { #[test] fn test_mint_to_dups() { - let program_id = pubkey_rand(); - let account1_key = pubkey_rand(); + let program_id = Pubkey::new_unique(); + let account1_key = Pubkey::new_unique(); let mut account1_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); - let owner_key = pubkey_rand(); + let owner_key = Pubkey::new_unique(); let mut owner_account = SolanaAccount::default(); let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into(); - let mint_key = pubkey_rand(); + let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mint_info: AccountInfo = (&mint_key, true, &mut mint_account).into(); @@ -2951,40 +2943,40 @@ mod tests { #[test] fn test_mint_to() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); + let program_id = Pubkey::new_unique(); + let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let account2_key = pubkey_rand(); + let account2_key = Pubkey::new_unique(); let mut account2_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let account3_key = pubkey_rand(); + let account3_key = Pubkey::new_unique(); let mut account3_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let mismatch_key = pubkey_rand(); + let mismatch_key = Pubkey::new_unique(); let mut mismatch_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let owner_key = pubkey_rand(); + let owner_key = Pubkey::new_unique(); let mut owner_account = SolanaAccount::default(); - let owner2_key = pubkey_rand(); + let owner2_key = Pubkey::new_unique(); let mut owner2_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); + let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mint2_key = pubkey_rand(); - let uninitialized_key = pubkey_rand(); + let mint2_key = Pubkey::new_unique(); + let uninitialized_key = Pubkey::new_unique(); let mut uninitialized_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), @@ -3154,18 +3146,18 @@ mod tests { #[test] fn test_burn_dups() { - let program_id = pubkey_rand(); - let account1_key = pubkey_rand(); + let program_id = Pubkey::new_unique(); + let account1_key = Pubkey::new_unique(); let mut account1_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); - let owner_key = pubkey_rand(); + let owner_key = Pubkey::new_unique(); let mut owner_account = SolanaAccount::default(); let owner_info: AccountInfo = (&owner_key, true, &mut owner_account).into(); - let mint_key = pubkey_rand(); + let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mint_info: AccountInfo = (&mint_key, true, &mut mint_account).into(); @@ -3354,41 +3346,41 @@ mod tests { #[test] fn test_burn() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); + let program_id = Pubkey::new_unique(); + let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let account2_key = pubkey_rand(); + let account2_key = Pubkey::new_unique(); let mut account2_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let account3_key = pubkey_rand(); + let account3_key = Pubkey::new_unique(); let mut account3_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let delegate_key = pubkey_rand(); + let delegate_key = Pubkey::new_unique(); let mut delegate_account = SolanaAccount::default(); - let mismatch_key = pubkey_rand(); + let mismatch_key = Pubkey::new_unique(); let mut mismatch_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let owner_key = pubkey_rand(); + let owner_key = Pubkey::new_unique(); let mut owner_account = SolanaAccount::default(); - let owner2_key = pubkey_rand(); + let owner2_key = Pubkey::new_unique(); let mut owner2_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); + let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let mint2_key = pubkey_rand(); + let mint2_key = Pubkey::new_unique(); let mut rent_sysvar = rent_sysvar(); // create new mint @@ -3619,34 +3611,34 @@ mod tests { #[test] fn test_multisig() { - let program_id = pubkey_rand(); - let mint_key = pubkey_rand(); + let program_id = Pubkey::new_unique(); + let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let account_key = pubkey_rand(); + let account_key = Pubkey::new_unique(); let mut account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let account2_key = pubkey_rand(); + let account2_key = Pubkey::new_unique(); let mut account2_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let owner_key = pubkey_rand(); + let owner_key = Pubkey::new_unique(); let mut owner_account = SolanaAccount::default(); - let multisig_key = pubkey_rand(); + let multisig_key = Pubkey::new_unique(); let mut multisig_account = SolanaAccount::new(42, Multisig::get_packed_len(), &program_id); - let multisig_delegate_key = pubkey_rand(); + let multisig_delegate_key = Pubkey::new_unique(); let mut multisig_delegate_account = SolanaAccount::new( multisig_minimum_balance(), Multisig::get_packed_len(), &program_id, ); - let signer_keys = vec![pubkey_rand(); MAX_SIGNERS]; - let signer_key_refs: Vec<&Pubkey> = signer_keys.iter().map(|key| key).collect(); + let signer_keys = vec![Pubkey::new_unique(); MAX_SIGNERS]; + let signer_key_refs: Vec<&Pubkey> = signer_keys.iter().collect(); let mut signer_accounts = vec![SolanaAccount::new(0, 0, &program_id); MAX_SIGNERS]; let mut rent_sysvar = rent_sysvar(); @@ -3911,13 +3903,13 @@ mod tests { .unwrap(); // freeze account - let account3_key = pubkey_rand(); + let account3_key = Pubkey::new_unique(); let mut account3_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let mint2_key = pubkey_rand(); + let mint2_key = Pubkey::new_unique(); let mut mint2_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); do_process_instruction( @@ -4023,11 +4015,11 @@ mod tests { #[test] fn test_validate_owner() { - let program_id = pubkey_rand(); - let owner_key = pubkey_rand(); + let program_id = Pubkey::new_unique(); + let owner_key = Pubkey::new_unique(); let mut signer_keys = [Pubkey::default(); MAX_SIGNERS]; for signer_key in signer_keys.iter_mut().take(MAX_SIGNERS) { - *signer_key = pubkey_rand(); + *signer_key = Pubkey::new_unique(); } let mut signer_lamports = 0; let mut signer_data = vec![]; @@ -4183,23 +4175,23 @@ mod tests { #[test] fn test_close_account_dups() { - let program_id = pubkey_rand(); - let account1_key = pubkey_rand(); + let program_id = Pubkey::new_unique(); + let account1_key = Pubkey::new_unique(); let mut account1_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); - let account2_key = pubkey_rand(); + let account2_key = Pubkey::new_unique(); let mut account2_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); let account2_info: AccountInfo = (&account2_key, true, &mut account2_account).into(); - let owner_key = pubkey_rand(); - let mint_key = pubkey_rand(); + let owner_key = Pubkey::new_unique(); + let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mint_info: AccountInfo = (&mint_key, false, &mut mint_account).into(); @@ -4269,31 +4261,31 @@ mod tests { #[test] fn test_close_account() { - let program_id = pubkey_rand(); - let mint_key = pubkey_rand(); + let program_id = Pubkey::new_unique(); + let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let account_key = pubkey_rand(); + let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let account2_key = pubkey_rand(); + let account2_key = Pubkey::new_unique(); let mut account2_account = SolanaAccount::new( account_minimum_balance() + 42, Account::get_packed_len(), &program_id, ); - let account3_key = pubkey_rand(); + let account3_key = Pubkey::new_unique(); let mut account3_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let owner_key = pubkey_rand(); + let owner_key = Pubkey::new_unique(); let mut owner_account = SolanaAccount::default(); - let owner2_key = pubkey_rand(); + let owner2_key = Pubkey::new_unique(); let mut owner2_account = SolanaAccount::default(); let mut rent_sysvar = rent_sysvar(); @@ -4410,13 +4402,13 @@ mod tests { assert_eq!(account.amount, 0); // fund and initialize new non-native account to test close authority - let account_key = pubkey_rand(); + let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let owner2_key = pubkey_rand(); + let owner2_key = Pubkey::new_unique(); let mut owner2_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), @@ -4498,24 +4490,24 @@ mod tests { #[test] fn test_native_token() { - let program_id = pubkey_rand(); + let program_id = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); - let account_key = pubkey_rand(); + let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance() + 40, Account::get_packed_len(), &program_id, ); - let account2_key = pubkey_rand(); + let account2_key = Pubkey::new_unique(); let mut account2_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let account3_key = pubkey_rand(); + let account3_key = Pubkey::new_unique(); let mut account3_account = SolanaAccount::new(account_minimum_balance(), 0, &program_id); - let owner_key = pubkey_rand(); + let owner_key = Pubkey::new_unique(); let mut owner_account = SolanaAccount::default(); let mut rent_sysvar = rent_sysvar(); @@ -4579,7 +4571,7 @@ mod tests { ); // burn unsupported - let bogus_mint_key = pubkey_rand(); + let bogus_mint_key = Pubkey::new_unique(); let mut bogus_mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); do_process_instruction( @@ -4675,26 +4667,26 @@ mod tests { #[test] fn test_overflow() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); + let program_id = Pubkey::new_unique(); + let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let account2_key = pubkey_rand(); + let account2_key = Pubkey::new_unique(); let mut account2_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let owner_key = pubkey_rand(); + let owner_key = Pubkey::new_unique(); let mut owner_account = SolanaAccount::default(); - let owner2_key = pubkey_rand(); + let owner2_key = Pubkey::new_unique(); let mut owner2_account = SolanaAccount::default(); - let mint_owner_key = pubkey_rand(); + let mint_owner_key = Pubkey::new_unique(); let mut mint_owner_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); + let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mut rent_sysvar = rent_sysvar(); @@ -4852,22 +4844,22 @@ mod tests { #[test] fn test_frozen() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); + let program_id = Pubkey::new_unique(); + let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let account2_key = pubkey_rand(); + let account2_key = Pubkey::new_unique(); let mut account2_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let owner_key = pubkey_rand(); + let owner_key = Pubkey::new_unique(); let mut owner_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); + let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mut rent_sysvar = rent_sysvar(); @@ -4964,7 +4956,7 @@ mod tests { let mut account = Account::unpack_unchecked(&account_account.data).unwrap(); account.state = AccountState::Frozen; Account::pack(account, &mut account_account.data).unwrap(); - let delegate_key = pubkey_rand(); + let delegate_key = Pubkey::new_unique(); let mut delegate_account = SolanaAccount::default(); assert_eq!( Err(TokenError::AccountFrozen.into()), @@ -5000,7 +4992,7 @@ mod tests { ); // no set authority if account is frozen - let new_owner_key = pubkey_rand(); + let new_owner_key = Pubkey::new_unique(); assert_eq!( Err(TokenError::AccountFrozen.into()), do_process_instruction( @@ -5038,16 +5030,16 @@ mod tests { #[test] fn test_freeze_thaw_dups() { - let program_id = pubkey_rand(); - let account1_key = pubkey_rand(); + let program_id = Pubkey::new_unique(); + let account1_key = Pubkey::new_unique(); let mut account1_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); - let owner_key = pubkey_rand(); - let mint_key = pubkey_rand(); + let owner_key = Pubkey::new_unique(); + let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mint_info: AccountInfo = (&mint_key, true, &mut mint_account).into(); @@ -5102,20 +5094,20 @@ mod tests { #[test] fn test_freeze_account() { - let program_id = pubkey_rand(); - let account_key = pubkey_rand(); + let program_id = Pubkey::new_unique(); + let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance(), Account::get_packed_len(), &program_id, ); - let account_owner_key = pubkey_rand(); + let account_owner_key = Pubkey::new_unique(); let mut account_owner_account = SolanaAccount::default(); - let owner_key = pubkey_rand(); + let owner_key = Pubkey::new_unique(); let mut owner_account = SolanaAccount::default(); - let owner2_key = pubkey_rand(); + let owner2_key = Pubkey::new_unique(); let mut owner2_account = SolanaAccount::default(); - let mint_key = pubkey_rand(); + let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let mut rent_sysvar = rent_sysvar(); diff --git a/program/src/state.rs b/program/src/state.rs index 3622471..c35f342 100644 --- a/program/src/state.rs +++ b/program/src/state.rs @@ -3,7 +3,7 @@ use crate::instruction::MAX_SIGNERS; use arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs}; use num_enum::TryFromPrimitive; -use solana_sdk::{ +use solana_program::{ program_error::ProgramError, program_option::COption, program_pack::{IsInitialized, Pack, Sealed}, From af8617ed49c52dc270d1326d6ab828fe4db97b03 Mon Sep 17 00:00:00 2001 From: Jack May Date: Mon, 26 Oct 2020 10:24:04 -0700 Subject: [PATCH 080/248] Refer to CreateAccount --- program/src/instruction.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 1d26643..ad205af 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -23,7 +23,7 @@ pub enum TokenInstruction { /// Initializes a new mint and optionally deposits all the newly minted tokens in an account. /// /// The `InitializeMint` instruction requires no signers and MUST be included within - /// the same Transaction as the system program's `CreateInstruction` that creates the account + /// the same Transaction as the system program's `CreateAccount` instruction that creates the account /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. /// /// Accounts expected by this instruction: @@ -45,7 +45,7 @@ pub enum TokenInstruction { /// initialized before this command can succeed. /// /// The `InitializeAccount` instruction requires no signers and MUST be included within - /// the same Transaction as the system program's `CreateInstruction` that creates the account + /// the same Transaction as the system program's `CreateAccount` instruction that creates the account /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. /// /// Accounts expected by this instruction: @@ -62,7 +62,7 @@ pub enum TokenInstruction { /// number of signers (M) required to validate this multisignature account. /// /// The `InitializeMultisig` instruction requires no signers and MUST be included within - /// the same Transaction as the system program's `CreateInstruction` that creates the account + /// the same Transaction as the system program's `CreateAccount` instruction that creates the account /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. /// /// Accounts expected by this instruction: From 1b0444fef8ea4a8a92dd4870ff4831c21576590c Mon Sep 17 00:00:00 2001 From: Jack May Date: Mon, 26 Oct 2020 18:04:39 -0700 Subject: [PATCH 081/248] reformat token instruction comments (#732) --- program/src/instruction.rs | 111 ++++++++++++++++++++++--------------- 1 file changed, 65 insertions(+), 46 deletions(-) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index ad205af..05d2d50 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -20,11 +20,14 @@ pub const MAX_SIGNERS: usize = 11; #[repr(C)] #[derive(Clone, Debug, PartialEq)] pub enum TokenInstruction { - /// Initializes a new mint and optionally deposits all the newly minted tokens in an account. + /// Initializes a new mint and optionally deposits all the newly minted + /// tokens in an account. /// - /// The `InitializeMint` instruction requires no signers and MUST be included within - /// the same Transaction as the system program's `CreateAccount` instruction that creates the account - /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. + /// The `InitializeMint` instruction requires no signers and MUST be + /// included within the same Transaction as the system program's + /// `CreateAccount` instruction that creates the account being initialized. + /// Otherwise another party can acquire ownership of the uninitialized + /// account. /// /// Accounts expected by this instruction: /// @@ -39,14 +42,17 @@ pub enum TokenInstruction { /// The freeze authority/multisignature of the mint. freeze_authority: COption, }, - /// Initializes a new account to hold tokens. If this account is associated with the native - /// mint then the token balance of the initialized account will be equal to the amount of SOL - /// in the account. If this account is associated with another mint, that mint must be - /// initialized before this command can succeed. + /// Initializes a new account to hold tokens. If this account is associated + /// with the native mint then the token balance of the initialized account + /// will be equal to the amount of SOL in the account. If this account is + /// associated with another mint, that mint must be initialized before this + /// command can succeed. /// - /// The `InitializeAccount` instruction requires no signers and MUST be included within - /// the same Transaction as the system program's `CreateAccount` instruction that creates the account - /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. + /// The `InitializeAccount` instruction requires no signers and MUST be + /// included within the same Transaction as the system program's + /// `CreateAccount` instruction that creates the account being initialized. + /// Otherwise another party can acquire ownership of the uninitialized + /// account. /// /// Accounts expected by this instruction: /// @@ -57,26 +63,32 @@ pub enum TokenInstruction { InitializeAccount, /// Initializes a multisignature account with N provided signers. /// - /// Multisignature accounts can used in place of any single owner/delegate accounts in any - /// token instruction that require an owner/delegate to be present. The variant field represents the - /// number of signers (M) required to validate this multisignature account. + /// Multisignature accounts can used in place of any single owner/delegate + /// accounts in any token instruction that require an owner/delegate to be + /// present. The variant field represents the number of signers (M) + /// required to validate this multisignature account. /// - /// The `InitializeMultisig` instruction requires no signers and MUST be included within - /// the same Transaction as the system program's `CreateAccount` instruction that creates the account - /// being initialized. Otherwise another party can acquire ownership of the uninitialized account. + /// The `InitializeMultisig` instruction requires no signers and MUST be + /// included within the same Transaction as the system program's + /// `CreateAccount` instruction that creates the account being initialized. + /// Otherwise another party can acquire ownership of the uninitialized + /// account. /// /// Accounts expected by this instruction: /// /// 0. `[writable]` The multisignature account to initialize. /// 1. `[]` Rent sysvar - /// 2. ..2+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. + /// 2. ..2+N. `[]` The signer accounts, must equal to N where 1 <= N <= + /// 11. InitializeMultisig { - /// The number of signers (M) required to validate this multisignature account. + /// The number of signers (M) required to validate this multisignature + /// account. m: u8, }, - /// Transfers tokens from one account to another either directly or via a delegate. If this - /// account is associated with the native mint then equal amounts of SOL and Tokens will be - /// transferred to the destination account. + /// Transfers tokens from one account to another either directly or via a + /// delegate. If this account is associated with the native mint then equal + /// amounts of SOL and Tokens will be transferred to the destination + /// account. /// /// Accounts expected by this instruction: /// @@ -94,8 +106,8 @@ pub enum TokenInstruction { /// The amount of tokens to transfer. amount: u64, }, - /// Approves a delegate. A delegate is given the authority over - /// tokens on behalf of the source account's owner. + /// Approves a delegate. A delegate is given the authority over tokens on + /// behalf of the source account's owner. /// /// Accounts expected by this instruction: /// @@ -144,7 +156,8 @@ pub enum TokenInstruction { /// The new authority new_authority: COption, }, - /// Mints new tokens to an account. The native mint does not support minting. + /// Mints new tokens to an account. The native mint does not support + /// minting. /// /// Accounts expected by this instruction: /// @@ -162,8 +175,8 @@ pub enum TokenInstruction { /// The amount of new tokens to mint. amount: u64, }, - /// Burns tokens by removing them from an account. `Burn` does not support accounts - /// associated with the native mint, use `CloseAccount` instead. + /// Burns tokens by removing them from an account. `Burn` does not support + /// accounts associated with the native mint, use `CloseAccount` instead. /// /// Accounts expected by this instruction: /// @@ -197,7 +210,8 @@ pub enum TokenInstruction { /// 2. `[]` The account's multisignature owner. /// 3. ..3+M `[signer]` M signer accounts. CloseAccount, - /// Freeze an Initialized account using the Mint's freeze_authority (if set). + /// Freeze an Initialized account using the Mint's freeze_authority (if + /// set). /// /// Accounts expected by this instruction: /// @@ -228,13 +242,14 @@ pub enum TokenInstruction { /// 3. ..3+M `[signer]` M signer accounts. ThawAccount, - /// Transfers tokens from one account to another either directly or via a delegate. If this - /// account is associated with the native mint then equal amounts of SOL and Tokens will be - /// transferred to the destination account. + /// Transfers tokens from one account to another either directly or via a + /// delegate. If this account is associated with the native mint then equal + /// amounts of SOL and Tokens will be transferred to the destination + /// account. /// - /// This instruction differs from Transfer in that the token mint and decimals value is - /// checked by the caller. This may be useful when creating transactions offline or within a - /// hardware wallet. + /// This instruction differs from Transfer in that the token mint and + /// decimals value is checked by the caller. This may be useful when + /// creating transactions offline or within a hardware wallet. /// /// Accounts expected by this instruction: /// @@ -256,12 +271,12 @@ pub enum TokenInstruction { /// Expected number of base 10 digits to the right of the decimal place. decimals: u8, }, - /// Approves a delegate. A delegate is given the authority over - /// tokens on behalf of the source account's owner. + /// Approves a delegate. A delegate is given the authority over tokens on + /// behalf of the source account's owner. /// - /// This instruction differs from Approve in that the token mint and decimals value is checked - /// by the caller. This may be useful when creating transactions offline or within a hardware - /// wallet. + /// This instruction differs from Approve in that the token mint and + /// decimals value is checked by the caller. This may be useful when + /// creating transactions offline or within a hardware wallet. /// /// Accounts expected by this instruction: /// @@ -283,10 +298,12 @@ pub enum TokenInstruction { /// Expected number of base 10 digits to the right of the decimal place. decimals: u8, }, - /// Mints new tokens to an account. The native mint does not support minting. + /// Mints new tokens to an account. The native mint does not support + /// minting. /// - /// This instruction differs from MintTo in that the decimals value is checked by the - /// caller. This may be useful when creating transactions offline or within a hardware wallet. + /// This instruction differs from MintTo in that the decimals value is + /// checked by the caller. This may be useful when creating transactions + /// offline or within a hardware wallet. /// /// Accounts expected by this instruction: /// @@ -306,11 +323,13 @@ pub enum TokenInstruction { /// Expected number of base 10 digits to the right of the decimal place. decimals: u8, }, - /// Burns tokens by removing them from an account. `BurnChecked` does not support accounts - /// associated with the native mint, use `CloseAccount` instead. + /// Burns tokens by removing them from an account. `BurnChecked` does not + /// support accounts associated with the native mint, use `CloseAccount` + /// instead. /// - /// This instruction differs from Burn in that the decimals value is checked by the caller. - /// This may be useful when creating transactions offline or within a hardware wallet. + /// This instruction differs from Burn in that the decimals value is checked + /// by the caller. This may be useful when creating transactions offline or + /// within a hardware wallet. /// /// Accounts expected by this instruction: /// From 591d8e76ebba8070bac821a17465fe740ac561dd Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Sat, 31 Oct 2020 19:10:33 -0600 Subject: [PATCH 082/248] Clippy suggestions (#765) --- program/src/instruction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 05d2d50..21d712c 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -1057,7 +1057,7 @@ pub fn burn_checked( /// Utility function that checks index is between MIN_SIGNERS and MAX_SIGNERS pub fn is_valid_signer_index(index: usize) -> bool { - !(index < MIN_SIGNERS || index > MAX_SIGNERS) + (MIN_SIGNERS..=MAX_SIGNERS).contains(&index) } #[cfg(test)] From d2b295c789d8a973319ad423bbc5b857737e94d6 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Sat, 31 Oct 2020 21:45:09 -0700 Subject: [PATCH 083/248] Update to solana v1.4.4 --- program/Cargo.toml | 8 ++++---- program/src/processor.rs | 10 ++++------ 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 6e20a1a..c44e218 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -12,15 +12,15 @@ exclude = ["js/**"] exclude_entrypoint = [] [dependencies] +arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" -remove_dir_all = "=0.5.0" -solana-program = "1.4.3" -thiserror = "1.0" -arrayref = "0.3.6" num_enum = "0.5.1" +solana-program = "1.4.4" +thiserror = "1.0" [dev-dependencies] +solana-sdk = "1.4.4" [lib] crate-type = ["cdylib", "lib"] diff --git a/program/src/processor.rs b/program/src/processor.rs index c9b5e10..da46e71 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -1,7 +1,5 @@ //! Program state processor -//#![cfg(feature = "program")] - use crate::{ error::TokenError, instruction::{is_valid_signer_index, AuthorityType, TokenInstruction, MAX_SIGNERS}, @@ -784,9 +782,9 @@ impl PrintProgramError for TokenError { mod tests { use super::*; use crate::instruction::*; - use solana_program::{ - account::Account as SolanaAccount, account_info::create_is_signer_account_infos, - clock::Epoch, instruction::Instruction, sysvar::rent, + use solana_program::{clock::Epoch, instruction::Instruction, sysvar::rent}; + use solana_sdk::account::{ + create_account, create_is_signer_account_infos, Account as SolanaAccount, }; fn do_process_instruction( @@ -816,7 +814,7 @@ mod tests { } fn rent_sysvar() -> SolanaAccount { - rent::create_account(42, &Rent::default()) + create_account(&Rent::default(), 42) } fn mint_minimum_balance() -> u64 { From 224b796022619b7d6a8f9118a8ca1685a4f37fe6 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Sun, 1 Nov 2020 00:22:04 -0700 Subject: [PATCH 084/248] Drop lifetimes --- program/src/entrypoint.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/src/entrypoint.rs b/program/src/entrypoint.rs index b255513..5331575 100644 --- a/program/src/entrypoint.rs +++ b/program/src/entrypoint.rs @@ -7,9 +7,9 @@ use solana_program::{ }; entrypoint!(process_instruction); -fn process_instruction<'a>( +fn process_instruction( program_id: &Pubkey, - accounts: &'a [AccountInfo<'a>], + accounts: &[AccountInfo], instruction_data: &[u8], ) -> ProgramResult { if let Err(error) = Processor::process(program_id, accounts, instruction_data) { From 324b8b2513efe66afc88abb625db7cff7de99e09 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Tue, 3 Nov 2020 09:33:32 -0800 Subject: [PATCH 085/248] Back to no-entrypoint feature name --- program/Cargo.toml | 2 +- program/src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index c44e218..eb76c04 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -9,7 +9,7 @@ edition = "2018" exclude = ["js/**"] [features] -exclude_entrypoint = [] +no-entrypoint = [] [dependencies] arrayref = "0.3.6" diff --git a/program/src/lib.rs b/program/src/lib.rs index fc9707d..a561e30 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -9,7 +9,7 @@ pub mod native_mint; pub mod processor; pub mod state; -#[cfg(not(feature = "exclude_entrypoint"))] +#[cfg(not(feature = "no-entrypoint"))] mod entrypoint; // Export current sdk types for downstream users building with a different sdk version From 0ef0667bd80a55647604a70db106d2de96e5d64d Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Wed, 4 Nov 2020 10:29:16 -0800 Subject: [PATCH 086/248] Bump spl-token to v3.0.0 --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index eb76c04..7f3860e 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-token" -version = "2.0.8" +version = "3.0.0" description = "Solana Program Library Token" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" From 00c2148cd45e003fcc8986d10961fa35e7581fbc Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Wed, 4 Nov 2020 11:34:17 -0800 Subject: [PATCH 087/248] Limit docs.rs builds --- program/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/program/Cargo.toml b/program/Cargo.toml index 7f3860e..fcb7cd9 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -24,3 +24,6 @@ solana-sdk = "1.4.4" [lib] crate-type = ["cdylib", "lib"] + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] From 7422754d60a940b5ddd6ce0db3f8902808c09a85 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Fri, 6 Nov 2020 09:18:20 -0800 Subject: [PATCH 088/248] Bump solana version to v1.4.5 --- program/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index fcb7cd9..5057e12 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,11 +16,11 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.1" -solana-program = "1.4.4" +solana-program = "1.4.5" thiserror = "1.0" [dev-dependencies] -solana-sdk = "1.4.4" +solana-sdk = "1.4.5" [lib] crate-type = ["cdylib", "lib"] From 4b76471221bdc8ffd9305988baec338d205d1a17 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Nov 2020 12:39:52 +0000 Subject: [PATCH 089/248] Bump solana-program from 1.4.5 to 1.4.6 (#826) Bumps [solana-program](https://github.com/solana-labs/solana) from 1.4.5 to 1.4.6. - [Release notes](https://github.com/solana-labs/solana/releases) - [Changelog](https://github.com/solana-labs/solana/blob/master/RELEASE.md) - [Commits](https://github.com/solana-labs/solana/compare/v1.4.5...v1.4.6) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 5057e12..5907704 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,7 +16,7 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.1" -solana-program = "1.4.5" +solana-program = "1.4.6" thiserror = "1.0" [dev-dependencies] From f4c265569cfe40f603daa8444057428baa5c3c2e Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Thu, 12 Nov 2020 09:19:39 -0800 Subject: [PATCH 090/248] Upgrade to Solana 1.4.7 --- program/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 5907704..3fc6997 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,11 +16,11 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.1" -solana-program = "1.4.6" +solana-program = "1.4.7" thiserror = "1.0" [dev-dependencies] -solana-sdk = "1.4.5" +solana-sdk = "1.4.7" [lib] crate-type = ["cdylib", "lib"] From b0128444879e4ccc23a2fac0c04b9e08e2f81d87 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Fri, 13 Nov 2020 17:57:21 -0800 Subject: [PATCH 091/248] Update to Solana 1.4.8 --- program/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 3fc6997..8362665 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,11 +16,11 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.1" -solana-program = "1.4.7" +solana-program = "1.4.8" thiserror = "1.0" [dev-dependencies] -solana-sdk = "1.4.7" +solana-sdk = "1.4.8" [lib] crate-type = ["cdylib", "lib"] From a614bfc42ec885b01e7fce4e70c3832382df7c30 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Tue, 17 Nov 2020 17:26:59 -0800 Subject: [PATCH 092/248] Bump solana version to v1.4.9 --- program/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 8362665..42e6318 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,11 +16,11 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.1" -solana-program = "1.4.8" +solana-program = "1.4.9" thiserror = "1.0" [dev-dependencies] -solana-sdk = "1.4.8" +solana-sdk = "1.4.9" [lib] crate-type = ["cdylib", "lib"] From 6b7f66176725c5276ab2322956a1693aff1c3df8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Dec 2020 12:54:42 +0000 Subject: [PATCH 093/248] Bump solana-program from 1.4.9 to 1.4.13 (#916) Bumps [solana-program](https://github.com/solana-labs/solana) from 1.4.9 to 1.4.13. - [Release notes](https://github.com/solana-labs/solana/releases) - [Changelog](https://github.com/solana-labs/solana/blob/master/RELEASE.md) - [Commits](https://github.com/solana-labs/solana/compare/v1.4.9...v1.4.13) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 42e6318..d1c1b2f 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,7 +16,7 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.1" -solana-program = "1.4.9" +solana-program = "1.4.13" thiserror = "1.0" [dev-dependencies] From ac63c052d675eb8e0fbc2b6f1fd1585bbed02223 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Wed, 2 Dec 2020 18:37:53 -0800 Subject: [PATCH 094/248] Upgrade to Solana v1.4.14 --- program/Cargo.toml | 4 +-- program/src/processor.rs | 74 +++++++++++++++++++--------------------- 2 files changed, 38 insertions(+), 40 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index d1c1b2f..458201b 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,11 +16,11 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.1" -solana-program = "1.4.13" +solana-program = "1.4.14" thiserror = "1.0" [dev-dependencies] -solana-sdk = "1.4.9" +solana-sdk = "1.4.14" [lib] crate-type = ["cdylib", "lib"] diff --git a/program/src/processor.rs b/program/src/processor.rs index da46e71..a1476ba 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -10,7 +10,7 @@ use solana_program::{ account_info::{next_account_info, AccountInfo}, decode_error::DecodeError, entrypoint::ProgramResult, - info, + msg, program_error::{PrintProgramError, ProgramError}, program_option::COption, program_pack::{IsInitialized, Pack}, @@ -628,70 +628,70 @@ impl Processor { mint_authority, freeze_authority, } => { - info!("Instruction: InitializeMint"); + msg!("Instruction: InitializeMint"); Self::process_initialize_mint(accounts, decimals, mint_authority, freeze_authority) } TokenInstruction::InitializeAccount => { - info!("Instruction: InitializeAccount"); + msg!("Instruction: InitializeAccount"); Self::process_initialize_account(accounts) } TokenInstruction::InitializeMultisig { m } => { - info!("Instruction: InitializeMultisig"); + msg!("Instruction: InitializeMultisig"); Self::process_initialize_multisig(accounts, m) } TokenInstruction::Transfer { amount } => { - info!("Instruction: Transfer"); + msg!("Instruction: Transfer"); Self::process_transfer(program_id, accounts, amount, None) } TokenInstruction::Approve { amount } => { - info!("Instruction: Approve"); + msg!("Instruction: Approve"); Self::process_approve(program_id, accounts, amount, None) } TokenInstruction::Revoke => { - info!("Instruction: Revoke"); + msg!("Instruction: Revoke"); Self::process_revoke(program_id, accounts) } TokenInstruction::SetAuthority { authority_type, new_authority, } => { - info!("Instruction: SetAuthority"); + msg!("Instruction: SetAuthority"); Self::process_set_authority(program_id, accounts, authority_type, new_authority) } TokenInstruction::MintTo { amount } => { - info!("Instruction: MintTo"); + msg!("Instruction: MintTo"); Self::process_mint_to(program_id, accounts, amount, None) } TokenInstruction::Burn { amount } => { - info!("Instruction: Burn"); + msg!("Instruction: Burn"); Self::process_burn(program_id, accounts, amount, None) } TokenInstruction::CloseAccount => { - info!("Instruction: CloseAccount"); + msg!("Instruction: CloseAccount"); Self::process_close_account(program_id, accounts) } TokenInstruction::FreezeAccount => { - info!("Instruction: FreezeAccount"); + msg!("Instruction: FreezeAccount"); Self::process_toggle_freeze_account(program_id, accounts, true) } TokenInstruction::ThawAccount => { - info!("Instruction: FreezeAccount"); + msg!("Instruction: FreezeAccount"); Self::process_toggle_freeze_account(program_id, accounts, false) } TokenInstruction::TransferChecked { amount, decimals } => { - info!("Instruction: TransferChecked"); + msg!("Instruction: TransferChecked"); Self::process_transfer(program_id, accounts, amount, Some(decimals)) } TokenInstruction::ApproveChecked { amount, decimals } => { - info!("Instruction: ApproveChecked"); + msg!("Instruction: ApproveChecked"); Self::process_approve(program_id, accounts, amount, Some(decimals)) } TokenInstruction::MintToChecked { amount, decimals } => { - info!("Instruction: MintToChecked"); + msg!("Instruction: MintToChecked"); Self::process_mint_to(program_id, accounts, amount, Some(decimals)) } TokenInstruction::BurnChecked { amount, decimals } => { - info!("Instruction: BurnChecked"); + msg!("Instruction: BurnChecked"); Self::process_burn(program_id, accounts, amount, Some(decimals)) } } @@ -741,38 +741,36 @@ impl PrintProgramError for TokenError { E: 'static + std::error::Error + DecodeError + PrintProgramError + FromPrimitive, { match self { - TokenError::NotRentExempt => { - info!("Error: Lamport balance below rent-exempt threshold") - } - TokenError::InsufficientFunds => info!("Error: insufficient funds"), - TokenError::InvalidMint => info!("Error: Invalid Mint"), - TokenError::MintMismatch => info!("Error: Account not associated with this Mint"), - TokenError::OwnerMismatch => info!("Error: owner does not match"), - TokenError::FixedSupply => info!("Error: the total supply of this token is fixed"), - TokenError::AlreadyInUse => info!("Error: account or token already in use"), + TokenError::NotRentExempt => msg!("Error: Lamport balance below rent-exempt threshold"), + TokenError::InsufficientFunds => msg!("Error: insufficient funds"), + TokenError::InvalidMint => msg!("Error: Invalid Mint"), + TokenError::MintMismatch => msg!("Error: Account not associated with this Mint"), + TokenError::OwnerMismatch => msg!("Error: owner does not match"), + TokenError::FixedSupply => msg!("Error: the total supply of this token is fixed"), + TokenError::AlreadyInUse => msg!("Error: account or token already in use"), TokenError::InvalidNumberOfProvidedSigners => { - info!("Error: Invalid number of provided signers") + msg!("Error: Invalid number of provided signers") } TokenError::InvalidNumberOfRequiredSigners => { - info!("Error: Invalid number of required signers") + msg!("Error: Invalid number of required signers") } - TokenError::UninitializedState => info!("Error: State is uninitialized"), + TokenError::UninitializedState => msg!("Error: State is uninitialized"), TokenError::NativeNotSupported => { - info!("Error: Instruction does not support native tokens") + msg!("Error: Instruction does not support native tokens") } TokenError::NonNativeHasBalance => { - info!("Error: Non-native account can only be closed if its balance is zero") + msg!("Error: Non-native account can only be closed if its balance is zero") } - TokenError::InvalidInstruction => info!("Error: Invalid instruction"), - TokenError::InvalidState => info!("Error: Invalid account state for operation"), - TokenError::Overflow => info!("Error: Operation overflowed"), + TokenError::InvalidInstruction => msg!("Error: Invalid instruction"), + TokenError::InvalidState => msg!("Error: Invalid account state for operation"), + TokenError::Overflow => msg!("Error: Operation overflowed"), TokenError::AuthorityTypeNotSupported => { - info!("Error: Account does not support specified authority type") + msg!("Error: Account does not support specified authority type") } - TokenError::MintCannotFreeze => info!("Error: This token mint cannot freeze accounts"), - TokenError::AccountFrozen => info!("Error: Account is frozen"), + TokenError::MintCannotFreeze => msg!("Error: This token mint cannot freeze accounts"), + TokenError::AccountFrozen => msg!("Error: Account is frozen"), TokenError::MintDecimalsMismatch => { - info!("Error: decimals different from the Mint decimals") + msg!("Error: decimals different from the Mint decimals") } } } From 82d6f88ddad4ecac4ef351c98e0a0bb880515c7a Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Wed, 16 Dec 2020 11:51:03 -0700 Subject: [PATCH 095/248] Bump {ata,memo,token-{program,cli}} to solana v1.4.17 To pick up cargo audit fixes for monorepo --- program/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 458201b..4281b5a 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,11 +16,11 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.1" -solana-program = "1.4.14" +solana-program = "1.4.17" thiserror = "1.0" [dev-dependencies] -solana-sdk = "1.4.14" +solana-sdk = "1.4.17" [lib] crate-type = ["cdylib", "lib"] From df0f7f7798acaec5109e4c5775dbf7455d5f7451 Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Wed, 16 Dec 2020 11:55:04 -0700 Subject: [PATCH 096/248] Bump token to v3.0.1 --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 4281b5a..257d0a4 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-token" -version = "3.0.0" +version = "3.0.1" description = "Solana Program Library Token" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" From 881525a9394ab47269d5560cf10668e9be369f6c Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Thu, 17 Dec 2020 16:11:56 -0800 Subject: [PATCH 097/248] Update to Solana 1.5.0 --- program/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 257d0a4..294d2a5 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,11 +16,11 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.1" -solana-program = "1.4.17" +solana-program = "1.5.0" thiserror = "1.0" [dev-dependencies] -solana-sdk = "1.4.17" +solana-sdk = "1.5.0" [lib] crate-type = ["cdylib", "lib"] From 5cb23afead72b6856e52b9c29044c5890a93e7dd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Jan 2021 13:51:29 +0000 Subject: [PATCH 098/248] build(deps): bump solana-program from 1.5.0 to 1.5.1 (#1025) Bumps [solana-program](https://github.com/solana-labs/solana) from 1.5.0 to 1.5.1. - [Release notes](https://github.com/solana-labs/solana/releases) - [Changelog](https://github.com/solana-labs/solana/blob/master/RELEASE.md) - [Commits](https://github.com/solana-labs/solana/compare/v1.5.0...v1.5.1) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 294d2a5..8c66334 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,7 +16,7 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.1" -solana-program = "1.5.0" +solana-program = "1.5.1" thiserror = "1.0" [dev-dependencies] From 66f96f6afb0cf8d49775aaaed294d1f79de4c6fc Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Tue, 26 Jan 2021 20:15:08 -0700 Subject: [PATCH 099/248] token: Fully check self-transfers --- program/src/processor.rs | 69 ++++++++++------------------------------ 1 file changed, 16 insertions(+), 53 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index a1476ba..ceea0ff 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -152,10 +152,6 @@ impl Processor { let dest_account_info = next_account_info(account_info_iter)?; let authority_info = next_account_info(account_info_iter)?; - if source_account_info.key == dest_account_info.key { - return Ok(()); - } - let mut source_account = Account::unpack(&source_account_info.data.borrow())?; let mut dest_account = Account::unpack(&dest_account_info.data.borrow())?; @@ -180,6 +176,8 @@ impl Processor { } } + let self_transfer = source_account_info.key == dest_account_info.key; + match source_account.delegate { COption::Some(ref delegate) if authority_info.key == delegate => { Self::validate_owner( @@ -191,12 +189,14 @@ impl Processor { if source_account.delegated_amount < amount { return Err(TokenError::InsufficientFunds.into()); } - source_account.delegated_amount = source_account - .delegated_amount - .checked_sub(amount) - .ok_or(TokenError::Overflow)?; - if source_account.delegated_amount == 0 { - source_account.delegate = COption::None; + if !self_transfer { + source_account.delegated_amount = source_account + .delegated_amount + .checked_sub(amount) + .ok_or(TokenError::Overflow)?; + if source_account.delegated_amount == 0 { + source_account.delegate = COption::None; + } } } _ => Self::validate_owner( @@ -207,6 +207,12 @@ impl Processor { )?, }; + // This check MUST occur just before the amounts are manipulated + // to ensure self-transfers are fully validated + if self_transfer { + return Ok(()); + } + source_account.amount = source_account .amount .checked_sub(amount) @@ -1709,49 +1715,6 @@ mod tests { let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.amount, 1000); - // bogus transfer to self using system accounts. - // - // Transfer will succeed if the source and destination accounts have the same address, - // regardless of whether it is a valid token account. - // - // This is probably wrong but transactions in the wild have been observed to do this so - // this behavior is now part of the token ABI - { - let system_account_key = Pubkey::new_unique(); - let mut system_account = SolanaAccount::new(1, 0, &Pubkey::default()); - - let instruction = transfer( - &program_id, - &system_account_key, - &system_account_key, - &owner_key, - &[], - 500, - ) - .unwrap(); - - let account_account_info = AccountInfo::from(( - &instruction.accounts[0].pubkey, - instruction.accounts[0].is_signer, - &mut system_account, - )); - let owner_account_info = AccountInfo::from(( - &instruction.accounts[2].pubkey, - instruction.accounts[2].is_signer, - &mut owner_account, - )); - Processor::process( - &instruction.program_id, - &[ - account_account_info.clone(), - account_account_info, - owner_account_info, - ], - &instruction.data, - ) - .unwrap() - } - // insufficient funds assert_eq!( Err(TokenError::InsufficientFunds.into()), From 215f7ef1210d0f02afeec6848142dc79070ffa7b Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Wed, 27 Jan 2021 22:54:01 -0700 Subject: [PATCH 100/248] token: Add a full suite of self-transfer tests --- program/src/processor.rs | 569 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 533 insertions(+), 36 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index ceea0ff..4ea40ae 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -786,7 +786,9 @@ impl PrintProgramError for TokenError { mod tests { use super::*; use crate::instruction::*; - use solana_program::{clock::Epoch, instruction::Instruction, sysvar::rent}; + use solana_program::{ + account_info::IntoAccountInfo, clock::Epoch, instruction::Instruction, sysvar::rent, + }; use solana_sdk::account::{ create_account, create_is_signer_account_infos, Account as SolanaAccount, }; @@ -1680,41 +1682,6 @@ mod tests { ) .unwrap(); - // transfer to self - { - let instruction = transfer( - &program_id, - &account_key, - &account_key, - &owner_key, - &[], - 500, - ) - .unwrap(); - let account_account_info = AccountInfo::from(( - &instruction.accounts[0].pubkey, - instruction.accounts[0].is_signer, - &mut account_account, - )); - let owner_account_info = AccountInfo::from(( - &instruction.accounts[2].pubkey, - instruction.accounts[2].is_signer, - &mut owner_account, - )); - Processor::process( - &instruction.program_id, - &[ - account_account_info.clone(), - account_account_info, - owner_account_info, - ], - &instruction.data, - ) - .unwrap() - } - let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert_eq!(account.amount, 1000); - // insufficient funds assert_eq!( Err(TokenError::InsufficientFunds.into()), @@ -1847,6 +1814,536 @@ mod tests { ); } + #[test] + fn test_self_transfer() { + let program_id = Pubkey::new_unique(); + let account_key = Pubkey::new_unique(); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account2_key = Pubkey::new_unique(); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let account3_key = Pubkey::new_unique(); + let mut account3_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let delegate_key = Pubkey::new_unique(); + let mut delegate_account = SolanaAccount::default(); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let owner2_key = Pubkey::new_unique(); + let mut owner2_account = SolanaAccount::default(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mut rent_sysvar = rent_sysvar(); + + // create mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account2_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account2_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // create another account + do_process_instruction( + initialize_account(&program_id, &account3_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account3_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // mint to account + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + + let account_info = (&account_key, false, &mut account_account).into_account_info(); + let account3_info = (&account3_key, false, &mut account3_account).into_account_info(); + let delegate_info = (&delegate_key, true, &mut delegate_account).into_account_info(); + let owner_info = (&owner_key, true, &mut owner_account).into_account_info(); + let owner2_info = (&owner2_key, true, &mut owner2_account).into_account_info(); + let mint_info = (&mint_key, false, &mut mint_account).into_account_info(); + + // transfer + let instruction = transfer( + &program_id, + &account_info.key, + &account_info.key, + &owner_info.key, + &[], + 1000, + ) + .unwrap(); + assert_eq!( + Ok(()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + account_info.clone(), + owner_info.clone(), + ], + &instruction.data, + ) + ); + // no balance change... + let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap(); + assert_eq!(account.amount, 1000); + + // transfer checked + let instruction = transfer_checked( + &program_id, + &account_info.key, + &mint_info.key, + &account_info.key, + &owner_info.key, + &[], + 1000, + 2, + ) + .unwrap(); + assert_eq!( + Ok(()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + mint_info.clone(), + account_info.clone(), + owner_info.clone(), + ], + &instruction.data, + ) + ); + // no balance change... + let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap(); + assert_eq!(account.amount, 1000); + + // missing signer + let mut owner_no_sign_info = owner_info.clone(); + let mut instruction = transfer( + &program_id, + &account_info.key, + &account_info.key, + &owner_no_sign_info.key, + &[], + 1000, + ) + .unwrap(); + instruction.accounts[2].is_signer = false; + owner_no_sign_info.is_signer = false; + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + account_info.clone(), + owner_no_sign_info.clone(), + ], + &instruction.data, + ) + ); + + // missing signer checked + let mut instruction = transfer_checked( + &program_id, + &account_info.key, + &mint_info.key, + &account_info.key, + &owner_no_sign_info.key, + &[], + 1000, + 2, + ) + .unwrap(); + instruction.accounts[3].is_signer = false; + assert_eq!( + Err(ProgramError::MissingRequiredSignature), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + mint_info.clone(), + account_info.clone(), + owner_no_sign_info, + ], + &instruction.data, + ) + ); + + // missing owner + let instruction = transfer( + &program_id, + &account_info.key, + &account_info.key, + &owner2_info.key, + &[], + 1000, + ) + .unwrap(); + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + account_info.clone(), + owner2_info.clone(), + ], + &instruction.data, + ) + ); + + // missing owner checked + let instruction = transfer_checked( + &program_id, + &account_info.key, + &mint_info.key, + &account_info.key, + &owner2_info.key, + &[], + 1000, + 2, + ) + .unwrap(); + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + mint_info.clone(), + account_info.clone(), + owner2_info.clone(), + ], + &instruction.data, + ) + ); + + // insufficient funds + let instruction = transfer( + &program_id, + &account_info.key, + &account_info.key, + &owner_info.key, + &[], + 1001, + ) + .unwrap(); + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + account_info.clone(), + owner_info.clone(), + ], + &instruction.data, + ) + ); + + // insufficient funds checked + let instruction = transfer_checked( + &program_id, + &account_info.key, + &mint_info.key, + &account_info.key, + &owner_info.key, + &[], + 1001, + 2, + ) + .unwrap(); + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + mint_info.clone(), + account_info.clone(), + owner_info.clone(), + ], + &instruction.data, + ) + ); + + // incorrect decimals + let instruction = transfer_checked( + &program_id, + &account_info.key, + &mint_info.key, + &account_info.key, + &owner_info.key, + &[], + 1, + 10, // <-- incorrect decimals + ) + .unwrap(); + assert_eq!( + Err(TokenError::MintDecimalsMismatch.into()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + mint_info.clone(), + account_info.clone(), + owner_info.clone(), + ], + &instruction.data, + ) + ); + + // incorrect mint + let instruction = transfer_checked( + &program_id, + &account_info.key, + &account3_info.key, // <-- incorrect mint + &account_info.key, + &owner_info.key, + &[], + 1, + 2, + ) + .unwrap(); + assert_eq!( + Err(TokenError::MintMismatch.into()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + account3_info.clone(), // <-- incorrect mint + account_info.clone(), + owner_info.clone(), + ], + &instruction.data, + ) + ); + + // approve delegate + let instruction = approve( + &program_id, + &account_info.key, + &delegate_info.key, + &owner_info.key, + &[], + 100, + ) + .unwrap(); + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + delegate_info.clone(), + owner_info.clone(), + ], + &instruction.data, + ) + .unwrap(); + + // delegate transfer + let instruction = transfer( + &program_id, + &account_info.key, + &account_info.key, + &delegate_info.key, + &[], + 100, + ) + .unwrap(); + assert_eq!( + Ok(()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + account_info.clone(), + delegate_info.clone(), + ], + &instruction.data, + ) + ); + // no balance change... + let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap(); + assert_eq!(account.amount, 1000); + assert_eq!(account.delegated_amount, 100); + + // delegate transfer checked + let instruction = transfer_checked( + &program_id, + &account_info.key, + &mint_info.key, + &account_info.key, + &delegate_info.key, + &[], + 100, + 2, + ) + .unwrap(); + assert_eq!( + Ok(()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + mint_info.clone(), + account_info.clone(), + delegate_info.clone(), + ], + &instruction.data, + ) + ); + // no balance change... + let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap(); + assert_eq!(account.amount, 1000); + assert_eq!(account.delegated_amount, 100); + + // delegate insufficient funds + let instruction = transfer( + &program_id, + &account_info.key, + &account_info.key, + &delegate_info.key, + &[], + 101, + ) + .unwrap(); + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + account_info.clone(), + delegate_info.clone(), + ], + &instruction.data, + ) + ); + + // delegate insufficient funds checked + let instruction = transfer_checked( + &program_id, + &account_info.key, + &mint_info.key, + &account_info.key, + &delegate_info.key, + &[], + 101, + 2, + ) + .unwrap(); + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + mint_info.clone(), + account_info.clone(), + delegate_info.clone(), + ], + &instruction.data, + ) + ); + + // owner transfer with delegate assigned + let instruction = transfer( + &program_id, + &account_info.key, + &account_info.key, + &owner_info.key, + &[], + 1000, + ) + .unwrap(); + assert_eq!( + Ok(()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + account_info.clone(), + owner_info.clone(), + ], + &instruction.data, + ) + ); + // no balance change... + let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap(); + assert_eq!(account.amount, 1000); + + // owner transfer with delegate assigned checked + let instruction = transfer_checked( + &program_id, + &account_info.key, + &mint_info.key, + &account_info.key, + &owner_info.key, + &[], + 1000, + 2, + ) + .unwrap(); + assert_eq!( + Ok(()), + Processor::process( + &instruction.program_id, + &[ + account_info.clone(), + mint_info.clone(), + account_info.clone(), + owner_info.clone(), + ], + &instruction.data, + ) + ); + // no balance change... + let account = Account::unpack_unchecked(&account_info.try_borrow_data().unwrap()).unwrap(); + assert_eq!(account.amount, 1000); + } + #[test] fn test_mintable_token_with_zero_supply() { let program_id = Pubkey::new_unique(); From 4a79f9dd6ca66192530e03d65e3679fe1cdc4412 Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Thu, 28 Jan 2021 16:31:35 -0700 Subject: [PATCH 101/248] token: Add InitializeAccount2 instruction Passes the owner as instruction data rather than on the accounts list, improving CPI ergonomics where the owner's `AccountInfo` isn't otherwise required --- program/src/instruction.rs | 59 ++++++++++++++++++++++++++++- program/src/processor.rs | 76 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 130 insertions(+), 5 deletions(-) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 21d712c..063eb7c 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -349,6 +349,20 @@ pub enum TokenInstruction { /// Expected number of base 10 digits to the right of the decimal place. decimals: u8, }, + /// Like InitializeAccount, but the owner pubkey is passed via instruction data + /// rather than the accounts list. This variant may be preferable when using + /// Cross Program Invocation from an instruction that does not need the owner's + /// `AccountInfo` otherwise. + /// + /// Accounts expected by this instruction: + /// + /// 0. `[writable]` The account to initialize. + /// 1. `[]` The mint this account will be associated with. + /// 3. `[]` Rent sysvar + InitializeAccount2 { + /// The new account's owner/multisignature. + owner: Pubkey, + }, } impl TokenInstruction { /// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html). @@ -446,6 +460,10 @@ impl TokenInstruction { Self::BurnChecked { amount, decimals } } + 16 => { + let (owner, _rest) = Self::unpack_pubkey(rest)?; + Self::InitializeAccount2 { owner } + } _ => return Err(TokenError::InvalidInstruction.into()), }) @@ -518,6 +536,10 @@ impl TokenInstruction { buf.extend_from_slice(&amount.to_le_bytes()); buf.push(decimals); } + &Self::InitializeAccount2 { owner } => { + buf.push(16); + buf.extend_from_slice(owner.as_ref()); + } }; buf } @@ -625,7 +647,7 @@ pub fn initialize_account( mint_pubkey: &Pubkey, owner_pubkey: &Pubkey, ) -> Result { - let data = TokenInstruction::InitializeAccount.pack(); // TODO do we need to return result? + let data = TokenInstruction::InitializeAccount.pack(); let accounts = vec![ AccountMeta::new(*account_pubkey, false), @@ -641,6 +663,31 @@ pub fn initialize_account( }) } +/// Creates a `InitializeAccount2` instruction. +pub fn initialize_account2( + token_program_id: &Pubkey, + account_pubkey: &Pubkey, + mint_pubkey: &Pubkey, + owner_pubkey: &Pubkey, +) -> Result { + let data = TokenInstruction::InitializeAccount2 { + owner: *owner_pubkey, + } + .pack(); + + let accounts = vec![ + AccountMeta::new(*account_pubkey, false), + AccountMeta::new_readonly(*mint_pubkey, false), + AccountMeta::new_readonly(sysvar::rent::id(), false), + ]; + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + /// Creates a `InitializeMultisig` instruction. pub fn initialize_multisig( token_program_id: &Pubkey, @@ -1214,5 +1261,15 @@ mod test { assert_eq!(packed, expect); let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); + + let check = TokenInstruction::InitializeAccount2 { + owner: Pubkey::new(&[2u8; 32]), + }; + let packed = check.pack(); + let mut expect = vec![16u8]; + expect.extend_from_slice(&[2u8; 32]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); } } diff --git a/program/src/processor.rs b/program/src/processor.rs index 4ea40ae..5d22ca5 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -52,12 +52,18 @@ impl Processor { Ok(()) } - /// Processes an [InitializeAccount](enum.TokenInstruction.html) instruction. - pub fn process_initialize_account(accounts: &[AccountInfo]) -> ProgramResult { + fn _process_initialize_account( + accounts: &[AccountInfo], + owner: Option<&Pubkey>, + ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let new_account_info = next_account_info(account_info_iter)?; let mint_info = next_account_info(account_info_iter)?; - let owner_info = next_account_info(account_info_iter)?; + let owner = if let Some(owner) = owner { + owner + } else { + next_account_info(account_info_iter)?.key + }; let new_account_info_data_len = new_account_info.data_len(); let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?; @@ -76,7 +82,7 @@ impl Processor { } account.mint = *mint_info.key; - account.owner = *owner_info.key; + account.owner = *owner; account.delegate = COption::None; account.delegated_amount = 0; account.state = AccountState::Initialized; @@ -97,6 +103,16 @@ impl Processor { Ok(()) } + /// Processes an [InitializeAccount](enum.TokenInstruction.html) instruction. + pub fn process_initialize_account(accounts: &[AccountInfo]) -> ProgramResult { + Self::_process_initialize_account(accounts, None) + } + + /// Processes an [InitializeAccount2](enum.TokenInstruction.html) instruction. + pub fn process_initialize_account2(accounts: &[AccountInfo], owner: Pubkey) -> ProgramResult { + Self::_process_initialize_account(accounts, Some(&owner)) + } + /// Processes a [InitializeMultisig](enum.TokenInstruction.html) instruction. pub fn process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult { let account_info_iter = &mut accounts.iter(); @@ -641,6 +657,10 @@ impl Processor { msg!("Instruction: InitializeAccount"); Self::process_initialize_account(accounts) } + TokenInstruction::InitializeAccount2 { owner } => { + msg!("Instruction: InitializeAccount2"); + Self::process_initialize_account2(accounts, owner) + } TokenInstruction::InitializeMultisig { m } => { msg!("Instruction: InitializeMultisig"); Self::process_initialize_multisig(accounts, m) @@ -5660,4 +5680,52 @@ mod tests { let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.state, AccountState::Initialized); } + + #[test] + fn test_initialize_account2() { + let program_id = Pubkey::new_unique(); + let account_key = Pubkey::new_unique(); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let mut account2_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mut rent_sysvar = rent_sysvar(); + + // create mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + do_process_instruction( + initialize_account2(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account2_account, &mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + assert_eq!(account_account, account2_account); + } } From d966090a48664456a820325ecbc9da271e2b5519 Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Fri, 5 Feb 2021 18:53:13 -0700 Subject: [PATCH 102/248] token: Wrap C bindings at 80 char --- program/inc/token.h | 111 ++++++++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 46 deletions(-) diff --git a/program/inc/token.h b/program/inc/token.h index 954d841..e027ac5 100644 --- a/program/inc/token.h +++ b/program/inc/token.h @@ -105,11 +105,14 @@ typedef struct Token_COption_Pubkey { */ typedef enum Token_TokenInstruction_Tag { /** - * Initializes a new mint and optionally deposits all the newly minted tokens in an account. + * Initializes a new mint and optionally deposits all the newly minted + * tokens in an account. * - * The `InitializeMint` instruction requires no signers and MUST be included within - * the same Transaction as the system program's `CreateInstruction` that creates the account - * being initialized. Otherwise another party can acquire ownership of the uninitialized account. + * The `InitializeMint` instruction requires no signers and MUST be + * included within the same Transaction as the system program's + * `CreateInstruction` that creates the account being initialized. + * Otherwise another party can acquire ownership of the uninitialized + * account. * * Accounts expected by this instruction: * @@ -119,14 +122,17 @@ typedef enum Token_TokenInstruction_Tag { */ Token_TokenInstruction_InitializeMint, /** - * Initializes a new account to hold tokens. If this account is associated with the native - * mint then the token balance of the initialized account will be equal to the amount of SOL - * in the account. If this account is associated with another mint, that mint must be - * initialized before this command can succeed. + * Initializes a new account to hold tokens. If this account is associated + * with the native mint then the token balance of the initialized account + * will be equal to the amount of SOL in the account. If this account is + * associated with another mint, that mint must be initialized before this + * command can succeed. * - * The `InitializeAccount` instruction requires no signers and MUST be included within - * the same Transaction as the system program's `CreateInstruction` that creates the account - * being initialized. Otherwise another party can acquire ownership of the uninitialized account. + * The `InitializeAccount` instruction requires no signers and MUST be + * included within the same Transaction as the system program's + * `CreateInstruction` that creates the account being initialized. + * Otherwise another party can acquire ownership of the uninitialized + * account. * * Accounts expected by this instruction: * @@ -139,25 +145,30 @@ typedef enum Token_TokenInstruction_Tag { /** * Initializes a multisignature account with N provided signers. * - * Multisignature accounts can used in place of any single owner/delegate accounts in any - * token instruction that require an owner/delegate to be present. The variant field represents the - * number of signers (M) required to validate this multisignature account. + * Multisignature accounts can used in place of any single owner/delegate + * accounts in any token instruction that require an owner/delegate to be + * present. The variant field represents the number of signers (M) + * required to validate this multisignature account. * - * The `InitializeMultisig` instruction requires no signers and MUST be included within - * the same Transaction as the system program's `CreateInstruction` that creates the account - * being initialized. Otherwise another party can acquire ownership of the uninitialized account. + * The `InitializeMultisig` instruction requires no signers and MUST be + * included within the same Transaction as the system program's + * `CreateInstruction` that creates the account being initialized. + * Otherwise another party can acquire ownership of the uninitialized + * account. * * Accounts expected by this instruction: * * 0. `[writable]` The multisignature account to initialize. * 1. `[]` Rent sysvar - * 2. ..2+N. `[]` The signer accounts, must equal to N where 1 <= N <= 11. + * 2. ..2+N. `[]` The signer accounts, must equal to N where 1 <= N <= + * 11. */ Token_TokenInstruction_InitializeMultisig, /** - * Transfers tokens from one account to another either directly or via a delegate. If this - * account is associated with the native mint then equal amounts of SOL and Tokens will be - * transferred to the destination account. + * Transfers tokens from one account to another either directly or via a + * delegate. If this account is associated with the native mint then equal + * amounts of SOL and Tokens will be transferred to the destination + * account. * * Accounts expected by this instruction: * @@ -174,8 +185,8 @@ typedef enum Token_TokenInstruction_Tag { */ Token_TokenInstruction_Transfer, /** - * Approves a delegate. A delegate is given the authority over - * tokens on behalf of the source account's owner. + * Approves a delegate. A delegate is given the authority over tokens on + * behalf of the source account's owner. * * Accounts expected by this instruction: * @@ -222,7 +233,8 @@ typedef enum Token_TokenInstruction_Tag { */ Token_TokenInstruction_SetAuthority, /** - * Mints new tokens to an account. The native mint does not support minting. + * Mints new tokens to an account. The native mint does not support + * minting. * * Accounts expected by this instruction: * @@ -239,8 +251,8 @@ typedef enum Token_TokenInstruction_Tag { */ Token_TokenInstruction_MintTo, /** - * Burns tokens by removing them from an account. `Burn` does not support accounts - * associated with the native mint, use `CloseAccount` instead. + * Burns tokens by removing them from an account. `Burn` does not support + * accounts associated with the native mint, use `CloseAccount` instead. * * Accounts expected by this instruction: * @@ -275,7 +287,8 @@ typedef enum Token_TokenInstruction_Tag { */ Token_TokenInstruction_CloseAccount, /** - * Freeze an Initialized account using the Mint's freeze_authority (if set). + * Freeze an Initialized account using the Mint's freeze_authority (if + * set). * * Accounts expected by this instruction: * @@ -309,13 +322,14 @@ typedef enum Token_TokenInstruction_Tag { */ Token_TokenInstruction_ThawAccount, /** - * Transfers tokens from one account to another either directly or via a delegate. If this - * account is associated with the native mint then equal amounts of SOL and Tokens will be - * transferred to the destination account. + * Transfers tokens from one account to another either directly or via a + * delegate. If this account is associated with the native mint then equal + * amounts of SOL and Tokens will be transferred to the destination + * account. * - * This instruction differs from Transfer in that the token mint and decimals value is - * asserted by the caller. This may be useful when creating transactions offline or within a - * hardware wallet. + * This instruction differs from Transfer in that the token mint and + * decimals value is asserted by the caller. This may be useful when + * creating transactions offline or within a hardware wallet. * * Accounts expected by this instruction: * @@ -334,12 +348,12 @@ typedef enum Token_TokenInstruction_Tag { */ Token_TokenInstruction_Transfer2, /** - * Approves a delegate. A delegate is given the authority over - * tokens on behalf of the source account's owner. + * Approves a delegate. A delegate is given the authority over tokens on + * behalf of the source account's owner. * - * This instruction differs from Approve in that the token mint and decimals value is asserted - * by the caller. This may be useful when creating transactions offline or within a hardware - * wallet. + * This instruction differs from Approve in that the token mint and + * decimals value is asserted by the caller. This may be useful when + * creating transactions offline or within a hardware wallet. * * Accounts expected by this instruction: * @@ -358,10 +372,12 @@ typedef enum Token_TokenInstruction_Tag { */ Token_TokenInstruction_Approve2, /** - * Mints new tokens to an account. The native mint does not support minting. + * Mints new tokens to an account. The native mint does not support + * minting. * - * This instruction differs from MintTo in that the decimals value is asserted by the - * caller. This may be useful when creating transactions offline or within a hardware wallet. + * This instruction differs from MintTo in that the decimals value is + * asserted by the caller. This may be useful when creating transactions + * offline or within a hardware wallet. * * Accounts expected by this instruction: * @@ -378,11 +394,13 @@ typedef enum Token_TokenInstruction_Tag { */ Token_TokenInstruction_MintTo2, /** - * Burns tokens by removing them from an account. `Burn2` does not support accounts - * associated with the native mint, use `CloseAccount` instead. + * Burns tokens by removing them from an account. `Burn2` does not + * support accounts associated with the native mint, use `CloseAccount` + * instead. * - * This instruction differs from Burn in that the decimals value is asserted by the caller. - * This may be useful when creating transactions offline or within a hardware wallet. + * This instruction differs from Burn in that the decimals value is asserted + * by the caller. This may be useful when creating transactions offline or + * within a hardware wallet. * * Accounts expected by this instruction: * @@ -417,7 +435,8 @@ typedef struct Token_TokenInstruction_Token_InitializeMint_Body { typedef struct Token_TokenInstruction_Token_InitializeMultisig_Body { /** - * The number of signers (M) required to validate this multisignature account. + * The number of signers (M) required to validate this multisignature + * account. */ uint8_t m; } Token_TokenInstruction_Token_InitializeMultisig_Body; From 98f0738ec9a63020a9cf92eccb9124ec78f37d13 Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Fri, 5 Feb 2021 18:54:18 -0700 Subject: [PATCH 103/248] token: cbindgen doing its thing... --- program/inc/token.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/program/inc/token.h b/program/inc/token.h index e027ac5..7bedb8b 100644 --- a/program/inc/token.h +++ b/program/inc/token.h @@ -8,14 +8,14 @@ #include /** - * Maximum number of multisignature signers (max N) + * Minimum number of multisignature signers (min N) */ -#define Token_MAX_SIGNERS 11 +#define Token_MIN_SIGNERS 1 /** - * Minimum number of multisignature signers (min N) + * Maximum number of multisignature signers (max N) */ -#define Token_MIN_SIGNERS 1 +#define Token_MAX_SIGNERS 11 /** * Account state. @@ -263,9 +263,9 @@ typedef enum Token_TokenInstruction_Tag { * * * Multisignature owner/delegate * 0. `[writable]` The account to burn from. - * 1. '[writable]' The token mint. + * 1. `[writable]` The token mint. * 2. `[]` The account's multisignature owner/delegate. - * 3. ..3+M '[signer]' M signer accounts. + * 3. ..3+M `[signer]` M signer accounts. */ Token_TokenInstruction_Burn, /** From ed0cb2b7e9ad30082ea6d7385828ff59bd39ab47 Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Fri, 5 Feb 2021 18:55:44 -0700 Subject: [PATCH 104/248] token: Get `COption` and `Pubkey` C bindings from solana-program crate --- program/inc/token.h | 30 +++++++++++++----------------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/program/inc/token.h b/program/inc/token.h index 7bedb8b..7cde65d 100644 --- a/program/inc/token.h +++ b/program/inc/token.h @@ -89,14 +89,12 @@ typedef enum Token_COption_Pubkey_Tag { Token_COption_Pubkey_Some_Pubkey, } Token_COption_Pubkey_Tag; -typedef struct Token_COption_Pubkey_Token_Some_Body_Pubkey { - Token_Pubkey _0; -} Token_COption_Pubkey_Token_Some_Body_Pubkey; - typedef struct Token_COption_Pubkey { Token_COption_Pubkey_Tag tag; union { - Token_COption_Pubkey_Token_Some_Body_Pubkey some; + struct { + Token_Pubkey some; + }; }; } Token_COption_Pubkey; @@ -430,7 +428,7 @@ typedef struct Token_TokenInstruction_Token_InitializeMint_Body { /** * The freeze authority/multisignature of the mint. */ - Token_COption_Pubkey freeze_authority; + struct Token_COption_Pubkey freeze_authority; } Token_TokenInstruction_Token_InitializeMint_Body; typedef struct Token_TokenInstruction_Token_InitializeMultisig_Body { @@ -463,7 +461,7 @@ typedef struct Token_TokenInstruction_Token_SetAuthority_Body { /** * The new authority */ - Token_COption_Pubkey new_authority; + struct Token_COption_Pubkey new_authority; } Token_TokenInstruction_Token_SetAuthority_Body; typedef struct Token_TokenInstruction_Token_MintTo_Body { @@ -550,7 +548,7 @@ typedef struct Token_Mint { * mint creation. If no mint authority is present then the mint has a fixed supply and no * further tokens may be minted. */ - Token_COption_Pubkey mint_authority; + struct Token_COption_Pubkey mint_authority; /** * Total supply of tokens. */ @@ -566,7 +564,7 @@ typedef struct Token_Mint { /** * Optional authority to freeze token accounts. */ - Token_COption_Pubkey freeze_authority; + struct Token_COption_Pubkey freeze_authority; } Token_Mint; /** @@ -583,14 +581,12 @@ typedef enum Token_COption_u64_Tag { Token_COption_u64_Some_u64, } Token_COption_u64_Tag; -typedef struct Token_COption_u64_Token_Some_Body_u64 { - uint64_t _0; -} Token_COption_u64_Token_Some_Body_u64; - typedef struct Token_COption_u64 { Token_COption_u64_Tag tag; union { - Token_COption_u64_Token_Some_Body_u64 some; + struct { + uint64_t some; + }; }; } Token_COption_u64; @@ -614,7 +610,7 @@ typedef struct Token_Account { * If `delegate` is `Some` then `delegated_amount` represents * the amount authorized by the delegate */ - Token_COption_Pubkey delegate; + struct Token_COption_Pubkey delegate; /** * The account's state */ @@ -624,7 +620,7 @@ typedef struct Token_Account { * is required to be rent-exempt, so the value is used by the Processor to ensure that wrapped * SOL accounts do not drop below this threshold. */ - Token_COption_u64 is_native; + struct Token_COption_u64 is_native; /** * The amount delegated */ @@ -632,7 +628,7 @@ typedef struct Token_Account { /** * Optional authority to close the account. */ - Token_COption_Pubkey close_authority; + struct Token_COption_Pubkey close_authority; } Token_Account; /** From 8670f154139005022b9448a942f6b1d416761dd5 Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Fri, 5 Feb 2021 18:56:43 -0700 Subject: [PATCH 105/248] token: C binding comment typos --- program/inc/token.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/program/inc/token.h b/program/inc/token.h index 7cde65d..c68a2eb 100644 --- a/program/inc/token.h +++ b/program/inc/token.h @@ -108,7 +108,7 @@ typedef enum Token_TokenInstruction_Tag { * * The `InitializeMint` instruction requires no signers and MUST be * included within the same Transaction as the system program's - * `CreateInstruction` that creates the account being initialized. + * `CreateAccount` instruction that creates the account being initialized. * Otherwise another party can acquire ownership of the uninitialized * account. * @@ -128,7 +128,7 @@ typedef enum Token_TokenInstruction_Tag { * * The `InitializeAccount` instruction requires no signers and MUST be * included within the same Transaction as the system program's - * `CreateInstruction` that creates the account being initialized. + * `CreateAccount` instruction that creates the account being initialized. * Otherwise another party can acquire ownership of the uninitialized * account. * @@ -150,7 +150,7 @@ typedef enum Token_TokenInstruction_Tag { * * The `InitializeMultisig` instruction requires no signers and MUST be * included within the same Transaction as the system program's - * `CreateInstruction` that creates the account being initialized. + * `CreateAccount` instruction that creates the account being initialized. * Otherwise another party can acquire ownership of the uninitialized * account. * @@ -226,7 +226,7 @@ typedef enum Token_TokenInstruction_Tag { * * * Multisignature authority * 0. `[writable]` The mint or account to change the authority of. - * 1. `[]` The mint's or account's multisignature authority. + * 1. `[]` The mint's or account's current multisignature authority. * 2. ..2+M `[signer]` M signer accounts */ Token_TokenInstruction_SetAuthority, From ffe2f7365839ed66ab28534d5a1f92df11082ed6 Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Fri, 5 Feb 2021 18:58:05 -0700 Subject: [PATCH 106/248] token: Rename v2 instructions in C bindings --- program/inc/token.h | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/program/inc/token.h b/program/inc/token.h index c68a2eb..726032a 100644 --- a/program/inc/token.h +++ b/program/inc/token.h @@ -326,7 +326,7 @@ typedef enum Token_TokenInstruction_Tag { * account. * * This instruction differs from Transfer in that the token mint and - * decimals value is asserted by the caller. This may be useful when + * decimals value is checked by the caller. This may be useful when * creating transactions offline or within a hardware wallet. * * Accounts expected by this instruction: @@ -344,13 +344,13 @@ typedef enum Token_TokenInstruction_Tag { * 3. `[]` The source account's multisignature owner/delegate. * 4. ..4+M `[signer]` M signer accounts. */ - Token_TokenInstruction_Transfer2, + Token_TokenInstruction_TransferChecked, /** * Approves a delegate. A delegate is given the authority over tokens on * behalf of the source account's owner. * * This instruction differs from Approve in that the token mint and - * decimals value is asserted by the caller. This may be useful when + * decimals value is checked by the caller. This may be useful when * creating transactions offline or within a hardware wallet. * * Accounts expected by this instruction: @@ -368,13 +368,13 @@ typedef enum Token_TokenInstruction_Tag { * 3. `[]` The source account's multisignature owner. * 4. ..4+M `[signer]` M signer accounts */ - Token_TokenInstruction_Approve2, + Token_TokenInstruction_ApproveChecked, /** * Mints new tokens to an account. The native mint does not support * minting. * * This instruction differs from MintTo in that the decimals value is - * asserted by the caller. This may be useful when creating transactions + * checked by the caller. This may be useful when creating transactions * offline or within a hardware wallet. * * Accounts expected by this instruction: @@ -390,13 +390,13 @@ typedef enum Token_TokenInstruction_Tag { * 2. `[]` The mint's multisignature mint-tokens authority. * 3. ..3+M `[signer]` M signer accounts. */ - Token_TokenInstruction_MintTo2, + Token_TokenInstruction_MintToChecked, /** - * Burns tokens by removing them from an account. `Burn2` does not + * Burns tokens by removing them from an account. `BurnChecked` does not * support accounts associated with the native mint, use `CloseAccount` * instead. * - * This instruction differs from Burn in that the decimals value is asserted + * This instruction differs from Burn in that the decimals value is checked * by the caller. This may be useful when creating transactions offline or * within a hardware wallet. * @@ -413,7 +413,7 @@ typedef enum Token_TokenInstruction_Tag { * 2. `[]` The account's multisignature owner/delegate. * 3. ..3+M `[signer]` M signer accounts. */ - Token_TokenInstruction_Burn2, + Token_TokenInstruction_BurnChecked, } Token_TokenInstruction_Tag; typedef struct Token_TokenInstruction_Token_InitializeMint_Body { @@ -478,7 +478,7 @@ typedef struct Token_TokenInstruction_Token_Burn_Body { uint64_t amount; } Token_TokenInstruction_Token_Burn_Body; -typedef struct Token_TokenInstruction_Token_Transfer2_Body { +typedef struct Token_TokenInstruction_Token_TransferChecked_Body { /** * The amount of tokens to transfer. */ @@ -487,9 +487,9 @@ typedef struct Token_TokenInstruction_Token_Transfer2_Body { * Expected number of base 10 digits to the right of the decimal place. */ uint8_t decimals; -} Token_TokenInstruction_Token_Transfer2_Body; +} Token_TokenInstruction_Token_TransferChecked_Body; -typedef struct Token_TokenInstruction_Token_Approve2_Body { +typedef struct Token_TokenInstruction_Token_ApproveChecked_Body { /** * The amount of tokens the delegate is approved for. */ @@ -498,9 +498,9 @@ typedef struct Token_TokenInstruction_Token_Approve2_Body { * Expected number of base 10 digits to the right of the decimal place. */ uint8_t decimals; -} Token_TokenInstruction_Token_Approve2_Body; +} Token_TokenInstruction_Token_ApproveChecked_Body; -typedef struct Token_TokenInstruction_Token_MintTo2_Body { +typedef struct Token_TokenInstruction_Token_MintToChecked_Body { /** * The amount of new tokens to mint. */ @@ -509,9 +509,9 @@ typedef struct Token_TokenInstruction_Token_MintTo2_Body { * Expected number of base 10 digits to the right of the decimal place. */ uint8_t decimals; -} Token_TokenInstruction_Token_MintTo2_Body; +} Token_TokenInstruction_Token_MintToChecked_Body; -typedef struct Token_TokenInstruction_Token_Burn2_Body { +typedef struct Token_TokenInstruction_Token_BurnChecked_Body { /** * The amount of tokens to burn. */ @@ -520,7 +520,7 @@ typedef struct Token_TokenInstruction_Token_Burn2_Body { * Expected number of base 10 digits to the right of the decimal place. */ uint8_t decimals; -} Token_TokenInstruction_Token_Burn2_Body; +} Token_TokenInstruction_Token_BurnChecked_Body; typedef struct Token_TokenInstruction { Token_TokenInstruction_Tag tag; @@ -532,10 +532,10 @@ typedef struct Token_TokenInstruction { Token_TokenInstruction_Token_SetAuthority_Body set_authority; Token_TokenInstruction_Token_MintTo_Body mint_to; Token_TokenInstruction_Token_Burn_Body burn; - Token_TokenInstruction_Token_Transfer2_Body transfer2; - Token_TokenInstruction_Token_Approve2_Body approve2; - Token_TokenInstruction_Token_MintTo2_Body mint_to2; - Token_TokenInstruction_Token_Burn2_Body burn2; + Token_TokenInstruction_Token_TransferChecked_Body transfer_checked; + Token_TokenInstruction_Token_ApproveChecked_Body approve_checked; + Token_TokenInstruction_Token_MintToChecked_Body mint_to_checked; + Token_TokenInstruction_Token_BurnChecked_Body burn_checked; }; } Token_TokenInstruction; From 022df4f9acc60c1b9f81c5728d74efa9f7e8a8e6 Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Fri, 5 Feb 2021 18:58:44 -0700 Subject: [PATCH 107/248] token: Add `InitializeAccount2` C binding --- program/inc/token.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/program/inc/token.h b/program/inc/token.h index 726032a..f68bba7 100644 --- a/program/inc/token.h +++ b/program/inc/token.h @@ -414,6 +414,19 @@ typedef enum Token_TokenInstruction_Tag { * 3. ..3+M `[signer]` M signer accounts. */ Token_TokenInstruction_BurnChecked, + /** + * Like InitializeAccount, but the owner pubkey is passed via instruction data + * rather than the accounts list. This variant may be preferable when using + * Cross Program Invocation from an instruction that does not need the owner's + * `AccountInfo` otherwise. + * + * Accounts expected by this instruction: + * + * 0. `[writable]` The account to initialize. + * 1. `[]` The mint this account will be associated with. + * 3. `[]` Rent sysvar + */ + Token_TokenInstruction_InitializeAccount2, } Token_TokenInstruction_Tag; typedef struct Token_TokenInstruction_Token_InitializeMint_Body { @@ -522,6 +535,13 @@ typedef struct Token_TokenInstruction_Token_BurnChecked_Body { uint8_t decimals; } Token_TokenInstruction_Token_BurnChecked_Body; +typedef struct Token_TokenInstruction_Token_InitializeAccount2_Body { + /** + * The new account's owner/multisignature. + */ + Token_Pubkey owner; +} Token_TokenInstruction_Token_InitializeAccount2_Body; + typedef struct Token_TokenInstruction { Token_TokenInstruction_Tag tag; union { @@ -536,6 +556,7 @@ typedef struct Token_TokenInstruction { Token_TokenInstruction_Token_ApproveChecked_Body approve_checked; Token_TokenInstruction_Token_MintToChecked_Body mint_to_checked; Token_TokenInstruction_Token_BurnChecked_Body burn_checked; + Token_TokenInstruction_Token_InitializeAccount2_Body initialize_account2; }; } Token_TokenInstruction; From b2c584dbf0d5bf348a48ebaa2d27dcc283e90ebf Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Thu, 11 Feb 2021 14:18:38 -0700 Subject: [PATCH 108/248] chore: bump solana crates to 1.5.6 --- program/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 8c66334..2472390 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,11 +16,11 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.1" -solana-program = "1.5.1" +solana-program = "1.5.6" thiserror = "1.0" [dev-dependencies] -solana-sdk = "1.5.0" +solana-sdk = "1.5.6" [lib] crate-type = ["cdylib", "lib"] From 853964ea6316689e708e95489c30b23cd2a12b38 Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Thu, 11 Feb 2021 14:28:05 -0700 Subject: [PATCH 109/248] chore: token - bump version to 3.1.0 --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 2472390..fa7162b 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-token" -version = "3.0.1" +version = "3.1.0" description = "Solana Program Library Token" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" From be2af7c7755579b9304da689381e9d7ec675b82f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 17 Feb 2021 23:56:18 +0000 Subject: [PATCH 110/248] build(deps): bump solana-program from 1.5.6 to 1.5.8 (#1261) Bumps [solana-program](https://github.com/solana-labs/solana) from 1.5.6 to 1.5.8. - [Release notes](https://github.com/solana-labs/solana/releases) - [Changelog](https://github.com/solana-labs/solana/blob/master/RELEASE.md) - [Commits](https://github.com/solana-labs/solana/compare/v1.5.6...v1.5.8) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index fa7162b..da8e8c3 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,7 +16,7 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.1" -solana-program = "1.5.6" +solana-program = "1.5.8" thiserror = "1.0" [dev-dependencies] From 1a357a43f0093e02bf7768c7f27527f7c078048b Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Tue, 2 Mar 2021 19:19:50 +0100 Subject: [PATCH 111/248] Update solana-program to 1.5.11 (#1362) * Update solana-program to 1.5.11 * Update all programs --- program/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index da8e8c3..480394d 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,11 +16,11 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.1" -solana-program = "1.5.8" +solana-program = "1.5.11" thiserror = "1.0" [dev-dependencies] -solana-sdk = "1.5.6" +solana-sdk = "1.5.11" [lib] crate-type = ["cdylib", "lib"] From 71e6aa09eb350ea132d706bc544bdcf407c6b60b Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Tue, 16 Mar 2021 13:47:59 -0700 Subject: [PATCH 112/248] Bump Solana version to 1.5.15 --- program/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 480394d..af666c6 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,11 +16,11 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.1" -solana-program = "1.5.11" +solana-program = "1.5.15" thiserror = "1.0" [dev-dependencies] -solana-sdk = "1.5.11" +solana-sdk = "1.5.15" [lib] crate-type = ["cdylib", "lib"] From 855139ff79a1aec1e4e3722546819e3654cd2426 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Mon, 15 Mar 2021 22:35:48 -0700 Subject: [PATCH 113/248] Bump Rust version to 1.50.0 --- program/src/processor.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/program/src/processor.rs b/program/src/processor.rs index 5d22ca5..ea9fb7f 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -159,6 +159,7 @@ impl Processor { let source_account_info = next_account_info(account_info_iter)?; + #[allow(clippy::manual_map)] let expected_mint_info = if let Some(expected_decimals) = expected_decimals { Some((next_account_info(account_info_iter)?, expected_decimals)) } else { @@ -266,6 +267,8 @@ impl Processor { let account_info_iter = &mut accounts.iter(); let source_account_info = next_account_info(account_info_iter)?; + + #[allow(clippy::manual_map)] let expected_mint_info = if let Some(expected_decimals) = expected_decimals { Some((next_account_info(account_info_iter)?, expected_decimals)) } else { From 2ae6ef6bcb0d3a57a746b13d1ee5349f78d0cef6 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Tue, 16 Mar 2021 20:31:22 -0700 Subject: [PATCH 114/248] Back out clippy::manual_map for now --- program/src/processor.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index ea9fb7f..7171741 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -159,7 +159,6 @@ impl Processor { let source_account_info = next_account_info(account_info_iter)?; - #[allow(clippy::manual_map)] let expected_mint_info = if let Some(expected_decimals) = expected_decimals { Some((next_account_info(account_info_iter)?, expected_decimals)) } else { @@ -268,7 +267,6 @@ impl Processor { let source_account_info = next_account_info(account_info_iter)?; - #[allow(clippy::manual_map)] let expected_mint_info = if let Some(expected_decimals) = expected_decimals { Some((next_account_info(account_info_iter)?, expected_decimals)) } else { From b582ba21711a9ecd7d606bd9eaaf6cb4c37efb09 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Wed, 17 Mar 2021 20:21:21 -0700 Subject: [PATCH 115/248] Bump Solana version to 1.6.1 --- program/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index af666c6..7da140b 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,11 +16,11 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.1" -solana-program = "1.5.15" +solana-program = "1.6.1" thiserror = "1.0" [dev-dependencies] -solana-sdk = "1.5.15" +solana-sdk = "1.6.1" [lib] crate-type = ["cdylib", "lib"] From 5741e9cbe3a06a18ae5feb0250ebfcd24d6bc960 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Tue, 30 Mar 2021 08:41:53 -0700 Subject: [PATCH 116/248] Update to Solana 1.6.2 --- program/Cargo.toml | 4 ++-- program/src/processor.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 7da140b..7c6480b 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,11 +16,11 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.1" -solana-program = "1.6.1" +solana-program = "1.6.2" thiserror = "1.0" [dev-dependencies] -solana-sdk = "1.6.1" +solana-sdk = "1.6.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/program/src/processor.rs b/program/src/processor.rs index 7171741..b4080be 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -811,7 +811,7 @@ mod tests { account_info::IntoAccountInfo, clock::Epoch, instruction::Instruction, sysvar::rent, }; use solana_sdk::account::{ - create_account, create_is_signer_account_infos, Account as SolanaAccount, + create_account_for_test, create_is_signer_account_infos, Account as SolanaAccount, }; fn do_process_instruction( @@ -841,7 +841,7 @@ mod tests { } fn rent_sysvar() -> SolanaAccount { - create_account(&Rent::default(), 42) + create_account_for_test(&Rent::default()) } fn mint_minimum_balance() -> u64 { From a5852dfb8e2807c2836e37f63d51125ddd2a7c1f Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Fri, 23 Apr 2021 12:31:36 -0700 Subject: [PATCH 117/248] Update SPL to Solana v1.6.6 --- program/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 7c6480b..14c8749 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,11 +16,11 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.1" -solana-program = "1.6.2" +solana-program = "1.6.6" thiserror = "1.0" [dev-dependencies] -solana-sdk = "1.6.2" +solana-sdk = "1.6.6" [lib] crate-type = ["cdylib", "lib"] From c6465b32cfee2898b7179c6497582f8ecfc5ead0 Mon Sep 17 00:00:00 2001 From: Trent Nelson Date: Tue, 4 May 2021 22:03:15 -0600 Subject: [PATCH 118/248] Bump solana crates to v1.6.7 --- program/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 14c8749..3809761 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,11 +16,11 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.1" -solana-program = "1.6.6" +solana-program = "1.6.7" thiserror = "1.0" [dev-dependencies] -solana-sdk = "1.6.6" +solana-sdk = "1.6.7" [lib] crate-type = ["cdylib", "lib"] From c520c5e015192cc4219cd671e900e874346138e8 Mon Sep 17 00:00:00 2001 From: Elliott Benisty Date: Wed, 12 May 2021 23:36:41 +0100 Subject: [PATCH 119/248] Fix potential vulnerabilities in programs using spl-token CPIs by adding program id checks (#1714) * Add spl-token program id check helper function. Add program id to instruction bindings. * Run cargo fmt * Fixup tests * Skip ATA tests when custom token program-id Co-authored-by: Tyera Eulberg --- program/src/instruction.rs | 19 ++++++++++++++- program/src/lib.rs | 9 +++++++ program/src/processor.rs | 48 +++++++++++++++++++------------------- 3 files changed, 51 insertions(+), 25 deletions(-) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 063eb7c..da28205 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -1,6 +1,6 @@ //! Instruction types -use crate::error::TokenError; +use crate::{check_program_account, error::TokenError}; use solana_program::{ instruction::{AccountMeta, Instruction}, program_error::ProgramError, @@ -620,6 +620,7 @@ pub fn initialize_mint( freeze_authority_pubkey: Option<&Pubkey>, decimals: u8, ) -> Result { + check_program_account(token_program_id)?; let freeze_authority = freeze_authority_pubkey.cloned().into(); let data = TokenInstruction::InitializeMint { mint_authority: *mint_authority_pubkey, @@ -647,6 +648,7 @@ pub fn initialize_account( mint_pubkey: &Pubkey, owner_pubkey: &Pubkey, ) -> Result { + check_program_account(token_program_id)?; let data = TokenInstruction::InitializeAccount.pack(); let accounts = vec![ @@ -670,6 +672,7 @@ pub fn initialize_account2( mint_pubkey: &Pubkey, owner_pubkey: &Pubkey, ) -> Result { + check_program_account(token_program_id)?; let data = TokenInstruction::InitializeAccount2 { owner: *owner_pubkey, } @@ -695,6 +698,7 @@ pub fn initialize_multisig( signer_pubkeys: &[&Pubkey], m: u8, ) -> Result { + check_program_account(token_program_id)?; if !is_valid_signer_index(m as usize) || !is_valid_signer_index(signer_pubkeys.len()) || m as usize > signer_pubkeys.len() @@ -726,6 +730,7 @@ pub fn transfer( signer_pubkeys: &[&Pubkey], amount: u64, ) -> Result { + check_program_account(token_program_id)?; let data = TokenInstruction::Transfer { amount }.pack(); let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); @@ -755,6 +760,7 @@ pub fn approve( signer_pubkeys: &[&Pubkey], amount: u64, ) -> Result { + check_program_account(token_program_id)?; let data = TokenInstruction::Approve { amount }.pack(); let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); @@ -782,6 +788,7 @@ pub fn revoke( owner_pubkey: &Pubkey, signer_pubkeys: &[&Pubkey], ) -> Result { + check_program_account(token_program_id)?; let data = TokenInstruction::Revoke.pack(); let mut accounts = Vec::with_capacity(2 + signer_pubkeys.len()); @@ -810,6 +817,7 @@ pub fn set_authority( owner_pubkey: &Pubkey, signer_pubkeys: &[&Pubkey], ) -> Result { + check_program_account(token_program_id)?; let new_authority = new_authority_pubkey.cloned().into(); let data = TokenInstruction::SetAuthority { authority_type, @@ -843,6 +851,7 @@ pub fn mint_to( signer_pubkeys: &[&Pubkey], amount: u64, ) -> Result { + check_program_account(token_program_id)?; let data = TokenInstruction::MintTo { amount }.pack(); let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); @@ -872,6 +881,7 @@ pub fn burn( signer_pubkeys: &[&Pubkey], amount: u64, ) -> Result { + check_program_account(token_program_id)?; let data = TokenInstruction::Burn { amount }.pack(); let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); @@ -900,6 +910,7 @@ pub fn close_account( owner_pubkey: &Pubkey, signer_pubkeys: &[&Pubkey], ) -> Result { + check_program_account(token_program_id)?; let data = TokenInstruction::CloseAccount.pack(); let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); @@ -928,6 +939,7 @@ pub fn freeze_account( owner_pubkey: &Pubkey, signer_pubkeys: &[&Pubkey], ) -> Result { + check_program_account(token_program_id)?; let data = TokenInstruction::FreezeAccount.pack(); let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); @@ -956,6 +968,7 @@ pub fn thaw_account( owner_pubkey: &Pubkey, signer_pubkeys: &[&Pubkey], ) -> Result { + check_program_account(token_program_id)?; let data = TokenInstruction::ThawAccount.pack(); let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); @@ -988,6 +1001,7 @@ pub fn transfer_checked( amount: u64, decimals: u8, ) -> Result { + check_program_account(token_program_id)?; let data = TokenInstruction::TransferChecked { amount, decimals }.pack(); let mut accounts = Vec::with_capacity(4 + signer_pubkeys.len()); @@ -1021,6 +1035,7 @@ pub fn approve_checked( amount: u64, decimals: u8, ) -> Result { + check_program_account(token_program_id)?; let data = TokenInstruction::ApproveChecked { amount, decimals }.pack(); let mut accounts = Vec::with_capacity(4 + signer_pubkeys.len()); @@ -1052,6 +1067,7 @@ pub fn mint_to_checked( amount: u64, decimals: u8, ) -> Result { + check_program_account(token_program_id)?; let data = TokenInstruction::MintToChecked { amount, decimals }.pack(); let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); @@ -1082,6 +1098,7 @@ pub fn burn_checked( amount: u64, decimals: u8, ) -> Result { + check_program_account(token_program_id)?; let data = TokenInstruction::BurnChecked { amount, decimals }.pack(); let mut accounts = Vec::with_capacity(3 + signer_pubkeys.len()); diff --git a/program/src/lib.rs b/program/src/lib.rs index a561e30..529aaa1 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -14,6 +14,7 @@ mod entrypoint; // Export current sdk types for downstream users building with a different sdk version pub use solana_program; +use solana_program::{entrypoint::ProgramResult, program_error::ProgramError, pubkey::Pubkey}; /// Convert the UI representation of a token amount (using the decimals field defined in its mint) /// to the raw amount @@ -27,3 +28,11 @@ pub fn amount_to_ui_amount(amount: u64, decimals: u8) -> f64 { } solana_program::declare_id!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); + +/// Checks that the supplied program ID is the correct one for SPL-token +pub fn check_program_account(spl_token_program_id: &Pubkey) -> ProgramResult { + if spl_token_program_id != &id() { + return Err(ProgramError::IncorrectProgramId); + } + Ok(()) +} diff --git a/program/src/processor.rs b/program/src/processor.rs index b4080be..4ba5bcc 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -985,7 +985,7 @@ mod tests { #[test] fn test_initialize_mint() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let owner_key = Pubkey::new_unique(); let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(42, Mint::get_packed_len(), &program_id); @@ -1033,7 +1033,7 @@ mod tests { #[test] fn test_initialize_mint_account() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new(42, Account::get_packed_len(), &program_id); let owner_key = Pubkey::new_unique(); @@ -1109,7 +1109,7 @@ mod tests { #[test] fn test_transfer_dups() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let account1_key = Pubkey::new_unique(); let mut account1_account = SolanaAccount::new( account_minimum_balance(), @@ -1416,7 +1416,7 @@ mod tests { #[test] fn test_transfer() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance(), @@ -1837,7 +1837,7 @@ mod tests { #[test] fn test_self_transfer() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance(), @@ -2367,7 +2367,7 @@ mod tests { #[test] fn test_mintable_token_with_zero_supply() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance(), @@ -2466,7 +2466,7 @@ mod tests { #[test] fn test_approve_dups() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let account1_key = Pubkey::new_unique(); let mut account1_account = SolanaAccount::new( account_minimum_balance(), @@ -2677,7 +2677,7 @@ mod tests { #[test] fn test_approve() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance(), @@ -2882,7 +2882,7 @@ mod tests { #[test] fn test_set_authority_dups() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let account1_key = Pubkey::new_unique(); let mut account1_account = SolanaAccount::new( account_minimum_balance(), @@ -2985,7 +2985,7 @@ mod tests { #[test] fn test_set_authority() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance(), @@ -3324,7 +3324,7 @@ mod tests { #[test] fn test_mint_to_dups() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let account1_key = Pubkey::new_unique(); let mut account1_account = SolanaAccount::new( account_minimum_balance(), @@ -3420,7 +3420,7 @@ mod tests { #[test] fn test_mint_to() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance(), @@ -3623,7 +3623,7 @@ mod tests { #[test] fn test_burn_dups() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let account1_key = Pubkey::new_unique(); let mut account1_account = SolanaAccount::new( account_minimum_balance(), @@ -3823,7 +3823,7 @@ mod tests { #[test] fn test_burn() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance(), @@ -4088,7 +4088,7 @@ mod tests { #[test] fn test_multisig() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); @@ -4492,7 +4492,7 @@ mod tests { #[test] fn test_validate_owner() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let owner_key = Pubkey::new_unique(); let mut signer_keys = [Pubkey::default(); MAX_SIGNERS]; for signer_key in signer_keys.iter_mut().take(MAX_SIGNERS) { @@ -4652,7 +4652,7 @@ mod tests { #[test] fn test_close_account_dups() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let account1_key = Pubkey::new_unique(); let mut account1_account = SolanaAccount::new( account_minimum_balance(), @@ -4738,7 +4738,7 @@ mod tests { #[test] fn test_close_account() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); @@ -4967,7 +4967,7 @@ mod tests { #[test] fn test_native_token() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); let account_key = Pubkey::new_unique(); @@ -5144,7 +5144,7 @@ mod tests { #[test] fn test_overflow() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance(), @@ -5321,7 +5321,7 @@ mod tests { #[test] fn test_frozen() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance(), @@ -5507,7 +5507,7 @@ mod tests { #[test] fn test_freeze_thaw_dups() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let account1_key = Pubkey::new_unique(); let mut account1_account = SolanaAccount::new( account_minimum_balance(), @@ -5571,7 +5571,7 @@ mod tests { #[test] fn test_freeze_account() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance(), @@ -5684,7 +5684,7 @@ mod tests { #[test] fn test_initialize_account2() { - let program_id = Pubkey::new_unique(); + let program_id = crate::id(); let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( account_minimum_balance(), From c2efab4b91239796d36410cd0cc6fb0880d1b9e8 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Mon, 17 May 2021 20:26:25 -0700 Subject: [PATCH 120/248] Bump spl-token patch version --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 3809761..b963355 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-token" -version = "3.1.0" +version = "3.1.1" description = "Solana Program Library Token" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" From f17592173f72080a77b1fbc69a251d5d4efdb28a Mon Sep 17 00:00:00 2001 From: Jonathan LEI Date: Wed, 19 May 2021 00:32:12 +0800 Subject: [PATCH 121/248] Fix #1739: ThawAccount incorrectly logged as FreezeAccount in token program (#1740) --- program/src/processor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index 4ba5bcc..48d09a6 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -702,7 +702,7 @@ impl Processor { Self::process_toggle_freeze_account(program_id, accounts, true) } TokenInstruction::ThawAccount => { - msg!("Instruction: FreezeAccount"); + msg!("Instruction: ThawAccount"); Self::process_toggle_freeze_account(program_id, accounts, false) } TokenInstruction::TransferChecked { amount, decimals } => { From fb645066cb014628bb725145ac261b21be9bbd3c Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Mon, 14 Jun 2021 14:44:58 -0600 Subject: [PATCH 122/248] Bump solana version --- program/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index b963355..dd657a5 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,11 +16,11 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.1" -solana-program = "1.6.7" +solana-program = "1.6.11" thiserror = "1.0" [dev-dependencies] -solana-sdk = "1.6.7" +solana-sdk = "1.6.11" [lib] crate-type = ["cdylib", "lib"] From 2b8aa1a77c3c9000043616000a863dbf3f43d115 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Fri, 25 Jun 2021 00:39:08 +0200 Subject: [PATCH 123/248] Update all solana dependencies to 1.7.3, fix issues (#1958) * Update all dependencies to 1.7.3, fix issues * Remove esm from mocha * Fix missed token test * Also update rust version * token-swap: update tolerance on sim test * Run `cargo clippy --fix` for needless_borrow errors * Rerun cargo fmt --- program/Cargo.toml | 4 +- program/src/processor.rs | 120 +++++++++++++++++++-------------------- 2 files changed, 62 insertions(+), 62 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index dd657a5..8dea41a 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,11 +16,11 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.1" -solana-program = "1.6.11" +solana-program = "1.7.3" thiserror = "1.0" [dev-dependencies] -solana-sdk = "1.6.11" +solana-sdk = "1.7.3" [lib] crate-type = ["cdylib", "lib"] diff --git a/program/src/processor.rs b/program/src/processor.rs index 48d09a6..d91f618 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -1927,9 +1927,9 @@ mod tests { // transfer let instruction = transfer( &program_id, - &account_info.key, - &account_info.key, - &owner_info.key, + account_info.key, + account_info.key, + owner_info.key, &[], 1000, ) @@ -1953,10 +1953,10 @@ mod tests { // transfer checked let instruction = transfer_checked( &program_id, - &account_info.key, - &mint_info.key, - &account_info.key, - &owner_info.key, + account_info.key, + mint_info.key, + account_info.key, + owner_info.key, &[], 1000, 2, @@ -1983,9 +1983,9 @@ mod tests { let mut owner_no_sign_info = owner_info.clone(); let mut instruction = transfer( &program_id, - &account_info.key, - &account_info.key, - &owner_no_sign_info.key, + account_info.key, + account_info.key, + owner_no_sign_info.key, &[], 1000, ) @@ -2008,10 +2008,10 @@ mod tests { // missing signer checked let mut instruction = transfer_checked( &program_id, - &account_info.key, - &mint_info.key, - &account_info.key, - &owner_no_sign_info.key, + account_info.key, + mint_info.key, + account_info.key, + owner_no_sign_info.key, &[], 1000, 2, @@ -2035,9 +2035,9 @@ mod tests { // missing owner let instruction = transfer( &program_id, - &account_info.key, - &account_info.key, - &owner2_info.key, + account_info.key, + account_info.key, + owner2_info.key, &[], 1000, ) @@ -2058,10 +2058,10 @@ mod tests { // missing owner checked let instruction = transfer_checked( &program_id, - &account_info.key, - &mint_info.key, - &account_info.key, - &owner2_info.key, + account_info.key, + mint_info.key, + account_info.key, + owner2_info.key, &[], 1000, 2, @@ -2084,9 +2084,9 @@ mod tests { // insufficient funds let instruction = transfer( &program_id, - &account_info.key, - &account_info.key, - &owner_info.key, + account_info.key, + account_info.key, + owner_info.key, &[], 1001, ) @@ -2107,10 +2107,10 @@ mod tests { // insufficient funds checked let instruction = transfer_checked( &program_id, - &account_info.key, - &mint_info.key, - &account_info.key, - &owner_info.key, + account_info.key, + mint_info.key, + account_info.key, + owner_info.key, &[], 1001, 2, @@ -2133,10 +2133,10 @@ mod tests { // incorrect decimals let instruction = transfer_checked( &program_id, - &account_info.key, - &mint_info.key, - &account_info.key, - &owner_info.key, + account_info.key, + mint_info.key, + account_info.key, + owner_info.key, &[], 1, 10, // <-- incorrect decimals @@ -2159,10 +2159,10 @@ mod tests { // incorrect mint let instruction = transfer_checked( &program_id, - &account_info.key, - &account3_info.key, // <-- incorrect mint - &account_info.key, - &owner_info.key, + account_info.key, + account3_info.key, // <-- incorrect mint + account_info.key, + owner_info.key, &[], 1, 2, @@ -2185,9 +2185,9 @@ mod tests { // approve delegate let instruction = approve( &program_id, - &account_info.key, - &delegate_info.key, - &owner_info.key, + account_info.key, + delegate_info.key, + owner_info.key, &[], 100, ) @@ -2206,9 +2206,9 @@ mod tests { // delegate transfer let instruction = transfer( &program_id, - &account_info.key, - &account_info.key, - &delegate_info.key, + account_info.key, + account_info.key, + delegate_info.key, &[], 100, ) @@ -2233,10 +2233,10 @@ mod tests { // delegate transfer checked let instruction = transfer_checked( &program_id, - &account_info.key, - &mint_info.key, - &account_info.key, - &delegate_info.key, + account_info.key, + mint_info.key, + account_info.key, + delegate_info.key, &[], 100, 2, @@ -2263,9 +2263,9 @@ mod tests { // delegate insufficient funds let instruction = transfer( &program_id, - &account_info.key, - &account_info.key, - &delegate_info.key, + account_info.key, + account_info.key, + delegate_info.key, &[], 101, ) @@ -2286,10 +2286,10 @@ mod tests { // delegate insufficient funds checked let instruction = transfer_checked( &program_id, - &account_info.key, - &mint_info.key, - &account_info.key, - &delegate_info.key, + account_info.key, + mint_info.key, + account_info.key, + delegate_info.key, &[], 101, 2, @@ -2312,9 +2312,9 @@ mod tests { // owner transfer with delegate assigned let instruction = transfer( &program_id, - &account_info.key, - &account_info.key, - &owner_info.key, + account_info.key, + account_info.key, + owner_info.key, &[], 1000, ) @@ -2338,10 +2338,10 @@ mod tests { // owner transfer with delegate assigned checked let instruction = transfer_checked( &program_id, - &account_info.key, - &mint_info.key, - &account_info.key, - &owner_info.key, + account_info.key, + mint_info.key, + account_info.key, + owner_info.key, &[], 1000, 2, From 15d0d433dd6a17c0f628797118f24c37200ae5b7 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Fri, 2 Jul 2021 10:53:41 -0600 Subject: [PATCH 124/248] Bump solana and borsh crates (#2015) --- program/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 8dea41a..74b77da 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,11 +16,11 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.1" -solana-program = "1.7.3" +solana-program = "1.7.4" thiserror = "1.0" [dev-dependencies] -solana-sdk = "1.7.3" +solana-sdk = "1.7.4" [lib] crate-type = ["cdylib", "lib"] From 9ac6916523891c27e0bc505837b09728d6c3d9a9 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Thu, 15 Jul 2021 11:56:15 +0200 Subject: [PATCH 125/248] token: Drop delegate on ownership transfer (#2085) * token: Drop delegate on ownership transfer * Unconditionalize the block --- program/src/processor.rs | 48 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index d91f618..3338556 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -367,6 +367,9 @@ impl Processor { } else { return Err(TokenError::InvalidInstruction.into()); } + + account.delegate = COption::None; + account.delegated_amount = 0; } AuthorityType::CloseAccount => { let authority = account.close_authority.unwrap_or(account.owner); @@ -3003,6 +3006,7 @@ mod tests { let owner2_key = Pubkey::new_unique(); let mut owner2_account = SolanaAccount::default(); let owner3_key = Pubkey::new_unique(); + let mut owner3_account = SolanaAccount::default(); let mint_key = Pubkey::new_unique(); let mut mint_account = SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); @@ -3133,12 +3137,34 @@ mod tests { ) ); + // set delegate + do_process_instruction( + approve( + &program_id, + &account_key, + &owner2_key, + &owner_key, + &[], + u64::MAX, + ) + .unwrap(), + vec![ + &mut account_account, + &mut owner2_account, + &mut owner_account, + ], + ) + .unwrap(); + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert_eq!(account.delegate, COption::Some(owner2_key)); + assert_eq!(account.delegated_amount, u64::MAX); + // set owner do_process_instruction( set_authority( &program_id, &account_key, - Some(&owner2_key), + Some(&owner3_key), AuthorityType::AccountOwner, &owner_key, &[], @@ -3148,6 +3174,26 @@ mod tests { ) .unwrap(); + // check delegate cleared + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert_eq!(account.delegate, COption::None); + assert_eq!(account.delegated_amount, 0); + + // set owner without existing delegate + do_process_instruction( + set_authority( + &program_id, + &account_key, + Some(&owner2_key), + AuthorityType::AccountOwner, + &owner3_key, + &[], + ) + .unwrap(), + vec![&mut account_account, &mut owner3_account], + ) + .unwrap(); + // set close_authority do_process_instruction( set_authority( From 2cb89579afaa557bd4f720f118504262c6a8337f Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Mon, 19 Jul 2021 21:50:42 +0200 Subject: [PATCH 126/248] token: Add SyncNative instruction (program, CLI, JS) (#2091) * Add SyncNative to program * Add CLI support * Add JS bindings * Fix syncNative test to not run for existing token program * Combine checks --- program/src/error.rs | 10 +++ program/src/instruction.rs | 35 +++++++++ program/src/processor.rs | 156 +++++++++++++++++++++++++++++++++++++ 3 files changed, 201 insertions(+) diff --git a/program/src/error.rs b/program/src/error.rs index 8305eaa..ed3faed 100644 --- a/program/src/error.rs +++ b/program/src/error.rs @@ -7,6 +7,7 @@ use thiserror::Error; /// Errors that may be returned by the Token program. #[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)] pub enum TokenError { + // 0 /// Lamport balance below rent-exempt threshold. #[error("Lamport balance below rent-exempt threshold")] NotRentExempt, @@ -22,6 +23,8 @@ pub enum TokenError { /// Owner does not match. #[error("Owner does not match")] OwnerMismatch, + + // 5 /// This token's supply is fixed and new tokens cannot be minted. #[error("Fixed supply")] FixedSupply, @@ -37,6 +40,8 @@ pub enum TokenError { /// State is uninitialized. #[error("State is unititialized")] UninitializedState, + + // 10 /// Instruction does not support native tokens #[error("Instruction does not support native tokens")] NativeNotSupported, @@ -52,6 +57,8 @@ pub enum TokenError { /// Operation overflowed #[error("Operation overflowed")] Overflow, + + // 15 /// Account does not support specified authority type. #[error("Account does not support specified authority type")] AuthorityTypeNotSupported, @@ -64,6 +71,9 @@ pub enum TokenError { /// Mint decimals mismatch between the client and mint #[error("The provided decimals value different from the Mint decimals")] MintDecimalsMismatch, + /// Instruction does not support non-native tokens + #[error("Instruction does not support non-native tokens")] + NonNativeNotSupported, } impl From for ProgramError { fn from(e: TokenError) -> Self { diff --git a/program/src/instruction.rs b/program/src/instruction.rs index da28205..3e9629a 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -363,6 +363,16 @@ pub enum TokenInstruction { /// The new account's owner/multisignature. owner: Pubkey, }, + /// Given a wrapped / native token account (a token account containing SOL) + /// updates its amount field based on the account's underlying `lamports`. + /// This is useful if a non-wrapped SOL account uses `system_instruction::transfer` + /// to move lamports to a wrapped token account, and needs to have its token + /// `amount` field updated. + /// + /// Accounts expected by this instruction: + /// + /// 0. `[writable]` The native token account to sync with its underlying lamports. + SyncNative, } impl TokenInstruction { /// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html). @@ -464,6 +474,7 @@ impl TokenInstruction { let (owner, _rest) = Self::unpack_pubkey(rest)?; Self::InitializeAccount2 { owner } } + 17 => Self::SyncNative, _ => return Err(TokenError::InvalidInstruction.into()), }) @@ -540,6 +551,9 @@ impl TokenInstruction { buf.push(16); buf.extend_from_slice(owner.as_ref()); } + &Self::SyncNative => { + buf.push(17); + } }; buf } @@ -1119,6 +1133,20 @@ pub fn burn_checked( }) } +/// Creates a `SyncNative` instruction +pub fn sync_native( + token_program_id: &Pubkey, + account_pubkey: &Pubkey, +) -> Result { + check_program_account(token_program_id)?; + + Ok(Instruction { + program_id: *token_program_id, + accounts: vec![AccountMeta::new(*account_pubkey, false)], + data: TokenInstruction::SyncNative.pack(), + }) +} + /// Utility function that checks index is between MIN_SIGNERS and MAX_SIGNERS pub fn is_valid_signer_index(index: usize) -> bool { (MIN_SIGNERS..=MAX_SIGNERS).contains(&index) @@ -1288,5 +1316,12 @@ mod test { assert_eq!(packed, expect); let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); + + let check = TokenInstruction::SyncNative; + let packed = check.pack(); + let expect = vec![17u8]; + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); } } diff --git a/program/src/processor.rs b/program/src/processor.rs index 3338556..44f66f4 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -644,6 +644,33 @@ impl Processor { Ok(()) } + /// Processes a [SyncNative](enum.TokenInstruction.html) instruction + pub fn process_sync_native(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let native_account_info = next_account_info(account_info_iter)?; + + if native_account_info.owner != program_id { + return Err(ProgramError::IncorrectProgramId); + } + let mut native_account = Account::unpack(&native_account_info.data.borrow())?; + + if let COption::Some(rent_exempt_reserve) = native_account.is_native { + let new_amount = native_account_info + .lamports() + .checked_sub(rent_exempt_reserve) + .ok_or(TokenError::Overflow)?; + if new_amount < native_account.amount { + return Err(TokenError::InvalidState.into()); + } + native_account.amount = new_amount; + } else { + return Err(TokenError::NonNativeNotSupported.into()); + } + + Account::pack(native_account, &mut native_account_info.data.borrow_mut())?; + Ok(()) + } + /// Processes an [Instruction](enum.Instruction.html). pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult { let instruction = TokenInstruction::unpack(input)?; @@ -724,6 +751,10 @@ impl Processor { msg!("Instruction: BurnChecked"); Self::process_burn(program_id, accounts, amount, Some(decimals)) } + TokenInstruction::SyncNative => { + msg!("Instruction: SyncNative"); + Self::process_sync_native(program_id, accounts) + } } } @@ -802,6 +833,9 @@ impl PrintProgramError for TokenError { TokenError::MintDecimalsMismatch => { msg!("Error: decimals different from the Mint decimals") } + TokenError::NonNativeNotSupported => { + msg!("Error: Instruction does not support non-native tokens") + } } } } @@ -5775,4 +5809,126 @@ mod tests { assert_eq!(account_account, account2_account); } + + #[test] + fn test_sync_native() { + let program_id = crate::id(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let native_account_key = Pubkey::new_unique(); + let lamports = 40; + let mut native_account = SolanaAccount::new( + account_minimum_balance() + lamports, + Account::get_packed_len(), + &program_id, + ); + let non_native_account_key = Pubkey::new_unique(); + let mut non_native_account = SolanaAccount::new( + account_minimum_balance() + 50, + Account::get_packed_len(), + &program_id, + ); + + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let mut rent_sysvar = rent_sysvar(); + + // initialize non-native mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + // initialize non-native account + do_process_instruction( + initialize_account(&program_id, &non_native_account_key, &mint_key, &owner_key) + .unwrap(), + vec![ + &mut non_native_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + let account = Account::unpack_unchecked(&non_native_account.data).unwrap(); + assert!(!account.is_native()); + assert_eq!(account.amount, 0); + + // fail sync non-native + assert_eq!( + Err(TokenError::NonNativeNotSupported.into()), + do_process_instruction( + sync_native(&program_id, &non_native_account_key,).unwrap(), + vec![&mut non_native_account], + ) + ); + + // fail sync uninitialized + assert_eq!( + Err(ProgramError::UninitializedAccount), + do_process_instruction( + sync_native(&program_id, &native_account_key,).unwrap(), + vec![&mut native_account], + ) + ); + + // wrap native account + do_process_instruction( + initialize_account( + &program_id, + &native_account_key, + &crate::native_mint::id(), + &owner_key, + ) + .unwrap(), + vec![ + &mut native_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + let account = Account::unpack_unchecked(&native_account.data).unwrap(); + assert!(account.is_native()); + assert_eq!(account.amount, lamports); + + // sync, no change + do_process_instruction( + sync_native(&program_id, &native_account_key).unwrap(), + vec![&mut native_account], + ) + .unwrap(); + let account = Account::unpack_unchecked(&native_account.data).unwrap(); + assert_eq!(account.amount, lamports); + + // transfer sol + let new_lamports = lamports + 50; + native_account.lamports = account_minimum_balance() + new_lamports; + + // success sync + do_process_instruction( + sync_native(&program_id, &native_account_key).unwrap(), + vec![&mut native_account], + ) + .unwrap(); + let account = Account::unpack_unchecked(&native_account.data).unwrap(); + assert_eq!(account.amount, new_lamports); + + // reduce sol + native_account.lamports -= 1; + + // fail sync + assert_eq!( + Err(TokenError::InvalidState.into()), + do_process_instruction( + sync_native(&program_id, &native_account_key,).unwrap(), + vec![&mut native_account], + ) + ); + } } From 80e1722cf85c58023803a7f5eead65b6e1120ef6 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Mon, 19 Jul 2021 17:57:26 -0600 Subject: [PATCH 127/248] token: Bump C bindings (#2109) * Update token C bindings * Fix Readme typo --- program/inc/token.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/program/inc/token.h b/program/inc/token.h index f68bba7..145c0c5 100644 --- a/program/inc/token.h +++ b/program/inc/token.h @@ -427,6 +427,18 @@ typedef enum Token_TokenInstruction_Tag { * 3. `[]` Rent sysvar */ Token_TokenInstruction_InitializeAccount2, + /** + * Given a wrapped / native token account (a token account containing SOL) + * updates its amount field based on the account's underlying `lamports`. + * This is useful if a non-wrapped SOL account uses `system_instruction::transfer` + * to move lamports to a wrapped token account, and needs to have its token + * `amount` field updated. + * + * Accounts expected by this instruction: + * + * 0. `[writable]` The native token account to sync with its underlying lamports. + */ + Token_TokenInstruction_SyncNative, } Token_TokenInstruction_Tag; typedef struct Token_TokenInstruction_Token_InitializeMint_Body { From 50c4c39da6a0de3fb7193681b4d2ed3bb989562f Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Tue, 20 Jul 2021 19:51:46 +0200 Subject: [PATCH 128/248] token: Clear close authority for native accounts (#2115) --- program/src/processor.rs | 47 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index 44f66f4..276c3be 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -370,6 +370,10 @@ impl Processor { account.delegate = COption::None; account.delegated_amount = 0; + + if account.is_native() { + account.close_authority = COption::None; + } } AuthorityType::CloseAccount => { let authority = account.close_authority.unwrap_or(account.owner); @@ -5066,6 +5070,9 @@ mod tests { let mut account3_account = SolanaAccount::new(account_minimum_balance(), 0, &program_id); let owner_key = Pubkey::new_unique(); let mut owner_account = SolanaAccount::default(); + let owner2_key = Pubkey::new_unique(); + let mut owner2_account = SolanaAccount::default(); + let owner3_key = Pubkey::new_unique(); let mut rent_sysvar = rent_sysvar(); // initialize native account @@ -5205,13 +5212,49 @@ mod tests { assert!(account.is_native()); assert_eq!(account.amount, 40); + // set close authority + do_process_instruction( + set_authority( + &program_id, + &account_key, + Some(&owner3_key), + AuthorityType::CloseAccount, + &owner_key, + &[], + ) + .unwrap(), + vec![&mut account_account, &mut owner_account], + ) + .unwrap(); + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert_eq!(account.close_authority, COption::Some(owner3_key)); + + // set new account owner + do_process_instruction( + set_authority( + &program_id, + &account_key, + Some(&owner2_key), + AuthorityType::AccountOwner, + &owner_key, + &[], + ) + .unwrap(), + vec![&mut account_account, &mut owner_account], + ) + .unwrap(); + + // close authority cleared + let account = Account::unpack_unchecked(&account_account.data).unwrap(); + assert_eq!(account.close_authority, COption::None); + // close native account do_process_instruction( - close_account(&program_id, &account_key, &account3_key, &owner_key, &[]).unwrap(), + close_account(&program_id, &account_key, &account3_key, &owner2_key, &[]).unwrap(), vec![ &mut account_account, &mut account3_account, - &mut owner_account, + &mut owner2_account, ], ) .unwrap(); From 45f3fb57113511e9b96351fb9ae46e0bfa458041 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Tue, 20 Jul 2021 23:40:32 +0200 Subject: [PATCH 129/248] token: Bump to 3.2.0 everywhere (#2116) --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 74b77da..f1dc7be 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-token" -version = "3.1.1" +version = "3.2.0" description = "Solana Program Library Token" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" From e1280def839da31aa0a9808fc748dd16a83b8392 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 Jul 2021 23:02:28 +0000 Subject: [PATCH 130/248] build(deps): bump num_enum from 0.5.1 to 0.5.2 (#2119) Bumps [num_enum](https://github.com/illicitonion/num_enum) from 0.5.1 to 0.5.2. - [Release notes](https://github.com/illicitonion/num_enum/releases) - [Commits](https://github.com/illicitonion/num_enum/compare/0.5.1...0.5.2) --- updated-dependencies: - dependency-name: num_enum dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index f1dc7be..b370b22 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -15,7 +15,7 @@ no-entrypoint = [] arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" -num_enum = "0.5.1" +num_enum = "0.5.2" solana-program = "1.7.4" thiserror = "1.0" From 0b040f3bb3c78d069a99997efba1aa34e0531ae3 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Fri, 23 Jul 2021 10:47:06 -0600 Subject: [PATCH 131/248] Bump solana crates (#2139) --- program/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index b370b22..2bcc091 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,11 +16,11 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.2" -solana-program = "1.7.4" +solana-program = "1.7.7" thiserror = "1.0" [dev-dependencies] -solana-sdk = "1.7.4" +solana-sdk = "1.7.7" [lib] crate-type = ["cdylib", "lib"] From d095562c264812a4189d4ffb558cf77f9d2f39ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Aug 2021 11:16:44 +0000 Subject: [PATCH 132/248] build(deps): bump num_enum from 0.5.2 to 0.5.3 (#2207) Bumps [num_enum](https://github.com/illicitonion/num_enum) from 0.5.2 to 0.5.3. - [Release notes](https://github.com/illicitonion/num_enum/releases) - [Commits](https://github.com/illicitonion/num_enum/compare/0.5.2...0.5.3) --- updated-dependencies: - dependency-name: num_enum dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 2bcc091..b578ada 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -15,7 +15,7 @@ no-entrypoint = [] arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" -num_enum = "0.5.2" +num_enum = "0.5.3" solana-program = "1.7.7" thiserror = "1.0" From 298e9903fc9b41d2edd6464d8df374eb6c3e4de4 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Fri, 13 Aug 2021 11:59:38 -0700 Subject: [PATCH 133/248] Add InitializeMint2/InitializeMultisig2/InitializeAccount3 instructions --- program/src/instruction.rs | 197 ++++++++++++++++++++++++++++++++++- program/src/lib.rs | 2 +- program/src/processor.rs | 204 +++++++++++++++++++++++++++++++++++-- 3 files changed, 390 insertions(+), 13 deletions(-) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 3e9629a..c5936cf 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -373,6 +373,42 @@ pub enum TokenInstruction { /// /// 0. `[writable]` The native token account to sync with its underlying lamports. SyncNative, + /// Like InitializeAccount2, but does not require the Rent sysvar to be provided + /// + /// Accounts expected by this instruction: + /// + /// 0. `[writable]` The account to initialize. + /// 1. `[]` The mint this account will be associated with. + InitializeAccount3 { + /// The new account's owner/multisignature. + owner: Pubkey, + }, + /// Like InitializeMultisig, but does not require the Rent sysvar to be provided + /// + /// Accounts expected by this instruction: + /// + /// 0. `[writable]` The multisignature account to initialize. + /// 1. ..1+N. `[]` The signer accounts, must equal to N where 1 <= N <= + /// 11. + InitializeMultisig2 { + /// The number of signers (M) required to validate this multisignature + /// account. + m: u8, + }, + /// Like InitializeMint, but does not require the Rent sysvar to be provided + /// + /// Accounts expected by this instruction: + /// + /// 0. `[writable]` The mint to initialize. + /// + InitializeMint2 { + /// Number of base 10 digits to the right of the decimal place. + decimals: u8, + /// The authority/multisignature to mint tokens. + mint_authority: Pubkey, + /// The freeze authority/multisignature of the mint. + freeze_authority: COption, + }, } impl TokenInstruction { /// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html). @@ -475,7 +511,24 @@ impl TokenInstruction { Self::InitializeAccount2 { owner } } 17 => Self::SyncNative, - + 18 => { + let (owner, _rest) = Self::unpack_pubkey(rest)?; + Self::InitializeAccount3 { owner } + } + 19 => { + let &m = rest.get(0).ok_or(InvalidInstruction)?; + Self::InitializeMultisig2 { m } + } + 20 => { + let (&decimals, rest) = rest.split_first().ok_or(InvalidInstruction)?; + let (mint_authority, rest) = Self::unpack_pubkey(rest)?; + let (freeze_authority, _rest) = Self::unpack_pubkey_option(rest)?; + Self::InitializeMint2 { + mint_authority, + freeze_authority, + decimals, + } + } _ => return Err(TokenError::InvalidInstruction.into()), }) } @@ -554,6 +607,24 @@ impl TokenInstruction { &Self::SyncNative => { buf.push(17); } + &Self::InitializeAccount3 { owner } => { + buf.push(18); + buf.extend_from_slice(owner.as_ref()); + } + &Self::InitializeMultisig2 { m } => { + buf.push(19); + buf.push(m); + } + &Self::InitializeMint2 { + ref mint_authority, + ref freeze_authority, + decimals, + } => { + buf.push(20); + buf.push(decimals); + buf.extend_from_slice(mint_authority.as_ref()); + Self::pack_pubkey_option(freeze_authority, &mut buf); + } }; buf } @@ -655,6 +726,32 @@ pub fn initialize_mint( }) } +/// Creates a `InitializeMint2` instruction. +pub fn initialize_mint2( + token_program_id: &Pubkey, + mint_pubkey: &Pubkey, + mint_authority_pubkey: &Pubkey, + freeze_authority_pubkey: Option<&Pubkey>, + decimals: u8, +) -> Result { + check_program_account(token_program_id)?; + let freeze_authority = freeze_authority_pubkey.cloned().into(); + let data = TokenInstruction::InitializeMint2 { + mint_authority: *mint_authority_pubkey, + freeze_authority, + decimals, + } + .pack(); + + let accounts = vec![AccountMeta::new(*mint_pubkey, false)]; + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + /// Creates a `InitializeAccount` instruction. pub fn initialize_account( token_program_id: &Pubkey, @@ -705,6 +802,31 @@ pub fn initialize_account2( }) } +/// Creates a `InitializeAccount3` instruction. +pub fn initialize_account3( + token_program_id: &Pubkey, + account_pubkey: &Pubkey, + mint_pubkey: &Pubkey, + owner_pubkey: &Pubkey, +) -> Result { + check_program_account(token_program_id)?; + let data = TokenInstruction::InitializeAccount3 { + owner: *owner_pubkey, + } + .pack(); + + let accounts = vec![ + AccountMeta::new(*account_pubkey, false), + AccountMeta::new_readonly(*mint_pubkey, false), + ]; + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + /// Creates a `InitializeMultisig` instruction. pub fn initialize_multisig( token_program_id: &Pubkey, @@ -735,6 +857,35 @@ pub fn initialize_multisig( }) } +/// Creates a `InitializeMultisig2` instruction. +pub fn initialize_multisig2( + token_program_id: &Pubkey, + multisig_pubkey: &Pubkey, + signer_pubkeys: &[&Pubkey], + m: u8, +) -> Result { + check_program_account(token_program_id)?; + if !is_valid_signer_index(m as usize) + || !is_valid_signer_index(signer_pubkeys.len()) + || m as usize > signer_pubkeys.len() + { + return Err(ProgramError::MissingRequiredSignature); + } + let data = TokenInstruction::InitializeMultisig2 { m }.pack(); + + let mut accounts = Vec::with_capacity(1 + 1 + signer_pubkeys.len()); + accounts.push(AccountMeta::new(*multisig_pubkey, false)); + for signer_pubkey in signer_pubkeys.iter() { + accounts.push(AccountMeta::new_readonly(**signer_pubkey, false)); + } + + Ok(Instruction { + program_id: *token_program_id, + accounts, + data, + }) +} + /// Creates a `Transfer` instruction. pub fn transfer( token_program_id: &Pubkey, @@ -1323,5 +1474,49 @@ mod test { assert_eq!(packed, expect); let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); + + let check = TokenInstruction::InitializeAccount3 { + owner: Pubkey::new(&[2u8; 32]), + }; + let packed = check.pack(); + let mut expect = vec![18u8]; + expect.extend_from_slice(&[2u8; 32]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::InitializeMultisig2 { m: 1 }; + let packed = check.pack(); + let expect = Vec::from([19u8, 1]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::InitializeMint2 { + decimals: 2, + mint_authority: Pubkey::new(&[1u8; 32]), + freeze_authority: COption::None, + }; + let packed = check.pack(); + let mut expect = Vec::from([20u8, 2]); + expect.extend_from_slice(&[1u8; 32]); + expect.extend_from_slice(&[0]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::InitializeMint2 { + decimals: 2, + mint_authority: Pubkey::new(&[2u8; 32]), + freeze_authority: COption::Some(Pubkey::new(&[3u8; 32])), + }; + let packed = check.pack(); + let mut expect = vec![20u8, 2]; + expect.extend_from_slice(&[2u8; 32]); + expect.extend_from_slice(&[1]); + expect.extend_from_slice(&[3u8; 32]); + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); } } diff --git a/program/src/lib.rs b/program/src/lib.rs index 529aaa1..4dda68d 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -1,5 +1,5 @@ #![deny(missing_docs)] -#![forbid(unsafe_code)] +#![cfg_attr(not(test), forbid(unsafe_code))] //! An ERC20-like Token program for the Solana blockchain diff --git a/program/src/processor.rs b/program/src/processor.rs index 276c3be..0587595 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -21,17 +21,21 @@ use solana_program::{ /// Program state handler. pub struct Processor {} impl Processor { - /// Processes an [InitializeMint](enum.TokenInstruction.html) instruction. - pub fn process_initialize_mint( + fn _process_initialize_mint( accounts: &[AccountInfo], decimals: u8, mint_authority: Pubkey, freeze_authority: COption, + rent_sysvar_account: bool, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let mint_info = next_account_info(account_info_iter)?; let mint_data_len = mint_info.data_len(); - let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?; + let rent = if rent_sysvar_account { + Rent::from_account_info(next_account_info(account_info_iter)?)? + } else { + Rent::get()? + }; let mut mint = Mint::unpack_unchecked(&mint_info.data.borrow())?; if mint.is_initialized { @@ -52,9 +56,30 @@ impl Processor { Ok(()) } + /// Processes an [InitializeMint](enum.TokenInstruction.html) instruction. + pub fn process_initialize_mint( + accounts: &[AccountInfo], + decimals: u8, + mint_authority: Pubkey, + freeze_authority: COption, + ) -> ProgramResult { + Self::_process_initialize_mint(accounts, decimals, mint_authority, freeze_authority, true) + } + + /// Processes an [InitializeMint2](enum.TokenInstruction.html) instruction. + pub fn process_initialize_mint2( + accounts: &[AccountInfo], + decimals: u8, + mint_authority: Pubkey, + freeze_authority: COption, + ) -> ProgramResult { + Self::_process_initialize_mint(accounts, decimals, mint_authority, freeze_authority, false) + } + fn _process_initialize_account( accounts: &[AccountInfo], owner: Option<&Pubkey>, + rent_sysvar_account: bool, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let new_account_info = next_account_info(account_info_iter)?; @@ -65,7 +90,11 @@ impl Processor { next_account_info(account_info_iter)?.key }; let new_account_info_data_len = new_account_info.data_len(); - let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?; + let rent = if rent_sysvar_account { + Rent::from_account_info(next_account_info(account_info_iter)?)? + } else { + Rent::get()? + }; let mut account = Account::unpack_unchecked(&new_account_info.data.borrow())?; if account.is_initialized() { @@ -105,20 +134,32 @@ impl Processor { /// Processes an [InitializeAccount](enum.TokenInstruction.html) instruction. pub fn process_initialize_account(accounts: &[AccountInfo]) -> ProgramResult { - Self::_process_initialize_account(accounts, None) + Self::_process_initialize_account(accounts, None, true) } /// Processes an [InitializeAccount2](enum.TokenInstruction.html) instruction. pub fn process_initialize_account2(accounts: &[AccountInfo], owner: Pubkey) -> ProgramResult { - Self::_process_initialize_account(accounts, Some(&owner)) + Self::_process_initialize_account(accounts, Some(&owner), true) } - /// Processes a [InitializeMultisig](enum.TokenInstruction.html) instruction. - pub fn process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult { + /// Processes an [InitializeAccount3](enum.TokenInstruction.html) instruction. + pub fn process_initialize_account3(accounts: &[AccountInfo], owner: Pubkey) -> ProgramResult { + Self::_process_initialize_account(accounts, Some(&owner), false) + } + + fn _process_initialize_multisig( + accounts: &[AccountInfo], + m: u8, + rent_sysvar_account: bool, + ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let multisig_info = next_account_info(account_info_iter)?; let multisig_info_data_len = multisig_info.data_len(); - let rent = &Rent::from_account_info(next_account_info(account_info_iter)?)?; + let rent = if rent_sysvar_account { + Rent::from_account_info(next_account_info(account_info_iter)?)? + } else { + Rent::get()? + }; let mut multisig = Multisig::unpack_unchecked(&multisig_info.data.borrow())?; if multisig.is_initialized { @@ -148,6 +189,16 @@ impl Processor { Ok(()) } + /// Processes a [InitializeMultisig](enum.TokenInstruction.html) instruction. + pub fn process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult { + Self::_process_initialize_multisig(accounts, m, true) + } + + /// Processes a [InitializeMultisig2](enum.TokenInstruction.html) instruction. + pub fn process_initialize_multisig2(accounts: &[AccountInfo], m: u8) -> ProgramResult { + Self::_process_initialize_multisig(accounts, m, false) + } + /// Processes a [Transfer](enum.TokenInstruction.html) instruction. pub fn process_transfer( program_id: &Pubkey, @@ -688,6 +739,14 @@ impl Processor { msg!("Instruction: InitializeMint"); Self::process_initialize_mint(accounts, decimals, mint_authority, freeze_authority) } + TokenInstruction::InitializeMint2 { + decimals, + mint_authority, + freeze_authority, + } => { + msg!("Instruction: InitializeMint2"); + Self::process_initialize_mint2(accounts, decimals, mint_authority, freeze_authority) + } TokenInstruction::InitializeAccount => { msg!("Instruction: InitializeAccount"); Self::process_initialize_account(accounts) @@ -696,10 +755,18 @@ impl Processor { msg!("Instruction: InitializeAccount2"); Self::process_initialize_account2(accounts, owner) } + TokenInstruction::InitializeAccount3 { owner } => { + msg!("Instruction: InitializeAccount3"); + Self::process_initialize_account3(accounts, owner) + } TokenInstruction::InitializeMultisig { m } => { msg!("Instruction: InitializeMultisig"); Self::process_initialize_multisig(accounts, m) } + TokenInstruction::InitializeMultisig2 { m } => { + msg!("Instruction: InitializeMultisig2"); + Self::process_initialize_multisig2(accounts, m) + } TokenInstruction::Transfer { amount } => { msg!("Instruction: Transfer"); Self::process_transfer(program_id, accounts, amount, None) @@ -849,16 +916,60 @@ mod tests { use super::*; use crate::instruction::*; use solana_program::{ - account_info::IntoAccountInfo, clock::Epoch, instruction::Instruction, sysvar::rent, + account_info::IntoAccountInfo, clock::Epoch, instruction::Instruction, program_error, + sysvar::rent, }; use solana_sdk::account::{ create_account_for_test, create_is_signer_account_infos, Account as SolanaAccount, }; + struct SyscallStubs {} + impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs { + fn sol_log(&self, _message: &str) {} + + fn sol_invoke_signed( + &self, + _instruction: &Instruction, + _account_infos: &[AccountInfo], + _signers_seeds: &[&[&[u8]]], + ) -> ProgramResult { + Err(ProgramError::Custom(42)) // Not supported + } + + fn sol_get_clock_sysvar(&self, _var_addr: *mut u8) -> u64 { + program_error::UNSUPPORTED_SYSVAR + } + + fn sol_get_epoch_schedule_sysvar(&self, _var_addr: *mut u8) -> u64 { + program_error::UNSUPPORTED_SYSVAR + } + + #[allow(deprecated)] + fn sol_get_fees_sysvar(&self, _var_addr: *mut u8) -> u64 { + program_error::UNSUPPORTED_SYSVAR + } + + fn sol_get_rent_sysvar(&self, var_addr: *mut u8) -> u64 { + unsafe { + *(var_addr as *mut _ as *mut Rent) = Rent::default(); + } + solana_program::entrypoint::SUCCESS + } + } + fn do_process_instruction( instruction: Instruction, accounts: Vec<&mut SolanaAccount>, ) -> ProgramResult { + { + use std::sync::Once; + static ONCE: Once = Once::new(); + + ONCE.call_once(|| { + solana_sdk::program_stubs::set_syscall_stubs(Box::new(SyscallStubs {})); + }); + } + let mut meta = instruction .accounts .iter() @@ -1072,6 +1183,53 @@ mod tests { assert_eq!(mint.freeze_authority, COption::Some(owner_key)); } + #[test] + fn test_initialize_mint2() { + let program_id = crate::id(); + let owner_key = Pubkey::new_unique(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = SolanaAccount::new(42, Mint::get_packed_len(), &program_id); + let mint2_key = Pubkey::new_unique(); + let mut mint2_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + + // mint is not rent exempt + assert_eq!( + Err(TokenError::NotRentExempt.into()), + do_process_instruction( + initialize_mint2(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account] + ) + ); + + mint_account.lamports = mint_minimum_balance(); + + // create new mint + do_process_instruction( + initialize_mint2(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + // create twice + assert_eq!( + Err(TokenError::AlreadyInUse.into()), + do_process_instruction( + initialize_mint2(&program_id, &mint_key, &owner_key, None, 2,).unwrap(), + vec![&mut mint_account] + ) + ); + + // create another mint that can freeze + do_process_instruction( + initialize_mint2(&program_id, &mint2_key, &owner_key, Some(&owner_key), 2).unwrap(), + vec![&mut mint2_account], + ) + .unwrap(); + let mint = Mint::unpack_unchecked(&mint2_account.data).unwrap(); + assert_eq!(mint.freeze_authority, COption::Some(owner_key)); + } + #[test] fn test_initialize_mint_account() { let program_id = crate::id(); @@ -4216,6 +4374,17 @@ mod tests { ], ) ); + assert_eq!( + Err(TokenError::NotRentExempt.into()), + do_process_instruction( + initialize_multisig2(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(), + vec![ + &mut multisig_account, + &mut rent_sysvar, + &mut account_info_iter.next().unwrap(), + ], + ) + ); multisig_account.lamports = multisig_minimum_balance(); @@ -5806,7 +5975,7 @@ mod tests { } #[test] - fn test_initialize_account2() { + fn test_initialize_account2_and_3() { let program_id = crate::id(); let account_key = Pubkey::new_unique(); let mut account_account = SolanaAccount::new( @@ -5819,6 +5988,11 @@ mod tests { Account::get_packed_len(), &program_id, ); + let mut account3_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); let owner_key = Pubkey::new_unique(); let mut owner_account = SolanaAccount::default(); let mint_key = Pubkey::new_unique(); @@ -5851,6 +6025,14 @@ mod tests { .unwrap(); assert_eq!(account_account, account2_account); + + do_process_instruction( + initialize_account3(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account3_account, &mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + assert_eq!(account_account, account3_account); } #[test] From 4b2acf6248c0cebdca7ad1aa0c56b39d808652b6 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Fri, 13 Aug 2021 18:55:06 -0700 Subject: [PATCH 134/248] review feedback --- program/src/processor.rs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index 0587595..a1d86f0 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -4374,19 +4374,9 @@ mod tests { ], ) ); - assert_eq!( - Err(TokenError::NotRentExempt.into()), - do_process_instruction( - initialize_multisig2(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(), - vec![ - &mut multisig_account, - &mut rent_sysvar, - &mut account_info_iter.next().unwrap(), - ], - ) - ); multisig_account.lamports = multisig_minimum_balance(); + let mut multisig_account2 = multisig_account.clone(); // single signer let account_info_iter = &mut signer_accounts.iter_mut(); @@ -4400,6 +4390,17 @@ mod tests { ) .unwrap(); + // single signer using `initialize_multisig2` + let account_info_iter = &mut signer_accounts.iter_mut(); + do_process_instruction( + initialize_multisig2(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(), + vec![ + &mut multisig_account2, + &mut account_info_iter.next().unwrap(), + ], + ) + .unwrap(); + // multiple signer let account_info_iter = &mut signer_accounts.iter_mut(); do_process_instruction( @@ -6028,7 +6029,7 @@ mod tests { do_process_instruction( initialize_account3(&program_id, &account_key, &mint_key, &owner_key).unwrap(), - vec![&mut account3_account, &mut mint_account, &mut rent_sysvar], + vec![&mut account3_account, &mut mint_account], ) .unwrap(); From 1e6bee142ccc8379a3b6e53f2369e72abd5a4cfc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Aug 2021 11:15:31 +0000 Subject: [PATCH 135/248] build(deps): bump num_enum from 0.5.3 to 0.5.4 (#2293) Bumps [num_enum](https://github.com/illicitonion/num_enum) from 0.5.3 to 0.5.4. - [Release notes](https://github.com/illicitonion/num_enum/releases) - [Commits](https://github.com/illicitonion/num_enum/compare/0.5.3...0.5.4) --- updated-dependencies: - dependency-name: num_enum dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index b578ada..538e0c0 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -15,7 +15,7 @@ no-entrypoint = [] arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" -num_enum = "0.5.3" +num_enum = "0.5.4" solana-program = "1.7.7" thiserror = "1.0" From 42425f2eac26e804baae6a2b00886eea75ef964e Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Mon, 30 Aug 2021 12:20:33 -0700 Subject: [PATCH 136/248] Upgrade to Solana 1.7.11 --- program/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 538e0c0..9637ccc 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,11 +16,11 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.4" -solana-program = "1.7.7" +solana-program = "1.7.11" thiserror = "1.0" [dev-dependencies] -solana-sdk = "1.7.7" +solana-sdk = "1.7.11" [lib] crate-type = ["cdylib", "lib"] From 8c60c4cbe428b51de149758969296354cdf139f5 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Tue, 12 Oct 2021 09:14:32 -0700 Subject: [PATCH 137/248] Upgrade to Solana 1.8.0 --- program/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 9637ccc..b2def07 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,11 +16,11 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.4" -solana-program = "1.7.11" +solana-program = "1.8.0" thiserror = "1.0" [dev-dependencies] -solana-sdk = "1.7.11" +solana-sdk = "1.8.0" [lib] crate-type = ["cdylib", "lib"] From 8cbb21864c28e028dab4021560a945a79405308a Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Wed, 20 Oct 2021 22:20:12 -0700 Subject: [PATCH 138/248] Upgrade to Solana v1.8.1 --- program/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index b2def07..a199dab 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -16,11 +16,11 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.4" -solana-program = "1.8.0" +solana-program = "1.8.1" thiserror = "1.0" [dev-dependencies] -solana-sdk = "1.8.0" +solana-sdk = "1.8.1" [lib] crate-type = ["cdylib", "lib"] From f454d4d04ad9fbecb070c8f7d34f46421f685680 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Tue, 16 Nov 2021 23:15:45 +0100 Subject: [PATCH 139/248] token: Transition perf-monitor to solana-program-test (#2586) * token: Transition perf-monitor to solana-program-test * fmt * Refactor for clarity --- program/Cargo.toml | 2 + program/tests/action.rs | 140 +++++++++++ program/tests/assert_instruction_count.rs | 284 ++++++++++++++++++++++ 3 files changed, 426 insertions(+) create mode 100644 program/tests/action.rs create mode 100644 program/tests/assert_instruction_count.rs diff --git a/program/Cargo.toml b/program/Cargo.toml index a199dab..5b9a976 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -10,6 +10,7 @@ exclude = ["js/**"] [features] no-entrypoint = [] +test-bpf = [] [dependencies] arrayref = "0.3.6" @@ -20,6 +21,7 @@ solana-program = "1.8.1" thiserror = "1.0" [dev-dependencies] +solana-program-test = "1.8.1" solana-sdk = "1.8.1" [lib] diff --git a/program/tests/action.rs b/program/tests/action.rs new file mode 100644 index 0000000..0a67538 --- /dev/null +++ b/program/tests/action.rs @@ -0,0 +1,140 @@ +use { + solana_program_test::BanksClient, + solana_sdk::{ + hash::Hash, + program_pack::Pack, + pubkey::Pubkey, + signature::{Keypair, Signer}, + system_instruction, + transaction::Transaction, + transport::TransportError, + }, + spl_token::{ + id, instruction, + state::{Account, Mint}, + }, +}; + +pub async fn create_mint( + banks_client: &mut BanksClient, + payer: &Keypair, + recent_blockhash: Hash, + pool_mint: &Keypair, + manager: &Pubkey, + decimals: u8, +) -> Result<(), TransportError> { + let rent = banks_client.get_rent().await.unwrap(); + let mint_rent = rent.minimum_balance(Mint::LEN); + + let transaction = Transaction::new_signed_with_payer( + &[ + system_instruction::create_account( + &payer.pubkey(), + &pool_mint.pubkey(), + mint_rent, + Mint::LEN as u64, + &id(), + ), + instruction::initialize_mint(&id(), &pool_mint.pubkey(), manager, None, decimals) + .unwrap(), + ], + Some(&payer.pubkey()), + &[payer, pool_mint], + recent_blockhash, + ); + banks_client.process_transaction(transaction).await?; + Ok(()) +} + +pub async fn create_account( + banks_client: &mut BanksClient, + payer: &Keypair, + recent_blockhash: Hash, + account: &Keypair, + pool_mint: &Pubkey, + owner: &Pubkey, +) -> Result<(), TransportError> { + let rent = banks_client.get_rent().await.unwrap(); + let account_rent = rent.minimum_balance(Account::LEN); + + let transaction = Transaction::new_signed_with_payer( + &[ + system_instruction::create_account( + &payer.pubkey(), + &account.pubkey(), + account_rent, + Account::LEN as u64, + &id(), + ), + instruction::initialize_account(&id(), &account.pubkey(), pool_mint, owner).unwrap(), + ], + Some(&payer.pubkey()), + &[payer, account], + recent_blockhash, + ); + banks_client.process_transaction(transaction).await?; + Ok(()) +} + +pub async fn mint_to( + banks_client: &mut BanksClient, + payer: &Keypair, + recent_blockhash: Hash, + mint: &Pubkey, + account: &Pubkey, + mint_authority: &Keypair, + amount: u64, +) -> Result<(), TransportError> { + let transaction = Transaction::new_signed_with_payer( + &[ + instruction::mint_to(&id(), mint, account, &mint_authority.pubkey(), &[], amount) + .unwrap(), + ], + Some(&payer.pubkey()), + &[payer, mint_authority], + recent_blockhash, + ); + banks_client.process_transaction(transaction).await?; + Ok(()) +} + +pub async fn transfer( + banks_client: &mut BanksClient, + payer: &Keypair, + recent_blockhash: Hash, + source: &Pubkey, + destination: &Pubkey, + authority: &Keypair, + amount: u64, +) -> Result<(), TransportError> { + let transaction = Transaction::new_signed_with_payer( + &[ + instruction::transfer(&id(), source, destination, &authority.pubkey(), &[], amount) + .unwrap(), + ], + Some(&payer.pubkey()), + &[payer, authority], + recent_blockhash, + ); + banks_client.process_transaction(transaction).await?; + Ok(()) +} + +pub async fn burn( + banks_client: &mut BanksClient, + payer: &Keypair, + recent_blockhash: Hash, + mint: &Pubkey, + account: &Pubkey, + authority: &Keypair, + amount: u64, +) -> Result<(), TransportError> { + let transaction = Transaction::new_signed_with_payer( + &[instruction::burn(&id(), account, mint, &authority.pubkey(), &[], amount).unwrap()], + Some(&payer.pubkey()), + &[payer, authority], + recent_blockhash, + ); + banks_client.process_transaction(transaction).await?; + Ok(()) +} diff --git a/program/tests/assert_instruction_count.rs b/program/tests/assert_instruction_count.rs new file mode 100644 index 0000000..858bd55 --- /dev/null +++ b/program/tests/assert_instruction_count.rs @@ -0,0 +1,284 @@ +#![cfg(feature = "test-bpf")] + +mod action; +use { + solana_program_test::{processor, tokio, ProgramTest}, + solana_sdk::{ + program_pack::Pack, + pubkey::Pubkey, + signature::{Keypair, Signer}, + system_instruction, + transaction::Transaction, + }, + spl_token::{ + id, instruction, + processor::Processor, + state::{Account, Mint}, + }, +}; + +const TRANSFER_AMOUNT: u64 = 1_000_000_000_000_000; + +#[tokio::test] +async fn initialize_mint() { + let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process)); + pt.set_bpf_compute_max_units(2_500); // last known 2252 + let (mut banks_client, payer, recent_blockhash) = pt.start().await; + + let owner_key = Pubkey::new_unique(); + let mint = Keypair::new(); + let decimals = 9; + + let rent = banks_client.get_rent().await.unwrap(); + let mint_rent = rent.minimum_balance(Mint::LEN); + let transaction = Transaction::new_signed_with_payer( + &[system_instruction::create_account( + &payer.pubkey(), + &mint.pubkey(), + mint_rent, + Mint::LEN as u64, + &id(), + )], + Some(&payer.pubkey()), + &[&payer, &mint], + recent_blockhash, + ); + banks_client.process_transaction(transaction).await.unwrap(); + + let transaction = Transaction::new_signed_with_payer( + &[ + instruction::initialize_mint(&id(), &mint.pubkey(), &owner_key, None, decimals) + .unwrap(), + ], + Some(&payer.pubkey()), + &[&payer], + recent_blockhash, + ); + banks_client.process_transaction(transaction).await.unwrap(); +} + +#[tokio::test] +async fn initialize_account() { + let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process)); + pt.set_bpf_compute_max_units(4_000); // last known 3284 + let (mut banks_client, payer, recent_blockhash) = pt.start().await; + + let owner = Keypair::new(); + let mint = Keypair::new(); + let account = Keypair::new(); + let decimals = 9; + + action::create_mint( + &mut banks_client, + &payer, + recent_blockhash, + &mint, + &owner.pubkey(), + decimals, + ) + .await + .unwrap(); + let rent = banks_client.get_rent().await.unwrap(); + let account_rent = rent.minimum_balance(Account::LEN); + let transaction = Transaction::new_signed_with_payer( + &[system_instruction::create_account( + &payer.pubkey(), + &account.pubkey(), + account_rent, + Account::LEN as u64, + &id(), + )], + Some(&payer.pubkey()), + &[&payer, &account], + recent_blockhash, + ); + banks_client.process_transaction(transaction).await.unwrap(); + + let transaction = Transaction::new_signed_with_payer( + &[instruction::initialize_account( + &id(), + &account.pubkey(), + &mint.pubkey(), + &owner.pubkey(), + ) + .unwrap()], + Some(&payer.pubkey()), + &[&payer], + recent_blockhash, + ); + banks_client.process_transaction(transaction).await.unwrap(); +} + +#[tokio::test] +async fn mint_to() { + let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process)); + pt.set_bpf_compute_max_units(4_000); // last known 2668 + let (mut banks_client, payer, recent_blockhash) = pt.start().await; + + let owner = Keypair::new(); + let mint = Keypair::new(); + let account = Keypair::new(); + let decimals = 9; + + action::create_mint( + &mut banks_client, + &payer, + recent_blockhash, + &mint, + &owner.pubkey(), + decimals, + ) + .await + .unwrap(); + action::create_account( + &mut banks_client, + &payer, + recent_blockhash, + &account, + &mint.pubkey(), + &owner.pubkey(), + ) + .await + .unwrap(); + + let transaction = Transaction::new_signed_with_payer( + &[instruction::mint_to( + &id(), + &mint.pubkey(), + &account.pubkey(), + &owner.pubkey(), + &[], + TRANSFER_AMOUNT, + ) + .unwrap()], + Some(&payer.pubkey()), + &[&payer, &owner], + recent_blockhash, + ); + banks_client.process_transaction(transaction).await.unwrap(); +} + +#[tokio::test] +async fn transfer() { + let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process)); + pt.set_bpf_compute_max_units(4_000); // last known 2972 + let (mut banks_client, payer, recent_blockhash) = pt.start().await; + + let owner = Keypair::new(); + let mint = Keypair::new(); + let source = Keypair::new(); + let destination = Keypair::new(); + let decimals = 9; + + action::create_mint( + &mut banks_client, + &payer, + recent_blockhash, + &mint, + &owner.pubkey(), + decimals, + ) + .await + .unwrap(); + action::create_account( + &mut banks_client, + &payer, + recent_blockhash, + &source, + &mint.pubkey(), + &owner.pubkey(), + ) + .await + .unwrap(); + action::create_account( + &mut banks_client, + &payer, + recent_blockhash, + &destination, + &mint.pubkey(), + &owner.pubkey(), + ) + .await + .unwrap(); + + action::mint_to( + &mut banks_client, + &payer, + recent_blockhash, + &mint.pubkey(), + &source.pubkey(), + &owner, + TRANSFER_AMOUNT, + ) + .await + .unwrap(); + + action::transfer( + &mut banks_client, + &payer, + recent_blockhash, + &source.pubkey(), + &destination.pubkey(), + &owner, + TRANSFER_AMOUNT, + ) + .await + .unwrap(); +} + +#[tokio::test] +async fn burn() { + let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process)); + pt.set_bpf_compute_max_units(4_000); // last known 2655 + let (mut banks_client, payer, recent_blockhash) = pt.start().await; + + let owner = Keypair::new(); + let mint = Keypair::new(); + let account = Keypair::new(); + let decimals = 9; + + action::create_mint( + &mut banks_client, + &payer, + recent_blockhash, + &mint, + &owner.pubkey(), + decimals, + ) + .await + .unwrap(); + action::create_account( + &mut banks_client, + &payer, + recent_blockhash, + &account, + &mint.pubkey(), + &owner.pubkey(), + ) + .await + .unwrap(); + + action::mint_to( + &mut banks_client, + &payer, + recent_blockhash, + &mint.pubkey(), + &account.pubkey(), + &owner, + TRANSFER_AMOUNT, + ) + .await + .unwrap(); + + action::burn( + &mut banks_client, + &payer, + recent_blockhash, + &mint.pubkey(), + &account.pubkey(), + &owner, + TRANSFER_AMOUNT, + ) + .await + .unwrap(); +} From 5203f40969441448eb0bd068e08506010c1b5cfb Mon Sep 17 00:00:00 2001 From: Victor Pontis Date: Sun, 28 Nov 2021 20:16:33 -0800 Subject: [PATCH 140/248] Update comment on state.rs regarding is_native MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I was confused when reading this comment and had to read the code to realize that the comment isn't a typo. (Btw, let me know if this kind of small changes are helpful or not 👍.) --- program/src/state.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/src/state.rs b/program/src/state.rs index c35f342..15cc183 100644 --- a/program/src/state.rs +++ b/program/src/state.rs @@ -95,9 +95,9 @@ pub struct Account { pub delegate: COption, /// The account's state pub state: AccountState, - /// If is_some, this is a native token, and the value logs the rent-exempt reserve. An Account - /// is required to be rent-exempt, so the value is used by the Processor to ensure that wrapped - /// SOL accounts do not drop below this threshold. + /// If is_native.is_some, this is a native token, and the value logs the rent-exempt reserve. An + /// Account is required to be rent-exempt, so the value is used by the Processor to ensure that + /// wrapped SOL accounts do not drop below this threshold. pub is_native: COption, /// The amount delegated pub delegated_amount: u64, From 1f48fb38c398e4df3102d1bc4e1f9fe4dfa28098 Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Thu, 9 Dec 2021 22:12:22 -0800 Subject: [PATCH 141/248] Update to Rust 1.57.0 --- program/src/processor.rs | 93 +++++++++++++++++++--------------------- 1 file changed, 45 insertions(+), 48 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index a1d86f0..5ab0002 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -4370,7 +4370,7 @@ mod tests { vec![ &mut multisig_account, &mut rent_sysvar, - &mut account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), ], ) ); @@ -4385,7 +4385,7 @@ mod tests { vec![ &mut multisig_account, &mut rent_sysvar, - &mut account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), ], ) .unwrap(); @@ -4394,10 +4394,7 @@ mod tests { let account_info_iter = &mut signer_accounts.iter_mut(); do_process_instruction( initialize_multisig2(&program_id, &multisig_key, &[&signer_keys[0]], 1).unwrap(), - vec![ - &mut multisig_account2, - &mut account_info_iter.next().unwrap(), - ], + vec![&mut multisig_account2, account_info_iter.next().unwrap()], ) .unwrap(); @@ -4414,17 +4411,17 @@ mod tests { vec![ &mut multisig_delegate_account, &mut rent_sysvar, - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), ], ) .unwrap(); @@ -4482,7 +4479,7 @@ mod tests { &mut mint_account, &mut account, &mut multisig_account, - &mut account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), ], ) .unwrap(); @@ -4503,7 +4500,7 @@ mod tests { &mut account, &mut multisig_delegate_account, &mut multisig_account, - &mut account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), ], ) .unwrap(); @@ -4524,7 +4521,7 @@ mod tests { &mut account, &mut account2_account, &mut multisig_account, - &mut account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), ], ) .unwrap(); @@ -4545,17 +4542,17 @@ mod tests { &mut account, &mut account2_account, &mut multisig_delegate_account, - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), ], ) .unwrap(); @@ -4576,7 +4573,7 @@ mod tests { &mut mint_account, &mut account2_account, &mut multisig_account, - &mut account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), ], ) .unwrap(); @@ -4597,7 +4594,7 @@ mod tests { &mut account, &mut mint_account, &mut multisig_account, - &mut account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), ], ) .unwrap(); @@ -4618,17 +4615,17 @@ mod tests { &mut account, &mut mint_account, &mut multisig_delegate_account, - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), - &mut account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), ], ) .unwrap(); @@ -4680,7 +4677,7 @@ mod tests { &mut mint2_account, &mut account3_account, &mut multisig_account, - &mut account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), ], ) .unwrap(); @@ -4698,7 +4695,7 @@ mod tests { &mut account3_account, &mut mint2_account, &mut multisig_account, - &mut account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), ], ) .unwrap(); @@ -4718,7 +4715,7 @@ mod tests { vec![ &mut mint_account, &mut multisig_account, - &mut account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), ], ) .unwrap(); @@ -4738,7 +4735,7 @@ mod tests { vec![ &mut account, &mut multisig_account, - &mut account_info_iter.next().unwrap(), + account_info_iter.next().unwrap(), ], ) .unwrap(); From 93151936acefb1a494643b3e1e382c8edb8bf3db Mon Sep 17 00:00:00 2001 From: Dmitri Makarov Date: Mon, 20 Dec 2021 15:29:36 -0800 Subject: [PATCH 142/248] Increase compute max units due to changes in handling of memory ops (#2653) --- program/tests/assert_instruction_count.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/program/tests/assert_instruction_count.rs b/program/tests/assert_instruction_count.rs index 858bd55..64f4e7c 100644 --- a/program/tests/assert_instruction_count.rs +++ b/program/tests/assert_instruction_count.rs @@ -22,7 +22,7 @@ const TRANSFER_AMOUNT: u64 = 1_000_000_000_000_000; #[tokio::test] async fn initialize_mint() { let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process)); - pt.set_bpf_compute_max_units(2_500); // last known 2252 + pt.set_bpf_compute_max_units(5_000); // last known 2252 let (mut banks_client, payer, recent_blockhash) = pt.start().await; let owner_key = Pubkey::new_unique(); @@ -60,7 +60,7 @@ async fn initialize_mint() { #[tokio::test] async fn initialize_account() { let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process)); - pt.set_bpf_compute_max_units(4_000); // last known 3284 + pt.set_bpf_compute_max_units(6_000); // last known 3284 let (mut banks_client, payer, recent_blockhash) = pt.start().await; let owner = Keypair::new(); @@ -112,7 +112,7 @@ async fn initialize_account() { #[tokio::test] async fn mint_to() { let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process)); - pt.set_bpf_compute_max_units(4_000); // last known 2668 + pt.set_bpf_compute_max_units(6_000); // last known 2668 let (mut banks_client, payer, recent_blockhash) = pt.start().await; let owner = Keypair::new(); @@ -161,7 +161,7 @@ async fn mint_to() { #[tokio::test] async fn transfer() { let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process)); - pt.set_bpf_compute_max_units(4_000); // last known 2972 + pt.set_bpf_compute_max_units(7_000); // last known 2972 let (mut banks_client, payer, recent_blockhash) = pt.start().await; let owner = Keypair::new(); @@ -229,7 +229,7 @@ async fn transfer() { #[tokio::test] async fn burn() { let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process)); - pt.set_bpf_compute_max_units(4_000); // last known 2655 + pt.set_bpf_compute_max_units(6_000); // last known 2655 let (mut banks_client, payer, recent_blockhash) = pt.start().await; let owner = Keypair::new(); From d4cfd9477d5627ed32570dc5eed77f2d9e7e543a Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Tue, 28 Dec 2021 23:02:47 -0500 Subject: [PATCH 143/248] Update SDK to 1.9.2, fix warnings (#2674) * Update SDK to 1.9.2, fix warnings * Upgrade honggfuzz * Use `get_latest_blockhash` correctly --- program/Cargo.toml | 6 +++--- program/tests/assert_instruction_count.rs | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 5b9a976..c62d27a 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -17,12 +17,12 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.4" -solana-program = "1.8.1" +solana-program = "1.9.2" thiserror = "1.0" [dev-dependencies] -solana-program-test = "1.8.1" -solana-sdk = "1.8.1" +solana-program-test = "1.9.2" +solana-sdk = "1.9.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/program/tests/assert_instruction_count.rs b/program/tests/assert_instruction_count.rs index 64f4e7c..27054fc 100644 --- a/program/tests/assert_instruction_count.rs +++ b/program/tests/assert_instruction_count.rs @@ -22,7 +22,7 @@ const TRANSFER_AMOUNT: u64 = 1_000_000_000_000_000; #[tokio::test] async fn initialize_mint() { let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process)); - pt.set_bpf_compute_max_units(5_000); // last known 2252 + pt.set_compute_max_units(5_000); // last known 2252 let (mut banks_client, payer, recent_blockhash) = pt.start().await; let owner_key = Pubkey::new_unique(); @@ -60,7 +60,7 @@ async fn initialize_mint() { #[tokio::test] async fn initialize_account() { let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process)); - pt.set_bpf_compute_max_units(6_000); // last known 3284 + pt.set_compute_max_units(6_000); // last known 3284 let (mut banks_client, payer, recent_blockhash) = pt.start().await; let owner = Keypair::new(); @@ -112,7 +112,7 @@ async fn initialize_account() { #[tokio::test] async fn mint_to() { let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process)); - pt.set_bpf_compute_max_units(6_000); // last known 2668 + pt.set_compute_max_units(6_000); // last known 2668 let (mut banks_client, payer, recent_blockhash) = pt.start().await; let owner = Keypair::new(); @@ -161,7 +161,7 @@ async fn mint_to() { #[tokio::test] async fn transfer() { let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process)); - pt.set_bpf_compute_max_units(7_000); // last known 2972 + pt.set_compute_max_units(7_000); // last known 2972 let (mut banks_client, payer, recent_blockhash) = pt.start().await; let owner = Keypair::new(); @@ -229,7 +229,7 @@ async fn transfer() { #[tokio::test] async fn burn() { let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process)); - pt.set_bpf_compute_max_units(6_000); // last known 2655 + pt.set_compute_max_units(6_000); // last known 2655 let (mut banks_client, payer, recent_blockhash) = pt.start().await; let owner = Keypair::new(); From dbd08364f1e8615e67c54004ba0d6018c8d2b6e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Piotrowski?= Date: Wed, 5 Jan 2022 23:11:31 +0100 Subject: [PATCH 144/248] token tests p1 (#2700) * token tests p1 * token tests - address review comments * fix cs --- program/src/state.rs | 75 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/program/src/state.rs b/program/src/state.rs index 15cc183..71f0d40 100644 --- a/program/src/state.rs +++ b/program/src/state.rs @@ -286,3 +286,78 @@ fn unpack_coption_u64(src: &[u8; 12]) -> Result, ProgramError> { _ => Err(ProgramError::InvalidAccountData), } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_mint_unpack_from_slice() { + let src: [u8; 82] = [0; 82]; + let mint = Mint::unpack_from_slice(&src).unwrap(); + assert!(!mint.is_initialized); + + let mut src: [u8; 82] = [0; 82]; + src[45] = 2; + let mint = Mint::unpack_from_slice(&src).unwrap_err(); + assert_eq!(mint, ProgramError::InvalidAccountData); + } + + #[test] + fn test_account_state() { + let account_state = AccountState::default(); + assert_eq!(account_state, AccountState::Uninitialized); + } + + #[test] + fn test_multisig_unpack_from_slice() { + let src: [u8; 355] = [0; 355]; + let multisig = Multisig::unpack_from_slice(&src).unwrap(); + assert_eq!(multisig.m, 0); + assert_eq!(multisig.n, 0); + assert!(!multisig.is_initialized); + + let mut src: [u8; 355] = [0; 355]; + src[0] = 1; + src[1] = 1; + src[2] = 1; + let multisig = Multisig::unpack_from_slice(&src).unwrap(); + assert_eq!(multisig.m, 1); + assert_eq!(multisig.n, 1); + assert!(multisig.is_initialized); + + let mut src: [u8; 355] = [0; 355]; + src[2] = 2; + let multisig = Multisig::unpack_from_slice(&src).unwrap_err(); + assert_eq!(multisig, ProgramError::InvalidAccountData); + } + + #[test] + fn test_unpack_coption_key() { + let src: [u8; 36] = [0; 36]; + let result = unpack_coption_key(&src).unwrap(); + assert_eq!(result, COption::None); + + let mut src: [u8; 36] = [0; 36]; + src[1] = 1; + let result = unpack_coption_key(&src).unwrap_err(); + assert_eq!(result, ProgramError::InvalidAccountData); + } + + #[test] + fn test_unpack_coption_u64() { + let src: [u8; 12] = [0; 12]; + let result = unpack_coption_u64(&src).unwrap(); + assert_eq!(result, COption::None); + + let mut src: [u8; 12] = [0; 12]; + src[0] = 1; + let result = unpack_coption_u64(&src).unwrap(); + assert_eq!(result, COption::Some(0)); + + let mut src: [u8; 12] = [0; 12]; + src[1] = 1; + let result = unpack_coption_u64(&src).unwrap_err(); + assert_eq!(result, ProgramError::InvalidAccountData); + } +} From f047aa8740149a68b0976e3c3675b1fbcfa7b233 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Fri, 21 Jan 2022 01:03:00 +0100 Subject: [PATCH 145/248] token: Zeroize data on account close (#2763) --- program/src/processor.rs | 343 +++++++++++++++++----- program/tests/assert_instruction_count.rs | 48 +++ 2 files changed, 320 insertions(+), 71 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index 5ab0002..d0045a4 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -12,9 +12,10 @@ use solana_program::{ entrypoint::ProgramResult, msg, program_error::{PrintProgramError, ProgramError}, + program_memory::{sol_memcmp, sol_memset}, program_option::COption, program_pack::{IsInitialized, Pack}, - pubkey::Pubkey, + pubkey::{Pubkey, PUBKEY_BYTES}, sysvar::{rent::Rent, Sysvar}, }; @@ -77,6 +78,7 @@ impl Processor { } fn _process_initialize_account( + program_id: &Pubkey, accounts: &[AccountInfo], owner: Option<&Pubkey>, rent_sysvar_account: bool, @@ -105,7 +107,9 @@ impl Processor { return Err(TokenError::NotRentExempt.into()); } - if *mint_info.key != crate::native_mint::id() { + let is_native_mint = Self::cmp_pubkeys(mint_info.key, &crate::native_mint::id()); + if !is_native_mint { + Self::check_account_owner(program_id, mint_info)?; let _ = Mint::unpack(&mint_info.data.borrow_mut()) .map_err(|_| Into::::into(TokenError::InvalidMint))?; } @@ -115,7 +119,7 @@ impl Processor { account.delegate = COption::None; account.delegated_amount = 0; account.state = AccountState::Initialized; - if *mint_info.key == crate::native_mint::id() { + if is_native_mint { let rent_exempt_reserve = rent.minimum_balance(new_account_info_data_len); account.is_native = COption::Some(rent_exempt_reserve); account.amount = new_account_info @@ -133,18 +137,29 @@ impl Processor { } /// Processes an [InitializeAccount](enum.TokenInstruction.html) instruction. - pub fn process_initialize_account(accounts: &[AccountInfo]) -> ProgramResult { - Self::_process_initialize_account(accounts, None, true) + pub fn process_initialize_account( + program_id: &Pubkey, + accounts: &[AccountInfo], + ) -> ProgramResult { + Self::_process_initialize_account(program_id, accounts, None, true) } /// Processes an [InitializeAccount2](enum.TokenInstruction.html) instruction. - pub fn process_initialize_account2(accounts: &[AccountInfo], owner: Pubkey) -> ProgramResult { - Self::_process_initialize_account(accounts, Some(&owner), true) + pub fn process_initialize_account2( + program_id: &Pubkey, + accounts: &[AccountInfo], + owner: Pubkey, + ) -> ProgramResult { + Self::_process_initialize_account(program_id, accounts, Some(&owner), true) } /// Processes an [InitializeAccount3](enum.TokenInstruction.html) instruction. - pub fn process_initialize_account3(accounts: &[AccountInfo], owner: Pubkey) -> ProgramResult { - Self::_process_initialize_account(accounts, Some(&owner), false) + pub fn process_initialize_account3( + program_id: &Pubkey, + accounts: &[AccountInfo], + owner: Pubkey, + ) -> ProgramResult { + Self::_process_initialize_account(program_id, accounts, Some(&owner), false) } fn _process_initialize_multisig( @@ -228,12 +243,12 @@ impl Processor { if source_account.amount < amount { return Err(TokenError::InsufficientFunds.into()); } - if source_account.mint != dest_account.mint { + if !Self::cmp_pubkeys(&source_account.mint, &dest_account.mint) { return Err(TokenError::MintMismatch.into()); } if let Some((mint_info, expected_decimals)) = expected_mint_info { - if source_account.mint != *mint_info.key { + if !Self::cmp_pubkeys(mint_info.key, &source_account.mint) { return Err(TokenError::MintMismatch.into()); } @@ -243,10 +258,10 @@ impl Processor { } } - let self_transfer = source_account_info.key == dest_account_info.key; + let self_transfer = Self::cmp_pubkeys(source_account_info.key, dest_account_info.key); match source_account.delegate { - COption::Some(ref delegate) if authority_info.key == delegate => { + COption::Some(ref delegate) if Self::cmp_pubkeys(authority_info.key, delegate) => { Self::validate_owner( program_id, delegate, @@ -274,6 +289,11 @@ impl Processor { )?, }; + if self_transfer || amount == 0 { + Self::check_account_owner(program_id, source_account_info)?; + Self::check_account_owner(program_id, dest_account_info)?; + } + // This check MUST occur just before the amounts are manipulated // to ensure self-transfers are fully validated if self_transfer { @@ -333,7 +353,7 @@ impl Processor { } if let Some((mint_info, expected_decimals)) = expected_mint_info { - if source_account.mint != *mint_info.key { + if !Self::cmp_pubkeys(mint_info.key, &source_account.mint) { return Err(TokenError::MintMismatch.into()); } @@ -504,7 +524,7 @@ impl Processor { if dest_account.is_native() { return Err(TokenError::NativeNotSupported.into()); } - if mint_info.key != &dest_account.mint { + if !Self::cmp_pubkeys(mint_info.key, &dest_account.mint) { return Err(TokenError::MintMismatch.into()); } @@ -525,6 +545,11 @@ impl Processor { COption::None => return Err(TokenError::FixedSupply.into()), } + if amount == 0 { + Self::check_account_owner(program_id, mint_info)?; + Self::check_account_owner(program_id, dest_account_info)?; + } + dest_account.amount = dest_account .amount .checked_add(amount) @@ -566,7 +591,7 @@ impl Processor { if source_account.amount < amount { return Err(TokenError::InsufficientFunds.into()); } - if mint_info.key != &source_account.mint { + if !Self::cmp_pubkeys(mint_info.key, &source_account.mint) { return Err(TokenError::MintMismatch.into()); } @@ -577,7 +602,7 @@ impl Processor { } match source_account.delegate { - COption::Some(ref delegate) if authority_info.key == delegate => { + COption::Some(ref delegate) if Self::cmp_pubkeys(authority_info.key, delegate) => { Self::validate_owner( program_id, delegate, @@ -604,6 +629,11 @@ impl Processor { )?, } + if amount == 0 { + Self::check_account_owner(program_id, source_account_info)?; + Self::check_account_owner(program_id, mint_info)?; + } + source_account.amount = source_account .amount .checked_sub(amount) @@ -626,7 +656,11 @@ impl Processor { let dest_account_info = next_account_info(account_info_iter)?; let authority_info = next_account_info(account_info_iter)?; - let mut source_account = Account::unpack(&source_account_info.data.borrow())?; + if Self::cmp_pubkeys(source_account_info.key, dest_account_info.key) { + return Err(ProgramError::InvalidAccountData); + } + + let source_account = Account::unpack(&source_account_info.data.borrow())?; if !source_account.is_native() && source_account.amount != 0 { return Err(TokenError::NonNativeHasBalance.into()); } @@ -647,9 +681,8 @@ impl Processor { .ok_or(TokenError::Overflow)?; **source_account_info.lamports.borrow_mut() = 0; - source_account.amount = 0; - Account::pack(source_account, &mut source_account_info.data.borrow_mut())?; + sol_memset(*source_account_info.data.borrow_mut(), 0, Account::LEN); Ok(()) } @@ -673,7 +706,7 @@ impl Processor { if source_account.is_native() { return Err(TokenError::NativeNotSupported.into()); } - if mint_info.key != &source_account.mint { + if !Self::cmp_pubkeys(mint_info.key, &source_account.mint) { return Err(TokenError::MintMismatch.into()); } @@ -703,10 +736,8 @@ impl Processor { pub fn process_sync_native(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let native_account_info = next_account_info(account_info_iter)?; + Self::check_account_owner(program_id, native_account_info)?; - if native_account_info.owner != program_id { - return Err(ProgramError::IncorrectProgramId); - } let mut native_account = Account::unpack(&native_account_info.data.borrow())?; if let COption::Some(rent_exempt_reserve) = native_account.is_native { @@ -749,15 +780,15 @@ impl Processor { } TokenInstruction::InitializeAccount => { msg!("Instruction: InitializeAccount"); - Self::process_initialize_account(accounts) + Self::process_initialize_account(program_id, accounts) } TokenInstruction::InitializeAccount2 { owner } => { msg!("Instruction: InitializeAccount2"); - Self::process_initialize_account2(accounts, owner) + Self::process_initialize_account2(program_id, accounts, owner) } TokenInstruction::InitializeAccount3 { owner } => { msg!("Instruction: InitializeAccount3"); - Self::process_initialize_account3(accounts, owner) + Self::process_initialize_account3(program_id, accounts, owner) } TokenInstruction::InitializeMultisig { m } => { msg!("Instruction: InitializeMultisig"); @@ -829,6 +860,21 @@ impl Processor { } } + /// Checks that the account is owned by the expected program + pub fn check_account_owner(program_id: &Pubkey, account_info: &AccountInfo) -> ProgramResult { + if !Self::cmp_pubkeys(program_id, account_info.owner) { + Err(ProgramError::IncorrectProgramId) + } else { + Ok(()) + } + } + + /// Checks two pubkeys for equality in a computationally cheap way using + /// `sol_memcmp` + pub fn cmp_pubkeys(a: &Pubkey, b: &Pubkey) -> bool { + sol_memcmp(a.as_ref(), b.as_ref(), PUBKEY_BYTES) == 0 + } + /// Validates owner(s) are present pub fn validate_owner( program_id: &Pubkey, @@ -836,10 +882,10 @@ impl Processor { owner_account_info: &AccountInfo, signers: &[AccountInfo], ) -> ProgramResult { - if expected_owner != owner_account_info.key { + if !Self::cmp_pubkeys(expected_owner, owner_account_info.key) { return Err(TokenError::OwnerMismatch.into()); } - if program_id == owner_account_info.owner + if Self::cmp_pubkeys(program_id, owner_account_info.owner) && owner_account_info.data_len() == Multisig::get_packed_len() { let multisig = Multisig::unpack(&owner_account_info.data.borrow())?; @@ -847,7 +893,7 @@ impl Processor { let mut matched = [false; MAX_SIGNERS]; for signer in signers.iter() { for (position, key) in multisig.signers[0..multisig.n as usize].iter().enumerate() { - if key == signer.key && !matched[position] { + if Self::cmp_pubkeys(key, signer.key) && !matched[position] { if !signer.is_signer { return Err(ProgramError::MissingRequiredSignature); } @@ -1279,6 +1325,23 @@ mod tests { ) .unwrap(); + // mint not owned by program + let not_program_id = Pubkey::new_unique(); + mint_account.owner = not_program_id; + assert_eq!( + Err(ProgramError::IncorrectProgramId), + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar + ], + ) + ); + mint_account.owner = program_id; + // create account do_process_instruction( initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), @@ -1782,6 +1845,38 @@ mod tests { ) ); + // account not owned by program + let not_program_id = Pubkey::new_unique(); + account_account.owner = not_program_id; + assert_eq!( + Err(ProgramError::IncorrectProgramId), + do_process_instruction( + transfer(&program_id, &account_key, &account2_key, &owner_key, &[], 0,).unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner2_account, + ], + ) + ); + account_account.owner = program_id; + + // account 2 not owned by program + let not_program_id = Pubkey::new_unique(); + account2_account.owner = not_program_id; + assert_eq!( + Err(ProgramError::IncorrectProgramId), + do_process_instruction( + transfer(&program_id, &account_key, &account2_key, &owner_key, &[], 0,).unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner2_account, + ], + ) + ); + account2_account.owner = program_id; + // transfer do_process_instruction( transfer( @@ -3819,6 +3914,30 @@ mod tests { ) ); + // mint not owned by program + let not_program_id = Pubkey::new_unique(); + mint_account.owner = not_program_id; + assert_eq!( + Err(ProgramError::IncorrectProgramId), + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 0).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + ); + mint_account.owner = program_id; + + // account not owned by program + let not_program_id = Pubkey::new_unique(); + account_account.owner = not_program_id; + assert_eq!( + Err(ProgramError::IncorrectProgramId), + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 0).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + ); + account_account.owner = program_id; + // uninitialized destination account assert_eq!( Err(ProgramError::UninitializedAccount), @@ -4199,6 +4318,30 @@ mod tests { ) ); + // account not owned by program + let not_program_id = Pubkey::new_unique(); + account_account.owner = not_program_id; + assert_eq!( + Err(ProgramError::IncorrectProgramId), + do_process_instruction( + burn(&program_id, &account_key, &mint_key, &owner_key, &[], 0).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + ); + account_account.owner = program_id; + + // mint not owned by program + let not_program_id = Pubkey::new_unique(); + mint_account.owner = not_program_id; + assert_eq!( + Err(ProgramError::IncorrectProgramId), + do_process_instruction( + burn(&program_id, &account_key, &mint_key, &owner_key, &[], 0).unwrap(), + vec![&mut account_account, &mut mint_account, &mut owner_account], + ) + ); + mint_account.owner = program_id; + // mint mismatch assert_eq!( Err(TokenError::MintMismatch.into()), @@ -4902,22 +5045,8 @@ mod tests { } #[test] - fn test_close_account_dups() { + fn test_owner_close_account_dups() { let program_id = crate::id(); - let account1_key = Pubkey::new_unique(); - let mut account1_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account1_info: AccountInfo = (&account1_key, true, &mut account1_account).into(); - let account2_key = Pubkey::new_unique(); - let mut account2_account = SolanaAccount::new( - account_minimum_balance(), - Account::get_packed_len(), - &program_id, - ); - let account2_info: AccountInfo = (&account2_key, true, &mut account2_account).into(); let owner_key = Pubkey::new_unique(); let mint_key = Pubkey::new_unique(); let mut mint_account = @@ -4934,13 +5063,29 @@ mod tests { ) .unwrap(); + let to_close_key = Pubkey::new_unique(); + let mut to_close_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let to_close_account_info: AccountInfo = + (&to_close_key, true, &mut to_close_account).into(); + let destination_account_key = Pubkey::new_unique(); + let mut destination_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let destination_account_info: AccountInfo = + (&destination_account_key, true, &mut destination_account).into(); // create account do_process_instruction_dups( - initialize_account(&program_id, &account1_key, &mint_key, &account1_key).unwrap(), + initialize_account(&program_id, &to_close_key, &mint_key, &to_close_key).unwrap(), vec![ - account1_info.clone(), + to_close_account_info.clone(), mint_info.clone(), - account1_info.clone(), + to_close_account_info.clone(), rent_info.clone(), ], ) @@ -4950,41 +5095,89 @@ mod tests { do_process_instruction_dups( close_account( &program_id, - &account1_key, - &account2_key, - &account1_key, + &to_close_key, + &destination_account_key, + &to_close_key, &[], ) .unwrap(), vec![ - account1_info.clone(), - account2_info.clone(), - account1_info.clone(), + to_close_account_info.clone(), + destination_account_info.clone(), + to_close_account_info.clone(), ], ) .unwrap(); + assert_eq!(*to_close_account_info.data.borrow(), &[0u8; Account::LEN]); + } - // source-close-authority close - let mut account = Account::unpack_unchecked(&account1_info.data.borrow()).unwrap(); - account.close_authority = COption::Some(account1_key); + #[test] + fn test_close_authority_close_account_dups() { + let program_id = crate::id(); + let owner_key = Pubkey::new_unique(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mint_info: AccountInfo = (&mint_key, false, &mut mint_account).into(); + let rent_key = rent::id(); + let mut rent_sysvar = rent_sysvar(); + let rent_info: AccountInfo = (&rent_key, false, &mut rent_sysvar).into(); + + // create mint + do_process_instruction_dups( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![mint_info.clone(), rent_info.clone()], + ) + .unwrap(); + + let to_close_key = Pubkey::new_unique(); + let mut to_close_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let to_close_account_info: AccountInfo = + (&to_close_key, true, &mut to_close_account).into(); + let destination_account_key = Pubkey::new_unique(); + let mut destination_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let destination_account_info: AccountInfo = + (&destination_account_key, true, &mut destination_account).into(); + // create account + do_process_instruction_dups( + initialize_account(&program_id, &to_close_key, &mint_key, &to_close_key).unwrap(), + vec![ + to_close_account_info.clone(), + mint_info.clone(), + to_close_account_info.clone(), + rent_info.clone(), + ], + ) + .unwrap(); + let mut account = Account::unpack_unchecked(&to_close_account_info.data.borrow()).unwrap(); + account.close_authority = COption::Some(to_close_key); account.owner = owner_key; - Account::pack(account, &mut account1_info.data.borrow_mut()).unwrap(); + Account::pack(account, &mut to_close_account_info.data.borrow_mut()).unwrap(); do_process_instruction_dups( close_account( &program_id, - &account1_key, - &account2_key, - &account1_key, + &to_close_key, + &destination_account_key, + &to_close_key, &[], ) .unwrap(), vec![ - account1_info.clone(), - account2_info.clone(), - account1_info.clone(), + to_close_account_info.clone(), + destination_account_info.clone(), + to_close_account_info.clone(), ], ) .unwrap(); + assert_eq!(*to_close_account_info.data.borrow(), &[0u8; Account::LEN]); } #[test] @@ -5206,10 +5399,7 @@ mod tests { ], ) .unwrap(); - let account = Account::unpack_unchecked(&account2_account.data).unwrap(); - assert!(account.is_native()); - assert_eq!(account_account.lamports, 0); - assert_eq!(account.amount, 0); + assert_eq!(account2_account.data, [0u8; Account::LEN]); assert_eq!( account3_account.lamports, 3 * account_minimum_balance() + 2 + 42 @@ -5427,9 +5617,7 @@ mod tests { .unwrap(); assert_eq!(account_account.lamports, 0); assert_eq!(account3_account.lamports, 2 * account_minimum_balance()); - let account = Account::unpack_unchecked(&account_account.data).unwrap(); - assert!(account.is_native()); - assert_eq!(account.amount, 0); + assert_eq!(account_account.data, [0u8; Account::LEN]); } #[test] @@ -6116,6 +6304,19 @@ mod tests { ], ) .unwrap(); + + // fail sync, not owned by program + let not_program_id = Pubkey::new_unique(); + native_account.owner = not_program_id; + assert_eq!( + Err(ProgramError::IncorrectProgramId), + do_process_instruction( + sync_native(&program_id, &native_account_key,).unwrap(), + vec![&mut native_account], + ) + ); + native_account.owner = program_id; + let account = Account::unpack_unchecked(&native_account.data).unwrap(); assert!(account.is_native()); assert_eq!(account.amount, lamports); diff --git a/program/tests/assert_instruction_count.rs b/program/tests/assert_instruction_count.rs index 27054fc..d663c54 100644 --- a/program/tests/assert_instruction_count.rs +++ b/program/tests/assert_instruction_count.rs @@ -282,3 +282,51 @@ async fn burn() { .await .unwrap(); } + +#[tokio::test] +async fn close_account() { + let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process)); + pt.set_compute_max_units(6_000); // last known 1783 + let (mut banks_client, payer, recent_blockhash) = pt.start().await; + + let owner = Keypair::new(); + let mint = Keypair::new(); + let account = Keypair::new(); + let decimals = 9; + + action::create_mint( + &mut banks_client, + &payer, + recent_blockhash, + &mint, + &owner.pubkey(), + decimals, + ) + .await + .unwrap(); + action::create_account( + &mut banks_client, + &payer, + recent_blockhash, + &account, + &mint.pubkey(), + &owner.pubkey(), + ) + .await + .unwrap(); + + let transaction = Transaction::new_signed_with_payer( + &[instruction::close_account( + &id(), + &account.pubkey(), + &owner.pubkey(), + &owner.pubkey(), + &[], + ) + .unwrap()], + Some(&payer.pubkey()), + &[&payer, &owner], + recent_blockhash, + ); + banks_client.process_transaction(transaction).await.unwrap(); +} From a85c96acccc670b298c51924283b42fd65884cf3 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Fri, 21 Jan 2022 01:47:02 +0100 Subject: [PATCH 146/248] token: Bump version to 3.3.0 (#2765) --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index c62d27a..3dd1210 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-token" -version = "3.2.0" +version = "3.3.0" description = "Solana Program Library Token" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" From 6cb24c6fdf7f5785b72ccc53e92e0da309720b1f Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Fri, 21 Jan 2022 22:52:10 -0700 Subject: [PATCH 147/248] Bump solana crates to v1.9.5 (#2780) * Bump solana crates to v1.9.5 * Update sol_set_return_data type signature and un-ignore test --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 3dd1210..76ee9f7 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -17,12 +17,12 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.4" -solana-program = "1.9.2" +solana-program = "1.9.5" thiserror = "1.0" [dev-dependencies] -solana-program-test = "1.9.2" -solana-sdk = "1.9.2" +solana-program-test = "1.9.5" +solana-sdk = "1.9.5" [lib] crate-type = ["cdylib", "lib"] From 1a8987c57bca904a9e7225827bc332b74abf5635 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Tue, 15 Feb 2022 14:20:14 +0100 Subject: [PATCH 148/248] token: Support both token programs in associated-token-account (#2904) * token: Support both token programs in associated-token-account * Rename spl-token -> spl-token-2022, spl-token-legacy -> spl-token * Use spl-token by default * Check mint in get_account_data_len * Add checks for either token program * Rebase fix * Update version comments in tests --- program/Cargo.toml | 1 + program/src/instruction.rs | 74 +++++++++++++++++++++ program/src/processor.rs | 132 +++++++++++++++++++++++++++++++++++++ 3 files changed, 207 insertions(+) diff --git a/program/Cargo.toml b/program/Cargo.toml index 76ee9f7..ed73a07 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -21,6 +21,7 @@ solana-program = "1.9.5" thiserror = "1.0" [dev-dependencies] +lazy_static = "1.4.0" solana-program-test = "1.9.5" solana-sdk = "1.9.5" diff --git a/program/src/instruction.rs b/program/src/instruction.rs index c5936cf..d548a99 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -409,6 +409,31 @@ pub enum TokenInstruction { /// The freeze authority/multisignature of the mint. freeze_authority: COption, }, + /// Gets the required size of an account for the given mint as a little-endian + /// `u64`. + /// + /// Return data can be fetched using `sol_get_return_data` and deserializing + /// the return data as a little-endian `u64`. + /// + /// Accounts expected by this instruction: + /// + /// 0. `[]` The mint to calculate for + GetAccountDataSize, // typically, there's also data, but this program ignores it + /// Initialize the Immutable Owner extension for the given token account + /// + /// Fails if the account has already been initialized, so must be called before + /// `InitializeAccount`. + /// + /// No-ops in this version of the program, but is included for compatibility + /// with the Associated Token Account program. + /// + /// Accounts expected by this instruction: + /// + /// 0. `[writable]` The account to initialize. + /// + /// Data expected by this instruction: + /// None + InitializeImmutableOwner, } impl TokenInstruction { /// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html). @@ -529,6 +554,8 @@ impl TokenInstruction { decimals, } } + 21 => Self::GetAccountDataSize, + 22 => Self::InitializeImmutableOwner, _ => return Err(TokenError::InvalidInstruction.into()), }) } @@ -625,6 +652,12 @@ impl TokenInstruction { buf.extend_from_slice(mint_authority.as_ref()); Self::pack_pubkey_option(freeze_authority, &mut buf); } + &Self::GetAccountDataSize => { + buf.push(21); + } + &Self::InitializeImmutableOwner => { + buf.push(22); + } }; buf } @@ -1298,6 +1331,33 @@ pub fn sync_native( }) } +/// Creates a `GetAccountDataSize` instruction +pub fn get_account_data_size( + token_program_id: &Pubkey, + mint_pubkey: &Pubkey, +) -> Result { + check_program_account(token_program_id)?; + + Ok(Instruction { + program_id: *token_program_id, + accounts: vec![AccountMeta::new(*mint_pubkey, false)], + data: TokenInstruction::GetAccountDataSize.pack(), + }) +} + +/// Creates a `InitializeImmutableOwner` instruction +pub fn initialize_immutable_owner( + token_program_id: &Pubkey, + account_pubkey: &Pubkey, +) -> Result { + check_program_account(token_program_id)?; + Ok(Instruction { + program_id: *token_program_id, + accounts: vec![AccountMeta::new(*account_pubkey, false)], + data: TokenInstruction::InitializeImmutableOwner.pack(), + }) +} + /// Utility function that checks index is between MIN_SIGNERS and MAX_SIGNERS pub fn is_valid_signer_index(index: usize) -> bool { (MIN_SIGNERS..=MAX_SIGNERS).contains(&index) @@ -1518,5 +1578,19 @@ mod test { assert_eq!(packed, expect); let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); + + let check = TokenInstruction::GetAccountDataSize; + let packed = check.pack(); + let expect = vec![21u8]; + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::InitializeImmutableOwner; + let packed = check.pack(); + let expect = vec![22u8]; + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); } } diff --git a/program/src/processor.rs b/program/src/processor.rs index d0045a4..f404d4e 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -11,6 +11,7 @@ use solana_program::{ decode_error::DecodeError, entrypoint::ProgramResult, msg, + program::set_return_data, program_error::{PrintProgramError, ProgramError}, program_memory::{sol_memcmp, sol_memset}, program_option::COption, @@ -757,6 +758,32 @@ impl Processor { Ok(()) } + /// Processes a [GetAccountDataSize](enum.TokenInstruction.html) instruction + pub fn process_get_account_data_size( + program_id: &Pubkey, + accounts: &[AccountInfo], + ) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + // make sure the mint is valid + let mint_info = next_account_info(account_info_iter)?; + Self::check_account_owner(program_id, mint_info)?; + let _ = Mint::unpack(&mint_info.data.borrow())?; + set_return_data(&Account::LEN.to_le_bytes()); + Ok(()) + } + + /// Processes an [InitializeImmutableOwner](enum.TokenInstruction.html) instruction + pub fn process_initialize_immutable_owner(accounts: &[AccountInfo]) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let token_account_info = next_account_info(account_info_iter)?; + let account = Account::unpack_unchecked(&token_account_info.data.borrow())?; + if account.is_initialized() { + return Err(TokenError::AlreadyInUse.into()); + } + msg!("Please upgrade to SPL Token 2022 for immutable owner support"); + Ok(()) + } + /// Processes an [Instruction](enum.Instruction.html). pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult { let instruction = TokenInstruction::unpack(input)?; @@ -857,6 +884,14 @@ impl Processor { msg!("Instruction: SyncNative"); Self::process_sync_native(program_id, accounts) } + TokenInstruction::GetAccountDataSize => { + msg!("Instruction: GetAccountDataSize"); + Self::process_get_account_data_size(program_id, accounts) + } + TokenInstruction::InitializeImmutableOwner => { + msg!("Instruction: InitializeImmutableOwner"); + Self::process_initialize_immutable_owner(accounts) + } } } @@ -968,6 +1003,15 @@ mod tests { use solana_sdk::account::{ create_account_for_test, create_is_signer_account_infos, Account as SolanaAccount, }; + use std::sync::{Arc, RwLock}; + + lazy_static::lazy_static! { + static ref EXPECTED_DATA: Arc>> = Arc::new(RwLock::new(Vec::new())); + } + + fn set_expected_data(expected_data: Vec) { + *EXPECTED_DATA.write().unwrap() = expected_data; + } struct SyscallStubs {} impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs { @@ -1001,6 +1045,10 @@ mod tests { } solana_program::entrypoint::SUCCESS } + + fn sol_set_return_data(&self, data: &[u8]) { + assert_eq!(&*EXPECTED_DATA.write().unwrap(), data) + } } fn do_process_instruction( @@ -6355,4 +6403,88 @@ mod tests { ) ); } + + #[test] + fn test_get_account_data_size() { + // see integration tests for return-data validity + let program_id = crate::id(); + let owner_key = Pubkey::new_unique(); + let mut rent_sysvar = rent_sysvar(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mint_key = Pubkey::new_unique(); + // fail if an invalid mint is passed in + assert_eq!( + Err(ProgramError::UninitializedAccount), + do_process_instruction( + get_account_data_size(&program_id, &mint_key).unwrap(), + vec![&mut mint_account], + ) + ); + + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + set_expected_data(Account::LEN.to_le_bytes().to_vec()); + do_process_instruction( + get_account_data_size(&program_id, &mint_key).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + } + + #[test] + fn test_initialize_immutable_owner() { + let program_id = crate::id(); + let account_key = Pubkey::new_unique(); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mut rent_sysvar = rent_sysvar(); + + // create mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + // success initialize immutable + do_process_instruction( + initialize_immutable_owner(&program_id, &account_key).unwrap(), + vec![&mut account_account], + ) + .unwrap(); + + // create account + do_process_instruction( + initialize_account(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut owner_account, + &mut rent_sysvar, + ], + ) + .unwrap(); + + // fail post-init + assert_eq!( + Err(TokenError::AlreadyInUse.into()), + do_process_instruction( + initialize_immutable_owner(&program_id, &account_key).unwrap(), + vec![&mut account_account], + ) + ); + } } From 03a37d7c76fd3f8534a6b5f435ca389346cbe800 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Tue, 15 Feb 2022 17:27:28 -0700 Subject: [PATCH 149/248] Initialize close_authority explicitly (#2919) --- program/src/processor.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/program/src/processor.rs b/program/src/processor.rs index f404d4e..eeee445 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -117,6 +117,7 @@ impl Processor { account.mint = *mint_info.key; account.owner = *owner; + account.close_authority = COption::None; account.delegate = COption::None; account.delegated_amount = 0; account.state = AccountState::Initialized; From 6e07a16c1997b0c7d0486210e14ab322424e286d Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Thu, 17 Feb 2022 16:25:57 -0700 Subject: [PATCH 150/248] Add token Amount/UiAmount conversion Instructions (#2928) * Add Amount/UiAmount conversion Instructions to spl-token * Use invalid-mint error in spl_token get_account_data_size * Add Amount/UiAmount conversion Instructions to spl-token-2022 --- program/Cargo.toml | 1 + program/src/instruction.rs | 99 +++++++++++++++- program/src/lib.rs | 49 ++++++++ program/src/processor.rs | 228 ++++++++++++++++++++++++++++++++++++- 4 files changed, 372 insertions(+), 5 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index ed73a07..2fe11c3 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -22,6 +22,7 @@ thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" +serial_test = "0.5.1" solana-program-test = "1.9.5" solana-sdk = "1.9.5" diff --git a/program/src/instruction.rs b/program/src/instruction.rs index d548a99..f3b22f1 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -19,7 +19,7 @@ pub const MAX_SIGNERS: usize = 11; /// Instructions supported by the token program. #[repr(C)] #[derive(Clone, Debug, PartialEq)] -pub enum TokenInstruction { +pub enum TokenInstruction<'a> { /// Initializes a new mint and optionally deposits all the newly minted /// tokens in an account. /// @@ -434,10 +434,38 @@ pub enum TokenInstruction { /// Data expected by this instruction: /// None InitializeImmutableOwner, + /// Convert an Amount of tokens to a UiAmount `string`, using the given mint. + /// In this version of the program, the mint can only specify the number of decimals. + /// + /// Fails on an invalid mint. + /// + /// Return data can be fetched using `sol_get_return_data` and deserialized with + /// `String::from_utf8`. + /// + /// Accounts expected by this instruction: + /// + /// 0. `[]` The mint to calculate for + AmountToUiAmount { + /// The amount of tokens to reformat. + amount: u64, + }, + /// Convert a UiAmount of tokens to a little-endian `u64` raw Amount, using the given mint. + /// In this version of the program, the mint can only specify the number of decimals. + /// + /// Return data can be fetched using `sol_get_return_data` and deserializing + /// the return data as a little-endian `u64`. + /// + /// Accounts expected by this instruction: + /// + /// 0. `[]` The mint to calculate for + UiAmountToAmount { + /// The ui_amount of tokens to reformat. + ui_amount: &'a str, + }, } -impl TokenInstruction { +impl<'a> TokenInstruction<'a> { /// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html). - pub fn unpack(input: &[u8]) -> Result { + pub fn unpack(input: &'a [u8]) -> Result { use TokenError::InvalidInstruction; let (&tag, rest) = input.split_first().ok_or(InvalidInstruction)?; @@ -556,6 +584,19 @@ impl TokenInstruction { } 21 => Self::GetAccountDataSize, 22 => Self::InitializeImmutableOwner, + 23 => { + let (amount, _rest) = rest.split_at(8); + let amount = amount + .try_into() + .ok() + .map(u64::from_le_bytes) + .ok_or(InvalidInstruction)?; + Self::AmountToUiAmount { amount } + } + 24 => { + let ui_amount = std::str::from_utf8(rest).map_err(|_| InvalidInstruction)?; + Self::UiAmountToAmount { ui_amount } + } _ => return Err(TokenError::InvalidInstruction.into()), }) } @@ -658,6 +699,14 @@ impl TokenInstruction { &Self::InitializeImmutableOwner => { buf.push(22); } + &Self::AmountToUiAmount { amount } => { + buf.push(23); + buf.extend_from_slice(&amount.to_le_bytes()); + } + Self::UiAmountToAmount { ui_amount } => { + buf.push(24); + buf.extend_from_slice(ui_amount.as_bytes()); + } }; buf } @@ -1358,6 +1407,36 @@ pub fn initialize_immutable_owner( }) } +/// Creates an `AmountToUiAmount` instruction +pub fn amount_to_ui_amount( + token_program_id: &Pubkey, + mint_pubkey: &Pubkey, + amount: u64, +) -> Result { + check_program_account(token_program_id)?; + + Ok(Instruction { + program_id: *token_program_id, + accounts: vec![AccountMeta::new_readonly(*mint_pubkey, false)], + data: TokenInstruction::AmountToUiAmount { amount }.pack(), + }) +} + +/// Creates a `UiAmountToAmount` instruction +pub fn ui_amount_to_amount( + token_program_id: &Pubkey, + mint_pubkey: &Pubkey, + ui_amount: &str, +) -> Result { + check_program_account(token_program_id)?; + + Ok(Instruction { + program_id: *token_program_id, + accounts: vec![AccountMeta::new_readonly(*mint_pubkey, false)], + data: TokenInstruction::UiAmountToAmount { ui_amount }.pack(), + }) +} + /// Utility function that checks index is between MIN_SIGNERS and MAX_SIGNERS pub fn is_valid_signer_index(index: usize) -> bool { (MIN_SIGNERS..=MAX_SIGNERS).contains(&index) @@ -1592,5 +1671,19 @@ mod test { assert_eq!(packed, expect); let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); + + let check = TokenInstruction::AmountToUiAmount { amount: 42 }; + let packed = check.pack(); + let expect = vec![23u8, 42, 0, 0, 0, 0, 0, 0, 0]; + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); + + let check = TokenInstruction::UiAmountToAmount { ui_amount: "0.42" }; + let packed = check.pack(); + let expect = vec![24u8, 48, 46, 52, 50]; + assert_eq!(packed, expect); + let unpacked = TokenInstruction::unpack(&expect).unwrap(); + assert_eq!(unpacked, check); } } diff --git a/program/src/lib.rs b/program/src/lib.rs index 4dda68d..2252ac1 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -27,6 +27,55 @@ pub fn amount_to_ui_amount(amount: u64, decimals: u8) -> f64 { amount as f64 / 10_usize.pow(decimals as u32) as f64 } +/// Convert a raw amount to its UI representation (using the decimals field defined in its mint) +pub fn amount_to_ui_amount_string(amount: u64, decimals: u8) -> String { + let decimals = decimals as usize; + if decimals > 0 { + // Left-pad zeros to decimals + 1, so we at least have an integer zero + let mut s = format!("{:01$}", amount, decimals + 1); + // Add the decimal point (Sorry, "," locales!) + s.insert(s.len() - decimals, '.'); + s + } else { + amount.to_string() + } +} + +/// Convert a raw amount to its UI representation using the given decimals field +/// Excess zeroes or unneeded decimal point are trimmed. +pub fn amount_to_ui_amount_string_trimmed(amount: u64, decimals: u8) -> String { + let mut s = amount_to_ui_amount_string(amount, decimals); + if decimals > 0 { + let zeros_trimmed = s.trim_end_matches('0'); + s = zeros_trimmed.trim_end_matches('.').to_string(); + } + s +} + +/// Try to convert a UI represenation of a token amount to its raw amount using the given decimals +/// field +pub fn try_ui_amount_into_amount(ui_amount: String, decimals: u8) -> Result { + let decimals = decimals as usize; + let mut parts = ui_amount.split('.'); + let mut amount_str = parts.next().unwrap().to_string(); // splitting a string, even an empty one, will always yield an iterator of at least len == 1 + let after_decimal = parts.next().unwrap_or(""); + let after_decimal = after_decimal.trim_end_matches('0'); + if (amount_str.is_empty() && after_decimal.is_empty()) + || parts.next().is_some() + || after_decimal.len() > decimals + { + return Err(ProgramError::InvalidArgument); + } + + amount_str.push_str(after_decimal); + for _ in 0..decimals.saturating_sub(after_decimal.len()) { + amount_str.push('0'); + } + amount_str + .parse::() + .map_err(|_| ProgramError::InvalidArgument) +} + solana_program::declare_id!("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); /// Checks that the supplied program ID is the correct one for SPL-token diff --git a/program/src/processor.rs b/program/src/processor.rs index eeee445..4dded86 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -1,9 +1,11 @@ //! Program state processor use crate::{ + amount_to_ui_amount_string_trimmed, error::TokenError, instruction::{is_valid_signer_index, AuthorityType, TokenInstruction, MAX_SIGNERS}, state::{Account, AccountState, Mint, Multisig}, + try_ui_amount_into_amount, }; use num_traits::FromPrimitive; use solana_program::{ @@ -768,7 +770,8 @@ impl Processor { // make sure the mint is valid let mint_info = next_account_info(account_info_iter)?; Self::check_account_owner(program_id, mint_info)?; - let _ = Mint::unpack(&mint_info.data.borrow())?; + let _ = Mint::unpack(&mint_info.data.borrow()) + .map_err(|_| Into::::into(TokenError::InvalidMint))?; set_return_data(&Account::LEN.to_le_bytes()); Ok(()) } @@ -785,6 +788,42 @@ impl Processor { Ok(()) } + /// Processes an [AmountToUiAmount](enum.TokenInstruction.html) instruction + pub fn process_amount_to_ui_amount( + program_id: &Pubkey, + accounts: &[AccountInfo], + amount: u64, + ) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let mint_info = next_account_info(account_info_iter)?; + Self::check_account_owner(program_id, mint_info)?; + + let mint = Mint::unpack(&mint_info.data.borrow_mut()) + .map_err(|_| Into::::into(TokenError::InvalidMint))?; + let ui_amount = amount_to_ui_amount_string_trimmed(amount, mint.decimals); + + set_return_data(&ui_amount.into_bytes()); + Ok(()) + } + + /// Processes an [AmountToUiAmount](enum.TokenInstruction.html) instruction + pub fn process_ui_amount_to_amount( + program_id: &Pubkey, + accounts: &[AccountInfo], + ui_amount: &str, + ) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + let mint_info = next_account_info(account_info_iter)?; + Self::check_account_owner(program_id, mint_info)?; + + let mint = Mint::unpack(&mint_info.data.borrow_mut()) + .map_err(|_| Into::::into(TokenError::InvalidMint))?; + let amount = try_ui_amount_into_amount(ui_amount.to_string(), mint.decimals)?; + + set_return_data(&amount.to_le_bytes()); + Ok(()) + } + /// Processes an [Instruction](enum.Instruction.html). pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult { let instruction = TokenInstruction::unpack(input)?; @@ -893,6 +932,14 @@ impl Processor { msg!("Instruction: InitializeImmutableOwner"); Self::process_initialize_immutable_owner(accounts) } + TokenInstruction::AmountToUiAmount { amount } => { + msg!("Instruction: AmountToUiAmount"); + Self::process_amount_to_ui_amount(program_id, accounts, amount) + } + TokenInstruction::UiAmountToAmount { ui_amount } => { + msg!("Instruction: UiAmountToAmount"); + Self::process_ui_amount_to_amount(program_id, accounts, ui_amount) + } } } @@ -997,6 +1044,7 @@ impl PrintProgramError for TokenError { mod tests { use super::*; use crate::instruction::*; + use serial_test::serial; use solana_program::{ account_info::IntoAccountInfo, clock::Epoch, instruction::Instruction, program_error, sysvar::rent, @@ -6406,6 +6454,7 @@ mod tests { } #[test] + #[serial] fn test_get_account_data_size() { // see integration tests for return-data validity let program_id = crate::id(); @@ -6416,7 +6465,7 @@ mod tests { let mint_key = Pubkey::new_unique(); // fail if an invalid mint is passed in assert_eq!( - Err(ProgramError::UninitializedAccount), + Err(TokenError::InvalidMint.into()), do_process_instruction( get_account_data_size(&program_id, &mint_key).unwrap(), vec![&mut mint_account], @@ -6488,4 +6537,179 @@ mod tests { ) ); } + + #[test] + #[serial] + fn test_amount_to_ui_amount() { + let program_id = crate::id(); + let owner_key = Pubkey::new_unique(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mut rent_sysvar = rent_sysvar(); + + // fail if an invalid mint is passed in + assert_eq!( + Err(TokenError::InvalidMint.into()), + do_process_instruction( + amount_to_ui_amount(&program_id, &mint_key, 110).unwrap(), + vec![&mut mint_account], + ) + ); + + // create mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + set_expected_data("0.23".as_bytes().to_vec()); + do_process_instruction( + amount_to_ui_amount(&program_id, &mint_key, 23).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + set_expected_data("1.1".as_bytes().to_vec()); + do_process_instruction( + amount_to_ui_amount(&program_id, &mint_key, 110).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + set_expected_data("42".as_bytes().to_vec()); + do_process_instruction( + amount_to_ui_amount(&program_id, &mint_key, 4200).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + set_expected_data("0".as_bytes().to_vec()); + do_process_instruction( + amount_to_ui_amount(&program_id, &mint_key, 0).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + } + + #[test] + #[serial] + fn test_ui_amount_to_amount() { + let program_id = crate::id(); + let owner_key = Pubkey::new_unique(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + let mut rent_sysvar = rent_sysvar(); + + // fail if an invalid mint is passed in + assert_eq!( + Err(TokenError::InvalidMint.into()), + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "1.1").unwrap(), + vec![&mut mint_account], + ) + ); + + // create mint + do_process_instruction( + initialize_mint(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account, &mut rent_sysvar], + ) + .unwrap(); + + set_expected_data(23u64.to_le_bytes().to_vec()); + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "0.23").unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + set_expected_data(20u64.to_le_bytes().to_vec()); + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "0.20").unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + set_expected_data(20u64.to_le_bytes().to_vec()); + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "0.2000").unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + set_expected_data(20u64.to_le_bytes().to_vec()); + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, ".20").unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + set_expected_data(110u64.to_le_bytes().to_vec()); + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "1.1").unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + set_expected_data(110u64.to_le_bytes().to_vec()); + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "1.10").unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + set_expected_data(4200u64.to_le_bytes().to_vec()); + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "42").unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + set_expected_data(4200u64.to_le_bytes().to_vec()); + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "42.").unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + set_expected_data(0u64.to_le_bytes().to_vec()); + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "0").unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + // fail if invalid ui_amount passed in + assert_eq!( + Err(ProgramError::InvalidArgument), + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "").unwrap(), + vec![&mut mint_account], + ) + ); + assert_eq!( + Err(ProgramError::InvalidArgument), + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, ".").unwrap(), + vec![&mut mint_account], + ) + ); + assert_eq!( + Err(ProgramError::InvalidArgument), + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "0.111").unwrap(), + vec![&mut mint_account], + ) + ); + assert_eq!( + Err(ProgramError::InvalidArgument), + do_process_instruction( + ui_amount_to_amount(&program_id, &mint_key, "0.t").unwrap(), + vec![&mut mint_account], + ) + ); + } } From 4ca309b658211fa3cc87a2764efe716abef67189 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Wed, 23 Feb 2022 16:20:55 -0700 Subject: [PATCH 151/248] Bump solana to v1.9.9 (#2902) --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 2fe11c3..87ba833 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -17,14 +17,14 @@ arrayref = "0.3.6" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.4" -solana-program = "1.9.5" +solana-program = "1.9.9" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" serial_test = "0.5.1" -solana-program-test = "1.9.5" -solana-sdk = "1.9.5" +solana-program-test = "1.9.9" +solana-sdk = "1.9.9" [lib] crate-type = ["cdylib", "lib"] From c850686b5467698ffa235725137995ba4b6f725e Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Thu, 24 Feb 2022 16:46:47 -0700 Subject: [PATCH 152/248] Add comment (#2954) --- program/src/instruction.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index f3b22f1..37f3ea7 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -462,6 +462,9 @@ pub enum TokenInstruction<'a> { /// The ui_amount of tokens to reformat. ui_amount: &'a str, }, + // Any new variants also need to be added to program-2022 `TokenInstruction`, so that the + // latter remains a superset of this instruction set. New variants also need to be added to + // token/js/src/instructions/types.ts to maintain @solana/spl-token compatability } impl<'a> TokenInstruction<'a> { /// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html). From 8cd3c775fbbf1892e341a236d5cc5c0febe50700 Mon Sep 17 00:00:00 2001 From: Lijun Wang <83639177+lijunwangs@users.noreply.github.com> Date: Wed, 2 Mar 2022 11:34:55 -0800 Subject: [PATCH 153/248] Support unpacking token accounts fields partially (#2970) * Support unpacking token accounts fields partially Co-authored-by: Tyera Eulberg --- program/Cargo.toml | 1 + program/src/state.rs | 88 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 87ba833..8e81fc6 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -14,6 +14,7 @@ test-bpf = [] [dependencies] arrayref = "0.3.6" +bytemuck = "1.7.2" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.4" diff --git a/program/src/state.rs b/program/src/state.rs index 71f0d40..bd53434 100644 --- a/program/src/state.rs +++ b/program/src/state.rs @@ -7,7 +7,7 @@ use solana_program::{ program_error::ProgramError, program_option::COption, program_pack::{IsInitialized, Pack, Sealed}, - pubkey::Pubkey, + pubkey::{Pubkey, PUBKEY_BYTES}, }; /// Mint data. @@ -287,6 +287,56 @@ fn unpack_coption_u64(src: &[u8; 12]) -> Result, ProgramError> { } } +const SPL_TOKEN_ACCOUNT_MINT_OFFSET: usize = 0; +const SPL_TOKEN_ACCOUNT_OWNER_OFFSET: usize = 32; + +/// A trait for token Account structs to enable efficiently unpacking various fields +/// without unpacking the complete state. +pub trait GenericTokenAccount { + /// Check if the account data is a valid token account + fn valid_account_data(account_data: &[u8]) -> bool; + + /// Call after account length has already been verified to unpack the account owner + fn unpack_account_owner_unchecked(account_data: &[u8]) -> &Pubkey { + Self::unpack_pubkey_unchecked(account_data, SPL_TOKEN_ACCOUNT_OWNER_OFFSET) + } + + /// Call after account length has already been verified to unpack the account mint + fn unpack_account_mint_unchecked(account_data: &[u8]) -> &Pubkey { + Self::unpack_pubkey_unchecked(account_data, SPL_TOKEN_ACCOUNT_MINT_OFFSET) + } + + /// Call after account length has already been verified to unpack a Pubkey at + /// the specified offset. Panics if `account_data.len()` is less than `PUBKEY_BYTES` + fn unpack_pubkey_unchecked(account_data: &[u8], offset: usize) -> &Pubkey { + bytemuck::from_bytes(&account_data[offset..offset + PUBKEY_BYTES]) + } + + /// Unpacks an account's owner from opaque account data. + fn unpack_account_owner(account_data: &[u8]) -> Option<&Pubkey> { + if Self::valid_account_data(account_data) { + Some(Self::unpack_account_owner_unchecked(account_data)) + } else { + None + } + } + + /// Unpacks an account's mint from opaque account data. + fn unpack_account_mint(account_data: &[u8]) -> Option<&Pubkey> { + if Self::valid_account_data(account_data) { + Some(Self::unpack_account_mint_unchecked(account_data)) + } else { + None + } + } +} + +impl GenericTokenAccount for Account { + fn valid_account_data(account_data: &[u8]) -> bool { + account_data.len() == Account::LEN + } +} + #[cfg(test)] mod tests { use super::*; @@ -360,4 +410,40 @@ mod tests { let result = unpack_coption_u64(&src).unwrap_err(); assert_eq!(result, ProgramError::InvalidAccountData); } + + #[test] + fn test_unpack_token_owner() { + // Account data length < Account::LEN, unpack will not return a key + let src: [u8; 12] = [0; 12]; + let result = Account::unpack_account_owner(&src); + assert_eq!(result, Option::None); + + // The right account data size, unpack will return some key + let src: [u8; Account::LEN] = [0; Account::LEN]; + let result = Account::unpack_account_owner(&src); + assert!(result.is_some()); + + // Account data length > account data size, unpack will not return a key + let src: [u8; Account::LEN + 5] = [0; Account::LEN + 5]; + let result = Account::unpack_account_owner(&src); + assert_eq!(result, Option::None); + } + + #[test] + fn test_unpack_token_mint() { + // Account data length < Account::LEN, unpack will not return a key + let src: [u8; 12] = [0; 12]; + let result = Account::unpack_account_mint(&src); + assert_eq!(result, Option::None); + + // The right account data size, unpack will return some key + let src: [u8; Account::LEN] = [0; Account::LEN]; + let result = Account::unpack_account_mint(&src); + assert!(result.is_some()); + + // Account data length > account data size, unpack will not return a key + let src: [u8; Account::LEN + 5] = [0; Account::LEN + 5]; + let result = Account::unpack_account_mint(&src); + assert_eq!(result, Option::None); + } } From 9afb10e8f09aa4532c7212f547f40d72b9c9ec02 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Thu, 3 Mar 2022 12:34:17 -0700 Subject: [PATCH 154/248] Spell out destination (#2977) --- program/src/processor.rs | 51 +++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index 4dded86..7c0ce9c 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -235,19 +235,19 @@ impl Processor { None }; - let dest_account_info = next_account_info(account_info_iter)?; + let destination_account_info = next_account_info(account_info_iter)?; let authority_info = next_account_info(account_info_iter)?; let mut source_account = Account::unpack(&source_account_info.data.borrow())?; - let mut dest_account = Account::unpack(&dest_account_info.data.borrow())?; + let mut destination_account = Account::unpack(&destination_account_info.data.borrow())?; - if source_account.is_frozen() || dest_account.is_frozen() { + if source_account.is_frozen() || destination_account.is_frozen() { return Err(TokenError::AccountFrozen.into()); } if source_account.amount < amount { return Err(TokenError::InsufficientFunds.into()); } - if !Self::cmp_pubkeys(&source_account.mint, &dest_account.mint) { + if !Self::cmp_pubkeys(&source_account.mint, &destination_account.mint) { return Err(TokenError::MintMismatch.into()); } @@ -262,7 +262,8 @@ impl Processor { } } - let self_transfer = Self::cmp_pubkeys(source_account_info.key, dest_account_info.key); + let self_transfer = + Self::cmp_pubkeys(source_account_info.key, destination_account_info.key); match source_account.delegate { COption::Some(ref delegate) if Self::cmp_pubkeys(authority_info.key, delegate) => { @@ -295,7 +296,7 @@ impl Processor { if self_transfer || amount == 0 { Self::check_account_owner(program_id, source_account_info)?; - Self::check_account_owner(program_id, dest_account_info)?; + Self::check_account_owner(program_id, destination_account_info)?; } // This check MUST occur just before the amounts are manipulated @@ -308,7 +309,7 @@ impl Processor { .amount .checked_sub(amount) .ok_or(TokenError::Overflow)?; - dest_account.amount = dest_account + destination_account.amount = destination_account .amount .checked_add(amount) .ok_or(TokenError::Overflow)?; @@ -319,14 +320,17 @@ impl Processor { .checked_sub(amount) .ok_or(TokenError::Overflow)?; - let dest_starting_lamports = dest_account_info.lamports(); - **dest_account_info.lamports.borrow_mut() = dest_starting_lamports + let destination_starting_lamports = destination_account_info.lamports(); + **destination_account_info.lamports.borrow_mut() = destination_starting_lamports .checked_add(amount) .ok_or(TokenError::Overflow)?; } Account::pack(source_account, &mut source_account_info.data.borrow_mut())?; - Account::pack(dest_account, &mut dest_account_info.data.borrow_mut())?; + Account::pack( + destination_account, + &mut destination_account_info.data.borrow_mut(), + )?; Ok(()) } @@ -517,18 +521,18 @@ impl Processor { ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let mint_info = next_account_info(account_info_iter)?; - let dest_account_info = next_account_info(account_info_iter)?; + let destination_account_info = next_account_info(account_info_iter)?; let owner_info = next_account_info(account_info_iter)?; - let mut dest_account = Account::unpack(&dest_account_info.data.borrow())?; - if dest_account.is_frozen() { + let mut destination_account = Account::unpack(&destination_account_info.data.borrow())?; + if destination_account.is_frozen() { return Err(TokenError::AccountFrozen.into()); } - if dest_account.is_native() { + if destination_account.is_native() { return Err(TokenError::NativeNotSupported.into()); } - if !Self::cmp_pubkeys(mint_info.key, &dest_account.mint) { + if !Self::cmp_pubkeys(mint_info.key, &destination_account.mint) { return Err(TokenError::MintMismatch.into()); } @@ -551,10 +555,10 @@ impl Processor { if amount == 0 { Self::check_account_owner(program_id, mint_info)?; - Self::check_account_owner(program_id, dest_account_info)?; + Self::check_account_owner(program_id, destination_account_info)?; } - dest_account.amount = dest_account + destination_account.amount = destination_account .amount .checked_add(amount) .ok_or(TokenError::Overflow)?; @@ -564,7 +568,10 @@ impl Processor { .checked_add(amount) .ok_or(TokenError::Overflow)?; - Account::pack(dest_account, &mut dest_account_info.data.borrow_mut())?; + Account::pack( + destination_account, + &mut destination_account_info.data.borrow_mut(), + )?; Mint::pack(mint, &mut mint_info.data.borrow_mut())?; Ok(()) @@ -657,10 +664,10 @@ impl Processor { pub fn process_close_account(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let source_account_info = next_account_info(account_info_iter)?; - let dest_account_info = next_account_info(account_info_iter)?; + let destination_account_info = next_account_info(account_info_iter)?; let authority_info = next_account_info(account_info_iter)?; - if Self::cmp_pubkeys(source_account_info.key, dest_account_info.key) { + if Self::cmp_pubkeys(source_account_info.key, destination_account_info.key) { return Err(ProgramError::InvalidAccountData); } @@ -679,8 +686,8 @@ impl Processor { account_info_iter.as_slice(), )?; - let dest_starting_lamports = dest_account_info.lamports(); - **dest_account_info.lamports.borrow_mut() = dest_starting_lamports + let destination_starting_lamports = destination_account_info.lamports(); + **destination_account_info.lamports.borrow_mut() = destination_starting_lamports .checked_add(source_account_info.lamports()) .ok_or(TokenError::Overflow)?; From 56c743dba8ba72266fa0dcd0305968a278b20647 Mon Sep 17 00:00:00 2001 From: Lijun Wang <83639177+lijunwangs@users.noreply.github.com> Date: Wed, 9 Mar 2022 16:53:48 -0800 Subject: [PATCH 155/248] Added checks if a token account is initialized when checking validity (#2986) * Added checks if an account is initialized when checking validity --- program/src/state.rs | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/program/src/state.rs b/program/src/state.rs index bd53434..e9e57ad 100644 --- a/program/src/state.rs +++ b/program/src/state.rs @@ -331,9 +331,21 @@ pub trait GenericTokenAccount { } } +/// The offset of state field in Account's C representation +pub const ACCOUNT_INITIALIZED_INDEX: usize = 108; + +/// Check if the account data buffer represents an initialized account. +/// This is checking the `state` (AccountState) field of an Account object. +pub fn is_initialized_account(account_data: &[u8]) -> bool { + *account_data + .get(ACCOUNT_INITIALIZED_INDEX) + .unwrap_or(&(AccountState::Uninitialized as u8)) + != AccountState::Uninitialized as u8 +} + impl GenericTokenAccount for Account { fn valid_account_data(account_data: &[u8]) -> bool { - account_data.len() == Account::LEN + account_data.len() == Account::LEN && is_initialized_account(account_data) } } @@ -418,11 +430,22 @@ mod tests { let result = Account::unpack_account_owner(&src); assert_eq!(result, Option::None); - // The right account data size, unpack will return some key - let src: [u8; Account::LEN] = [0; Account::LEN]; + // The right account data size and intialized, unpack will return some key + let mut src: [u8; Account::LEN] = [0; Account::LEN]; + src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Initialized as u8; let result = Account::unpack_account_owner(&src); assert!(result.is_some()); + // The right account data size and frozen, unpack will return some key + src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Frozen as u8; + let result = Account::unpack_account_owner(&src); + assert!(result.is_some()); + + // The right account data size and uninitialized, unpack will return None + src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Uninitialized as u8; + let result = Account::unpack_account_mint(&src); + assert_eq!(result, Option::None); + // Account data length > account data size, unpack will not return a key let src: [u8; Account::LEN + 5] = [0; Account::LEN + 5]; let result = Account::unpack_account_owner(&src); @@ -436,11 +459,22 @@ mod tests { let result = Account::unpack_account_mint(&src); assert_eq!(result, Option::None); - // The right account data size, unpack will return some key - let src: [u8; Account::LEN] = [0; Account::LEN]; + // The right account data size and initialized, unpack will return some key + let mut src: [u8; Account::LEN] = [0; Account::LEN]; + src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Initialized as u8; + let result = Account::unpack_account_mint(&src); + assert!(result.is_some()); + + // The right account data size and frozen, unpack will return some key + src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Frozen as u8; let result = Account::unpack_account_mint(&src); assert!(result.is_some()); + // The right account data size and uninitialized, unpack will return None + src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Uninitialized as u8; + let result = Account::unpack_account_mint(&src); + assert_eq!(result, Option::None); + // Account data length > account data size, unpack will not return a key let src: [u8; Account::LEN + 5] = [0; Account::LEN + 5]; let result = Account::unpack_account_mint(&src); From 334951c0cb771e7947ed3d92b52ada5411550566 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Wed, 9 Mar 2022 19:31:39 -0700 Subject: [PATCH 156/248] token-2022: Allow anyone to burn/close an Account owned by the system program or the incinerator (#2890) * Allow anyone to burn and close token Accounts owned by the system program and the incinerator * Require rent from incinerator/system-owned token accounts be burnt when accounts closed * Add support to OG program --- program/src/processor.rs | 321 +++++++++++++++++++++++++++++++++++---- program/src/state.rs | 5 + 2 files changed, 298 insertions(+), 28 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index 7c0ce9c..ac8e30a 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -612,32 +612,34 @@ impl Processor { } } - match source_account.delegate { - COption::Some(ref delegate) if Self::cmp_pubkeys(authority_info.key, delegate) => { - Self::validate_owner( + if !source_account.is_owned_by_system_program_or_incinerator() { + match source_account.delegate { + COption::Some(ref delegate) if Self::cmp_pubkeys(authority_info.key, delegate) => { + Self::validate_owner( + program_id, + delegate, + authority_info, + account_info_iter.as_slice(), + )?; + + if source_account.delegated_amount < amount { + return Err(TokenError::InsufficientFunds.into()); + } + source_account.delegated_amount = source_account + .delegated_amount + .checked_sub(amount) + .ok_or(TokenError::Overflow)?; + if source_account.delegated_amount == 0 { + source_account.delegate = COption::None; + } + } + _ => Self::validate_owner( program_id, - delegate, + &source_account.owner, authority_info, account_info_iter.as_slice(), - )?; - - if source_account.delegated_amount < amount { - return Err(TokenError::InsufficientFunds.into()); - } - source_account.delegated_amount = source_account - .delegated_amount - .checked_sub(amount) - .ok_or(TokenError::Overflow)?; - if source_account.delegated_amount == 0 { - source_account.delegate = COption::None; - } + )?, } - _ => Self::validate_owner( - program_id, - &source_account.owner, - authority_info, - account_info_iter.as_slice(), - )?, } if amount == 0 { @@ -679,12 +681,16 @@ impl Processor { let authority = source_account .close_authority .unwrap_or(source_account.owner); - Self::validate_owner( - program_id, - &authority, - authority_info, - account_info_iter.as_slice(), - )?; + if !source_account.is_owned_by_system_program_or_incinerator() { + Self::validate_owner( + program_id, + &authority, + authority_info, + account_info_iter.as_slice(), + )?; + } else if !solana_program::incinerator::check_id(destination_account_info.key) { + return Err(ProgramError::InvalidAccountData); + } let destination_starting_lamports = destination_account_info.lamports(); **destination_account_info.lamports.borrow_mut() = destination_starting_lamports @@ -4575,6 +4581,265 @@ mod tests { ); } + #[test] + fn test_burn_and_close_system_and_incinerator_tokens() { + let program_id = crate::id(); + let account_key = Pubkey::new_unique(); + let mut account_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let incinerator_account_key = Pubkey::new_unique(); + let mut incinerator_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let system_account_key = Pubkey::new_unique(); + let mut system_account = SolanaAccount::new( + account_minimum_balance(), + Account::get_packed_len(), + &program_id, + ); + let owner_key = Pubkey::new_unique(); + let mut owner_account = SolanaAccount::default(); + let recipient_key = Pubkey::new_unique(); + let mut recipient_account = SolanaAccount::default(); + let mut mock_incinerator_account = SolanaAccount::default(); + let mint_key = Pubkey::new_unique(); + let mut mint_account = + SolanaAccount::new(mint_minimum_balance(), Mint::get_packed_len(), &program_id); + + // create new mint + do_process_instruction( + initialize_mint2(&program_id, &mint_key, &owner_key, None, 2).unwrap(), + vec![&mut mint_account], + ) + .unwrap(); + + // create account + do_process_instruction( + initialize_account3(&program_id, &account_key, &mint_key, &owner_key).unwrap(), + vec![&mut account_account, &mut mint_account], + ) + .unwrap(); + + // create incinerator- and system-owned accounts + do_process_instruction( + initialize_account3( + &program_id, + &incinerator_account_key, + &mint_key, + &solana_program::incinerator::id(), + ) + .unwrap(), + vec![&mut incinerator_account, &mut mint_account], + ) + .unwrap(); + do_process_instruction( + initialize_account3( + &program_id, + &system_account_key, + &mint_key, + &solana_program::system_program::id(), + ) + .unwrap(), + vec![&mut system_account, &mut mint_account], + ) + .unwrap(); + + // mint to account + do_process_instruction( + mint_to(&program_id, &mint_key, &account_key, &owner_key, &[], 1000).unwrap(), + vec![&mut mint_account, &mut account_account, &mut owner_account], + ) + .unwrap(); + + // transfer half to incinerator, half to system program + do_process_instruction( + transfer( + &program_id, + &account_key, + &incinerator_account_key, + &owner_key, + &[], + 500, + ) + .unwrap(), + vec![ + &mut account_account, + &mut incinerator_account, + &mut owner_account, + ], + ) + .unwrap(); + do_process_instruction( + transfer( + &program_id, + &account_key, + &system_account_key, + &owner_key, + &[], + 500, + ) + .unwrap(), + vec![ + &mut account_account, + &mut system_account, + &mut owner_account, + ], + ) + .unwrap(); + + // close with balance fails + assert_eq!( + Err(TokenError::NonNativeHasBalance.into()), + do_process_instruction( + close_account( + &program_id, + &incinerator_account_key, + &solana_program::incinerator::id(), + &owner_key, + &[] + ) + .unwrap(), + vec![ + &mut incinerator_account, + &mut mock_incinerator_account, + &mut owner_account, + ], + ) + ); + assert_eq!( + Err(TokenError::NonNativeHasBalance.into()), + do_process_instruction( + close_account( + &program_id, + &system_account_key, + &solana_program::incinerator::id(), + &owner_key, + &[] + ) + .unwrap(), + vec![ + &mut system_account, + &mut mock_incinerator_account, + &mut owner_account, + ], + ) + ); + + // anyone can burn + do_process_instruction( + burn( + &program_id, + &incinerator_account_key, + &mint_key, + &recipient_key, + &[], + 500, + ) + .unwrap(), + vec![ + &mut incinerator_account, + &mut mint_account, + &mut recipient_account, + ], + ) + .unwrap(); + do_process_instruction( + burn( + &program_id, + &system_account_key, + &mint_key, + &recipient_key, + &[], + 500, + ) + .unwrap(), + vec![ + &mut system_account, + &mut mint_account, + &mut recipient_account, + ], + ) + .unwrap(); + + // closing fails if destination is not the incinerator + assert_eq!( + Err(ProgramError::InvalidAccountData), + do_process_instruction( + close_account( + &program_id, + &incinerator_account_key, + &recipient_key, + &owner_key, + &[] + ) + .unwrap(), + vec![ + &mut incinerator_account, + &mut recipient_account, + &mut owner_account, + ], + ) + ); + assert_eq!( + Err(ProgramError::InvalidAccountData), + do_process_instruction( + close_account( + &program_id, + &system_account_key, + &recipient_key, + &owner_key, + &[] + ) + .unwrap(), + vec![ + &mut system_account, + &mut recipient_account, + &mut owner_account, + ], + ) + ); + + // closing succeeds with incinerator recipient + do_process_instruction( + close_account( + &program_id, + &incinerator_account_key, + &solana_program::incinerator::id(), + &owner_key, + &[], + ) + .unwrap(), + vec![ + &mut incinerator_account, + &mut mock_incinerator_account, + &mut owner_account, + ], + ) + .unwrap(); + + do_process_instruction( + close_account( + &program_id, + &system_account_key, + &solana_program::incinerator::id(), + &owner_key, + &[], + ) + .unwrap(), + vec![ + &mut system_account, + &mut mock_incinerator_account, + &mut owner_account, + ], + ) + .unwrap(); + } + #[test] fn test_multisig() { let program_id = crate::id(); diff --git a/program/src/state.rs b/program/src/state.rs index e9e57ad..6070141 100644 --- a/program/src/state.rs +++ b/program/src/state.rs @@ -113,6 +113,11 @@ impl Account { pub fn is_native(&self) -> bool { self.is_native.is_some() } + /// Checks if a token Account's owner is the system_program or the incinerator + pub fn is_owned_by_system_program_or_incinerator(&self) -> bool { + solana_program::system_program::check_id(&self.owner) + || solana_program::incinerator::check_id(&self.owner) + } } impl Sealed for Account {} impl IsInitialized for Account { From f36505c7c21cfe9bed57e4280d887dfdff7abfc9 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Mon, 11 Apr 2022 22:25:27 +0200 Subject: [PATCH 157/248] Upgrade crates to 1.10.8 (#3076) --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 8e81fc6..ded6b86 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,14 +18,14 @@ bytemuck = "1.7.2" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.4" -solana-program = "1.9.9" +solana-program = "1.10.8" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" serial_test = "0.5.1" -solana-program-test = "1.9.9" -solana-sdk = "1.9.9" +solana-program-test = "1.10.8" +solana-sdk = "1.10.8" [lib] crate-type = ["cdylib", "lib"] From c914a872d81fd874a952117f9a29c7f55956e542 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Fri, 22 Apr 2022 23:55:18 +0200 Subject: [PATCH 158/248] token-2022: Add compatibility test for instructions (#3106) --- program/src/instruction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 37f3ea7..c69c7bb 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -1392,7 +1392,7 @@ pub fn get_account_data_size( Ok(Instruction { program_id: *token_program_id, - accounts: vec![AccountMeta::new(*mint_pubkey, false)], + accounts: vec![AccountMeta::new_readonly(*mint_pubkey, false)], data: TokenInstruction::GetAccountDataSize.pack(), }) } From 5811e5652f4895e42d6fea078e7a644647d959cc Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Tue, 26 Apr 2022 14:15:26 -0400 Subject: [PATCH 159/248] Bump token-2022 (#3113) * Bump token-2022 * Bump solana dependencies --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index ded6b86..4c53835 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,14 +18,14 @@ bytemuck = "1.7.2" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.4" -solana-program = "1.10.8" +solana-program = "1.10.10" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" serial_test = "0.5.1" -solana-program-test = "1.10.8" -solana-sdk = "1.10.8" +solana-program-test = "1.10.10" +solana-sdk = "1.10.10" [lib] crate-type = ["cdylib", "lib"] From 5c24b97d9d32ec4d230319352495993e5947331d Mon Sep 17 00:00:00 2001 From: Dmitri Makarov Date: Tue, 17 May 2022 11:27:30 -0700 Subject: [PATCH 160/248] Bump solana to v1.10.15 (#3176) --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 4c53835..dacf748 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,14 +18,14 @@ bytemuck = "1.7.2" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.4" -solana-program = "1.10.10" +solana-program = "1.10.15" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" serial_test = "0.5.1" -solana-program-test = "1.10.10" -solana-sdk = "1.10.10" +solana-program-test = "1.10.15" +solana-sdk = "1.10.15" [lib] crate-type = ["cdylib", "lib"] From af203e90417899d7d4568b8306950c9a4b8c2cce Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Fri, 1 Jul 2022 22:51:01 +0200 Subject: [PATCH 161/248] Update Solana crates to 1.10.29 (#3303) --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index dacf748..fdf1ebe 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,14 +18,14 @@ bytemuck = "1.7.2" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.4" -solana-program = "1.10.15" +solana-program = "1.10.29" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" serial_test = "0.5.1" -solana-program-test = "1.10.15" -solana-sdk = "1.10.15" +solana-program-test = "1.10.29" +solana-sdk = "1.10.29" [lib] crate-type = ["cdylib", "lib"] From 0ad821d1b7dd94229653a2df5af107b139832935 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Justin=20Mal=C4=8Di=C4=87?= Date: Tue, 26 Jul 2022 12:59:37 +0100 Subject: [PATCH 162/248] Improve token error messages in token-swap (#3357) * Move PrintProgramError impls * Add wrapper function * Add PrintProgramError to use declaration --- program/src/error.rs | 54 +++++++++++++++++++++++++++++++++++++++- program/src/processor.rs | 53 ++++----------------------------------- 2 files changed, 58 insertions(+), 49 deletions(-) diff --git a/program/src/error.rs b/program/src/error.rs index ed3faed..454aa9e 100644 --- a/program/src/error.rs +++ b/program/src/error.rs @@ -1,7 +1,11 @@ //! Error types use num_derive::FromPrimitive; -use solana_program::{decode_error::DecodeError, program_error::ProgramError}; +use solana_program::{ + decode_error::DecodeError, + msg, + program_error::{PrintProgramError, ProgramError}, +}; use thiserror::Error; /// Errors that may be returned by the Token program. @@ -85,3 +89,51 @@ impl DecodeError for TokenError { "TokenError" } } + +impl PrintProgramError for TokenError { + fn print(&self) + where + E: 'static + + std::error::Error + + DecodeError + + PrintProgramError + + num_traits::FromPrimitive, + { + match self { + TokenError::NotRentExempt => msg!("Error: Lamport balance below rent-exempt threshold"), + TokenError::InsufficientFunds => msg!("Error: insufficient funds"), + TokenError::InvalidMint => msg!("Error: Invalid Mint"), + TokenError::MintMismatch => msg!("Error: Account not associated with this Mint"), + TokenError::OwnerMismatch => msg!("Error: owner does not match"), + TokenError::FixedSupply => msg!("Error: the total supply of this token is fixed"), + TokenError::AlreadyInUse => msg!("Error: account or token already in use"), + TokenError::InvalidNumberOfProvidedSigners => { + msg!("Error: Invalid number of provided signers") + } + TokenError::InvalidNumberOfRequiredSigners => { + msg!("Error: Invalid number of required signers") + } + TokenError::UninitializedState => msg!("Error: State is uninitialized"), + TokenError::NativeNotSupported => { + msg!("Error: Instruction does not support native tokens") + } + TokenError::NonNativeHasBalance => { + msg!("Error: Non-native account can only be closed if its balance is zero") + } + TokenError::InvalidInstruction => msg!("Error: Invalid instruction"), + TokenError::InvalidState => msg!("Error: Invalid account state for operation"), + TokenError::Overflow => msg!("Error: Operation overflowed"), + TokenError::AuthorityTypeNotSupported => { + msg!("Error: Account does not support specified authority type") + } + TokenError::MintCannotFreeze => msg!("Error: This token mint cannot freeze accounts"), + TokenError::AccountFrozen => msg!("Error: Account is frozen"), + TokenError::MintDecimalsMismatch => { + msg!("Error: decimals different from the Mint decimals") + } + TokenError::NonNativeNotSupported => { + msg!("Error: Instruction does not support non-native tokens") + } + } + } +} diff --git a/program/src/processor.rs b/program/src/processor.rs index ac8e30a..e617709 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -7,14 +7,12 @@ use crate::{ state::{Account, AccountState, Mint, Multisig}, try_ui_amount_into_amount, }; -use num_traits::FromPrimitive; use solana_program::{ account_info::{next_account_info, AccountInfo}, - decode_error::DecodeError, entrypoint::ProgramResult, msg, program::set_return_data, - program_error::{PrintProgramError, ProgramError}, + program_error::ProgramError, program_memory::{sol_memcmp, sol_memset}, program_option::COption, program_pack::{IsInitialized, Pack}, @@ -1009,57 +1007,16 @@ impl Processor { } } -impl PrintProgramError for TokenError { - fn print(&self) - where - E: 'static + std::error::Error + DecodeError + PrintProgramError + FromPrimitive, - { - match self { - TokenError::NotRentExempt => msg!("Error: Lamport balance below rent-exempt threshold"), - TokenError::InsufficientFunds => msg!("Error: insufficient funds"), - TokenError::InvalidMint => msg!("Error: Invalid Mint"), - TokenError::MintMismatch => msg!("Error: Account not associated with this Mint"), - TokenError::OwnerMismatch => msg!("Error: owner does not match"), - TokenError::FixedSupply => msg!("Error: the total supply of this token is fixed"), - TokenError::AlreadyInUse => msg!("Error: account or token already in use"), - TokenError::InvalidNumberOfProvidedSigners => { - msg!("Error: Invalid number of provided signers") - } - TokenError::InvalidNumberOfRequiredSigners => { - msg!("Error: Invalid number of required signers") - } - TokenError::UninitializedState => msg!("Error: State is uninitialized"), - TokenError::NativeNotSupported => { - msg!("Error: Instruction does not support native tokens") - } - TokenError::NonNativeHasBalance => { - msg!("Error: Non-native account can only be closed if its balance is zero") - } - TokenError::InvalidInstruction => msg!("Error: Invalid instruction"), - TokenError::InvalidState => msg!("Error: Invalid account state for operation"), - TokenError::Overflow => msg!("Error: Operation overflowed"), - TokenError::AuthorityTypeNotSupported => { - msg!("Error: Account does not support specified authority type") - } - TokenError::MintCannotFreeze => msg!("Error: This token mint cannot freeze accounts"), - TokenError::AccountFrozen => msg!("Error: Account is frozen"), - TokenError::MintDecimalsMismatch => { - msg!("Error: decimals different from the Mint decimals") - } - TokenError::NonNativeNotSupported => { - msg!("Error: Instruction does not support non-native tokens") - } - } - } -} - #[cfg(test)] mod tests { use super::*; use crate::instruction::*; use serial_test::serial; use solana_program::{ - account_info::IntoAccountInfo, clock::Epoch, instruction::Instruction, program_error, + account_info::IntoAccountInfo, + clock::Epoch, + instruction::Instruction, + program_error::{self, PrintProgramError}, sysvar::rent, }; use solana_sdk::account::{ From ad7e562149df26778b4632180fb7d2c95f84e8c0 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Wed, 27 Jul 2022 10:28:14 -0700 Subject: [PATCH 163/248] Bump solana crates to v1.10.33 (#3385) --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index fdf1ebe..45166f9 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,14 +18,14 @@ bytemuck = "1.7.2" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.4" -solana-program = "1.10.29" +solana-program = "1.10.33" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" serial_test = "0.5.1" -solana-program-test = "1.10.29" -solana-sdk = "1.10.29" +solana-program-test = "1.10.33" +solana-sdk = "1.10.33" [lib] crate-type = ["cdylib", "lib"] From 4d60db88825a44c4ff4bade68cca4e93bf62d9d7 Mon Sep 17 00:00:00 2001 From: anatoly yakovenko Date: Tue, 2 Aug 2022 22:11:13 -0700 Subject: [PATCH 164/248] check that unpack is tolerant of small sizes (#3416) --- program/src/instruction.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index c69c7bb..7e70b80 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -519,6 +519,9 @@ impl<'a> TokenInstruction<'a> { 10 => Self::FreezeAccount, 11 => Self::ThawAccount, 12 => { + if rest.len() < 8 { + return Err(TokenError::InvalidInstruction.into()); + } let (amount, rest) = rest.split_at(8); let amount = amount .try_into() @@ -530,6 +533,9 @@ impl<'a> TokenInstruction<'a> { Self::TransferChecked { amount, decimals } } 13 => { + if rest.len() < 8 { + return Err(TokenError::InvalidInstruction.into()); + } let (amount, rest) = rest.split_at(8); let amount = amount .try_into() @@ -541,6 +547,9 @@ impl<'a> TokenInstruction<'a> { Self::ApproveChecked { amount, decimals } } 14 => { + if rest.len() < 8 { + return Err(TokenError::InvalidInstruction.into()); + } let (amount, rest) = rest.split_at(8); let amount = amount .try_into() @@ -552,6 +561,9 @@ impl<'a> TokenInstruction<'a> { Self::MintToChecked { amount, decimals } } 15 => { + if rest.len() < 8 { + return Err(TokenError::InvalidInstruction.into()); + } let (amount, rest) = rest.split_at(8); let amount = amount .try_into() @@ -588,6 +600,9 @@ impl<'a> TokenInstruction<'a> { 21 => Self::GetAccountDataSize, 22 => Self::InitializeImmutableOwner, 23 => { + if rest.len() < 8 { + return Err(TokenError::InvalidInstruction.into()); + } let (amount, _rest) = rest.split_at(8); let amount = amount .try_into() @@ -1689,4 +1704,12 @@ mod test { let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); } + + #[test] + fn test_instruction_unpack_panic() { + for i in 0..255u8 { + let expect = Vec::from([i, 1, 0, 0, 0, 0, 0, 0, 0, 2]); + _ = TokenInstruction::unpack(&expect[0..2]); + } + } } From b48847646b70bb1f3410e3cc1d93bbd93c26141b Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Wed, 3 Aug 2022 00:24:38 -0700 Subject: [PATCH 165/248] Bump spl-token (#3418) --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 45166f9..5013edc 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-token" -version = "3.3.0" +version = "3.3.1" description = "Solana Program Library Token" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" From a5381321f13128bb7fe8af539ca85fe31c466663 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Wed, 3 Aug 2022 00:44:55 -0700 Subject: [PATCH 166/248] Refactor unpack and make test more robust (#3417) * Refactor hasty fix to match token-2022 * Make test exhaustive * cargo fmt Co-authored-by: Michael Vines --- program/src/instruction.rs | 82 +++++++++++++------------------------- 1 file changed, 27 insertions(+), 55 deletions(-) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 7e70b80..4a34e78 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -15,6 +15,8 @@ use std::mem::size_of; pub const MIN_SIGNERS: usize = 1; /// Maximum number of multisignature signers (max N) pub const MAX_SIGNERS: usize = 11; +/// Serialized length of a u64, for unpacking +const U64_BYTES: usize = 8; /// Instructions supported by the token program. #[repr(C)] @@ -519,59 +521,19 @@ impl<'a> TokenInstruction<'a> { 10 => Self::FreezeAccount, 11 => Self::ThawAccount, 12 => { - if rest.len() < 8 { - return Err(TokenError::InvalidInstruction.into()); - } - let (amount, rest) = rest.split_at(8); - let amount = amount - .try_into() - .ok() - .map(u64::from_le_bytes) - .ok_or(InvalidInstruction)?; - let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?; - + let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?; Self::TransferChecked { amount, decimals } } 13 => { - if rest.len() < 8 { - return Err(TokenError::InvalidInstruction.into()); - } - let (amount, rest) = rest.split_at(8); - let amount = amount - .try_into() - .ok() - .map(u64::from_le_bytes) - .ok_or(InvalidInstruction)?; - let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?; - + let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?; Self::ApproveChecked { amount, decimals } } 14 => { - if rest.len() < 8 { - return Err(TokenError::InvalidInstruction.into()); - } - let (amount, rest) = rest.split_at(8); - let amount = amount - .try_into() - .ok() - .map(u64::from_le_bytes) - .ok_or(InvalidInstruction)?; - let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?; - + let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?; Self::MintToChecked { amount, decimals } } 15 => { - if rest.len() < 8 { - return Err(TokenError::InvalidInstruction.into()); - } - let (amount, rest) = rest.split_at(8); - let amount = amount - .try_into() - .ok() - .map(u64::from_le_bytes) - .ok_or(InvalidInstruction)?; - let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?; - + let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?; Self::BurnChecked { amount, decimals } } 16 => { @@ -600,15 +562,7 @@ impl<'a> TokenInstruction<'a> { 21 => Self::GetAccountDataSize, 22 => Self::InitializeImmutableOwner, 23 => { - if rest.len() < 8 { - return Err(TokenError::InvalidInstruction.into()); - } - let (amount, _rest) = rest.split_at(8); - let amount = amount - .try_into() - .ok() - .map(u64::from_le_bytes) - .ok_or(InvalidInstruction)?; + let (amount, _rest) = Self::unpack_u64(rest)?; Self::AmountToUiAmount { amount } } 24 => { @@ -760,6 +714,21 @@ impl<'a> TokenInstruction<'a> { COption::None => buf.push(0), } } + + fn unpack_u64(input: &[u8]) -> Result<(u64, &[u8]), ProgramError> { + let value = input + .get(..U64_BYTES) + .and_then(|slice| slice.try_into().ok()) + .map(u64::from_le_bytes) + .ok_or(TokenError::InvalidInstruction)?; + Ok((value, &input[U64_BYTES..])) + } + + fn unpack_amount_decimals(input: &[u8]) -> Result<(u64, u8, &[u8]), ProgramError> { + let (amount, rest) = Self::unpack_u64(input)?; + let (&decimals, rest) = rest.split_first().ok_or(TokenError::InvalidInstruction)?; + Ok((amount, decimals, rest)) + } } /// Specifies the authority type for SetAuthority instructions @@ -1708,8 +1677,11 @@ mod test { #[test] fn test_instruction_unpack_panic() { for i in 0..255u8 { - let expect = Vec::from([i, 1, 0, 0, 0, 0, 0, 0, 0, 2]); - _ = TokenInstruction::unpack(&expect[0..2]); + for j in 1..10 { + let mut data = vec![0; j]; + data[0] = i; + let _no_panic = TokenInstruction::unpack(&data); + } } } } From 3d4a7d72bcdbd1d01134fc0cfc44acad0a2163e9 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Wed, 3 Aug 2022 20:01:08 +0200 Subject: [PATCH 167/248] token[-2022]: Add proptest for unpacking (#3421) --- program/Cargo.toml | 1 + program/src/instruction.rs | 17 ++++++++--------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 5013edc..481319e 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -23,6 +23,7 @@ thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" +proptest = "1.0" serial_test = "0.5.1" solana-program-test = "1.10.33" solana-sdk = "1.10.33" diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 4a34e78..d31c118 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -1431,7 +1431,7 @@ pub fn is_valid_signer_index(index: usize) -> bool { #[cfg(test)] mod test { - use super::*; + use {super::*, proptest::prelude::*}; #[test] fn test_instruction_packing() { @@ -1674,14 +1674,13 @@ mod test { assert_eq!(unpacked, check); } - #[test] - fn test_instruction_unpack_panic() { - for i in 0..255u8 { - for j in 1..10 { - let mut data = vec![0; j]; - data[0] = i; - let _no_panic = TokenInstruction::unpack(&data); - } + proptest! { + #![proptest_config(ProptestConfig::with_cases(1024))] + #[test] + fn test_instruction_unpack_panic( + data in prop::collection::vec(any::(), 0..255) + ) { + let _no_panic = TokenInstruction::unpack(&data); } } } From 82ed39407bb30c1ca51bc6aed8af0f63cb0e4204 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Wed, 3 Aug 2022 23:49:36 +0200 Subject: [PATCH 168/248] token: Reassign and reallocate accounts on close (#3415) * token: Reassign and reallocate accounts on close * Revert "Refactor unpack and make test more robust (#3417)" This reverts commit 3cd0c9a82209eaf445662c00122188155594ad4a. * Revert "check that unpack is tolerant of small sizes (#3416)" This reverts commit c202d51918c994230fc71f9b8829a7f7300d5d12. * Also revert 842d4bfad96329968121d0460403268fa73ffe4a --- program/src/instruction.rs | 72 ++++++------ program/src/processor.rs | 25 +++- program/tests/close_account.rs | 202 +++++++++++++++++++++++++++++++++ 3 files changed, 263 insertions(+), 36 deletions(-) create mode 100644 program/tests/close_account.rs diff --git a/program/src/instruction.rs b/program/src/instruction.rs index d31c118..c69c7bb 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -15,8 +15,6 @@ use std::mem::size_of; pub const MIN_SIGNERS: usize = 1; /// Maximum number of multisignature signers (max N) pub const MAX_SIGNERS: usize = 11; -/// Serialized length of a u64, for unpacking -const U64_BYTES: usize = 8; /// Instructions supported by the token program. #[repr(C)] @@ -521,19 +519,47 @@ impl<'a> TokenInstruction<'a> { 10 => Self::FreezeAccount, 11 => Self::ThawAccount, 12 => { - let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?; + let (amount, rest) = rest.split_at(8); + let amount = amount + .try_into() + .ok() + .map(u64::from_le_bytes) + .ok_or(InvalidInstruction)?; + let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?; + Self::TransferChecked { amount, decimals } } 13 => { - let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?; + let (amount, rest) = rest.split_at(8); + let amount = amount + .try_into() + .ok() + .map(u64::from_le_bytes) + .ok_or(InvalidInstruction)?; + let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?; + Self::ApproveChecked { amount, decimals } } 14 => { - let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?; + let (amount, rest) = rest.split_at(8); + let amount = amount + .try_into() + .ok() + .map(u64::from_le_bytes) + .ok_or(InvalidInstruction)?; + let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?; + Self::MintToChecked { amount, decimals } } 15 => { - let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?; + let (amount, rest) = rest.split_at(8); + let amount = amount + .try_into() + .ok() + .map(u64::from_le_bytes) + .ok_or(InvalidInstruction)?; + let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?; + Self::BurnChecked { amount, decimals } } 16 => { @@ -562,7 +588,12 @@ impl<'a> TokenInstruction<'a> { 21 => Self::GetAccountDataSize, 22 => Self::InitializeImmutableOwner, 23 => { - let (amount, _rest) = Self::unpack_u64(rest)?; + let (amount, _rest) = rest.split_at(8); + let amount = amount + .try_into() + .ok() + .map(u64::from_le_bytes) + .ok_or(InvalidInstruction)?; Self::AmountToUiAmount { amount } } 24 => { @@ -714,21 +745,6 @@ impl<'a> TokenInstruction<'a> { COption::None => buf.push(0), } } - - fn unpack_u64(input: &[u8]) -> Result<(u64, &[u8]), ProgramError> { - let value = input - .get(..U64_BYTES) - .and_then(|slice| slice.try_into().ok()) - .map(u64::from_le_bytes) - .ok_or(TokenError::InvalidInstruction)?; - Ok((value, &input[U64_BYTES..])) - } - - fn unpack_amount_decimals(input: &[u8]) -> Result<(u64, u8, &[u8]), ProgramError> { - let (amount, rest) = Self::unpack_u64(input)?; - let (&decimals, rest) = rest.split_first().ok_or(TokenError::InvalidInstruction)?; - Ok((amount, decimals, rest)) - } } /// Specifies the authority type for SetAuthority instructions @@ -1431,7 +1447,7 @@ pub fn is_valid_signer_index(index: usize) -> bool { #[cfg(test)] mod test { - use {super::*, proptest::prelude::*}; + use super::*; #[test] fn test_instruction_packing() { @@ -1673,14 +1689,4 @@ mod test { let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); } - - proptest! { - #![proptest_config(ProptestConfig::with_cases(1024))] - #[test] - fn test_instruction_unpack_panic( - data in prop::collection::vec(any::(), 0..255) - ) { - let _no_panic = TokenInstruction::unpack(&data); - } - } } diff --git a/program/src/processor.rs b/program/src/processor.rs index e617709..de7152a 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -13,10 +13,11 @@ use solana_program::{ msg, program::set_return_data, program_error::ProgramError, - program_memory::{sol_memcmp, sol_memset}, + program_memory::sol_memcmp, program_option::COption, program_pack::{IsInitialized, Pack}, pubkey::{Pubkey, PUBKEY_BYTES}, + system_program, sysvar::{rent::Rent, Sysvar}, }; @@ -696,8 +697,7 @@ impl Processor { .ok_or(TokenError::Overflow)?; **source_account_info.lamports.borrow_mut() = 0; - - sol_memset(*source_account_info.data.borrow_mut(), 0, Account::LEN); + delete_account(source_account_info)?; Ok(()) } @@ -1007,6 +1007,25 @@ impl Processor { } } +/// Helper function to mostly delete an account in a test environment. We could +/// potentially muck around the bytes assuming that a vec is passed in, but that +/// would be more trouble than it's worth. +#[cfg(not(target_arch = "bpf"))] +fn delete_account(account_info: &AccountInfo) -> Result<(), ProgramError> { + account_info.assign(&system_program::id()); + let mut account_data = account_info.data.borrow_mut(); + let data_len = account_data.len(); + solana_program::program_memory::sol_memset(*account_data, 0, data_len); + Ok(()) +} + +/// Helper function to totally delete an account on-chain +#[cfg(target_arch = "bpf")] +fn delete_account(account_info: &AccountInfo) -> Result<(), ProgramError> { + account_info.assign(&system_program::id()); + account_info.realloc(0, false) +} + #[cfg(test)] mod tests { use super::*; diff --git a/program/tests/close_account.rs b/program/tests/close_account.rs new file mode 100644 index 0000000..195a7aa --- /dev/null +++ b/program/tests/close_account.rs @@ -0,0 +1,202 @@ +#![cfg(feature = "test-bpf")] + +use { + solana_program_test::{processor, tokio, ProgramTest, ProgramTestContext}, + solana_sdk::{ + instruction::InstructionError, + program_pack::Pack, + pubkey::Pubkey, + signature::Signer, + signer::keypair::Keypair, + system_instruction, + transaction::{Transaction, TransactionError}, + }, + spl_token::{ + instruction, + processor::Processor, + state::{Account, Mint}, + }, +}; + +async fn setup_mint_and_account( + context: &mut ProgramTestContext, + mint: &Keypair, + token_account: &Keypair, + owner: &Pubkey, + token_program_id: &Pubkey, +) { + let rent = context.banks_client.get_rent().await.unwrap(); + let mint_authority_pubkey = Pubkey::new_unique(); + + let space = Mint::LEN; + let tx = Transaction::new_signed_with_payer( + &[ + system_instruction::create_account( + &context.payer.pubkey(), + &mint.pubkey(), + rent.minimum_balance(space), + space as u64, + &token_program_id, + ), + instruction::initialize_mint( + &token_program_id, + &mint.pubkey(), + &mint_authority_pubkey, + None, + 9, + ) + .unwrap(), + ], + Some(&context.payer.pubkey()), + &[&context.payer, mint], + context.last_blockhash, + ); + context.banks_client.process_transaction(tx).await.unwrap(); + let space = Account::LEN; + let tx = Transaction::new_signed_with_payer( + &[ + system_instruction::create_account( + &context.payer.pubkey(), + &token_account.pubkey(), + rent.minimum_balance(space), + space as u64, + &token_program_id, + ), + instruction::initialize_account( + &token_program_id, + &token_account.pubkey(), + &mint.pubkey(), + &owner, + ) + .unwrap(), + ], + Some(&context.payer.pubkey()), + &[&context.payer, &token_account], + context.last_blockhash, + ); + context.banks_client.process_transaction(tx).await.unwrap(); +} + +#[tokio::test] +async fn success_init_after_close_account() { + let program_test = + ProgramTest::new("spl_token", spl_token::id(), processor!(Processor::process)); + let mut context = program_test.start_with_context().await; + let mint = Keypair::new(); + let token_account = Keypair::new(); + let owner = Keypair::new(); + let token_program_id = spl_token::id(); + setup_mint_and_account( + &mut context, + &mint, + &token_account, + &owner.pubkey(), + &token_program_id, + ) + .await; + + let destination = Pubkey::new_unique(); + let tx = Transaction::new_signed_with_payer( + &[ + instruction::close_account( + &token_program_id, + &token_account.pubkey(), + &destination, + &owner.pubkey(), + &[], + ) + .unwrap(), + system_instruction::create_account( + &context.payer.pubkey(), + &token_account.pubkey(), + 1_000_000_000, + Account::LEN as u64, + &token_program_id, + ), + instruction::initialize_account( + &token_program_id, + &token_account.pubkey(), + &mint.pubkey(), + &owner.pubkey(), + ) + .unwrap(), + ], + Some(&context.payer.pubkey()), + &[&context.payer, &owner, &token_account], + context.last_blockhash, + ); + context.banks_client.process_transaction(tx).await.unwrap(); + let destination = context + .banks_client + .get_account(destination) + .await + .unwrap() + .unwrap(); + assert!(destination.lamports > 0); +} + +#[tokio::test] +async fn fail_init_after_close_account() { + let program_test = + ProgramTest::new("spl_token", spl_token::id(), processor!(Processor::process)); + let mut context = program_test.start_with_context().await; + let mint = Keypair::new(); + let token_account = Keypair::new(); + let owner = Keypair::new(); + let token_program_id = spl_token::id(); + setup_mint_and_account( + &mut context, + &mint, + &token_account, + &owner.pubkey(), + &token_program_id, + ) + .await; + + let destination = Pubkey::new_unique(); + let tx = Transaction::new_signed_with_payer( + &[ + instruction::close_account( + &token_program_id, + &token_account.pubkey(), + &destination, + &owner.pubkey(), + &[], + ) + .unwrap(), + system_instruction::transfer( + &context.payer.pubkey(), + &token_account.pubkey(), + 1_000_000_000, + ), + instruction::initialize_account( + &token_program_id, + &token_account.pubkey(), + &mint.pubkey(), + &owner.pubkey(), + ) + .unwrap(), + ], + Some(&context.payer.pubkey()), + &[&context.payer, &owner], + context.last_blockhash, + ); + #[allow(clippy::useless_conversion)] + let error: TransactionError = context + .banks_client + .process_transaction(tx) + .await + .unwrap_err() + .unwrap() + .into(); + assert_eq!( + error, + TransactionError::InstructionError(2, InstructionError::InvalidAccountData) + ); + assert!(context + .banks_client + .get_account(destination) + .await + .unwrap() + .is_none()); +} From 56d94fd392f465ce5fa63e171a40dea76e5fcc41 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Thu, 4 Aug 2022 01:03:04 +0200 Subject: [PATCH 169/248] token/ata: Bump versions to 3.4.0 and 1.1.0, respectively (#3427) --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 481319e..cf21ce7 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-token" -version = "3.3.1" +version = "3.4.0" description = "Solana Program Library Token" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" From 95aa34ac41e92b9294166ba83a75b78008f8db71 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Wed, 3 Aug 2022 23:25:07 -0700 Subject: [PATCH 170/248] Prep for spl-token v3.5.0 (#3426) * check that unpack is tolerant of small sizes (#3416) * Refactor unpack and make test more robust (#3417) * Refactor hasty fix to match token-2022 * Make test exhaustive * cargo fmt Co-authored-by: Michael Vines * Readd proptests without losing unit test, #3421 Co-authored-by: anatoly yakovenko Co-authored-by: Michael Vines --- program/src/instruction.rs | 83 ++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 39 deletions(-) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index c69c7bb..9dccdfb 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -15,6 +15,8 @@ use std::mem::size_of; pub const MIN_SIGNERS: usize = 1; /// Maximum number of multisignature signers (max N) pub const MAX_SIGNERS: usize = 11; +/// Serialized length of a u64, for unpacking +const U64_BYTES: usize = 8; /// Instructions supported by the token program. #[repr(C)] @@ -519,47 +521,19 @@ impl<'a> TokenInstruction<'a> { 10 => Self::FreezeAccount, 11 => Self::ThawAccount, 12 => { - let (amount, rest) = rest.split_at(8); - let amount = amount - .try_into() - .ok() - .map(u64::from_le_bytes) - .ok_or(InvalidInstruction)?; - let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?; - + let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?; Self::TransferChecked { amount, decimals } } 13 => { - let (amount, rest) = rest.split_at(8); - let amount = amount - .try_into() - .ok() - .map(u64::from_le_bytes) - .ok_or(InvalidInstruction)?; - let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?; - + let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?; Self::ApproveChecked { amount, decimals } } 14 => { - let (amount, rest) = rest.split_at(8); - let amount = amount - .try_into() - .ok() - .map(u64::from_le_bytes) - .ok_or(InvalidInstruction)?; - let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?; - + let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?; Self::MintToChecked { amount, decimals } } 15 => { - let (amount, rest) = rest.split_at(8); - let amount = amount - .try_into() - .ok() - .map(u64::from_le_bytes) - .ok_or(InvalidInstruction)?; - let (&decimals, _rest) = rest.split_first().ok_or(InvalidInstruction)?; - + let (amount, decimals, _rest) = Self::unpack_amount_decimals(rest)?; Self::BurnChecked { amount, decimals } } 16 => { @@ -588,12 +562,7 @@ impl<'a> TokenInstruction<'a> { 21 => Self::GetAccountDataSize, 22 => Self::InitializeImmutableOwner, 23 => { - let (amount, _rest) = rest.split_at(8); - let amount = amount - .try_into() - .ok() - .map(u64::from_le_bytes) - .ok_or(InvalidInstruction)?; + let (amount, _rest) = Self::unpack_u64(rest)?; Self::AmountToUiAmount { amount } } 24 => { @@ -745,6 +714,21 @@ impl<'a> TokenInstruction<'a> { COption::None => buf.push(0), } } + + fn unpack_u64(input: &[u8]) -> Result<(u64, &[u8]), ProgramError> { + let value = input + .get(..U64_BYTES) + .and_then(|slice| slice.try_into().ok()) + .map(u64::from_le_bytes) + .ok_or(TokenError::InvalidInstruction)?; + Ok((value, &input[U64_BYTES..])) + } + + fn unpack_amount_decimals(input: &[u8]) -> Result<(u64, u8, &[u8]), ProgramError> { + let (amount, rest) = Self::unpack_u64(input)?; + let (&decimals, rest) = rest.split_first().ok_or(TokenError::InvalidInstruction)?; + Ok((amount, decimals, rest)) + } } /// Specifies the authority type for SetAuthority instructions @@ -1447,7 +1431,7 @@ pub fn is_valid_signer_index(index: usize) -> bool { #[cfg(test)] mod test { - use super::*; + use {super::*, proptest::prelude::*}; #[test] fn test_instruction_packing() { @@ -1689,4 +1673,25 @@ mod test { let unpacked = TokenInstruction::unpack(&expect).unwrap(); assert_eq!(unpacked, check); } + + #[test] + fn test_instruction_unpack_panic() { + for i in 0..255u8 { + for j in 1..10 { + let mut data = vec![0; j]; + data[0] = i; + let _no_panic = TokenInstruction::unpack(&data); + } + } + } + + proptest! { + #![proptest_config(ProptestConfig::with_cases(1024))] + #[test] + fn test_instruction_unpack_proptest( + data in prop::collection::vec(any::(), 0..255) + ) { + let _no_panic = TokenInstruction::unpack(&data); + } + } } From 9f4075d50026448f96198ab1c9d77283085aa1c1 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Thu, 4 Aug 2022 00:09:38 -0700 Subject: [PATCH 171/248] Bump token to v3.5.0 and ata to v1.1.1 (#3430) --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index cf21ce7..6cb8499 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-token" -version = "3.4.0" +version = "3.5.0" description = "Solana Program Library Token" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" From babbce16373bd938cba13c320a897743de3efc32 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Tue, 16 Aug 2022 11:33:24 -0700 Subject: [PATCH 172/248] Bump solana to v1.10.35 (#3485) --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 6cb8499..3978fec 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,15 +18,15 @@ bytemuck = "1.7.2" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.4" -solana-program = "1.10.33" +solana-program = "1.10.35" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" proptest = "1.0" serial_test = "0.5.1" -solana-program-test = "1.10.33" -solana-sdk = "1.10.33" +solana-program-test = "1.10.35" +solana-sdk = "1.10.35" [lib] crate-type = ["cdylib", "lib"] From 3a4c3249314cd7f68f6fa4485130925beb5a3eb7 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Thu, 18 Aug 2022 13:38:10 +0200 Subject: [PATCH 173/248] token[-2022]: Change `target_arch` to `target_os` (#3493) --- program/src/processor.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index de7152a..fbfe6bd 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -1010,7 +1010,7 @@ impl Processor { /// Helper function to mostly delete an account in a test environment. We could /// potentially muck around the bytes assuming that a vec is passed in, but that /// would be more trouble than it's worth. -#[cfg(not(target_arch = "bpf"))] +#[cfg(not(target_os = "solana"))] fn delete_account(account_info: &AccountInfo) -> Result<(), ProgramError> { account_info.assign(&system_program::id()); let mut account_data = account_info.data.borrow_mut(); @@ -1020,7 +1020,7 @@ fn delete_account(account_info: &AccountInfo) -> Result<(), ProgramError> { } /// Helper function to totally delete an account on-chain -#[cfg(target_arch = "bpf")] +#[cfg(target_os = "solana")] fn delete_account(account_info: &AccountInfo) -> Result<(), ProgramError> { account_info.assign(&system_program::id()); account_info.realloc(0, false) From 34a0b4a7d7373475c813e18b448057dfe6186cd5 Mon Sep 17 00:00:00 2001 From: hana <81144685+2501babe@users.noreply.github.com> Date: Thu, 18 Aug 2022 12:16:02 -0700 Subject: [PATCH 174/248] Update rust to 1.60, solana to 1.11.6 (#3492) also change bpf to sbf throughout the codebase Co-authored-by: Jon Cinque --- program/Cargo.toml | 8 ++++---- program/tests/assert_instruction_count.rs | 2 +- program/tests/close_account.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 3978fec..80543d0 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -10,7 +10,7 @@ exclude = ["js/**"] [features] no-entrypoint = [] -test-bpf = [] +test-sbf = [] [dependencies] arrayref = "0.3.6" @@ -18,15 +18,15 @@ bytemuck = "1.7.2" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.4" -solana-program = "1.10.35" +solana-program = "1.11.6" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" proptest = "1.0" serial_test = "0.5.1" -solana-program-test = "1.10.35" -solana-sdk = "1.10.35" +solana-program-test = "1.11.6" +solana-sdk = "1.11.6" [lib] crate-type = ["cdylib", "lib"] diff --git a/program/tests/assert_instruction_count.rs b/program/tests/assert_instruction_count.rs index d663c54..a6df337 100644 --- a/program/tests/assert_instruction_count.rs +++ b/program/tests/assert_instruction_count.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "test-bpf")] +#![cfg(feature = "test-sbf")] mod action; use { diff --git a/program/tests/close_account.rs b/program/tests/close_account.rs index 195a7aa..25adda2 100644 --- a/program/tests/close_account.rs +++ b/program/tests/close_account.rs @@ -1,4 +1,4 @@ -#![cfg(feature = "test-bpf")] +#![cfg(feature = "test-sbf")] use { solana_program_test::{processor, tokio, ProgramTest, ProgramTestContext}, From 20c49b3da21f2dd64931489a7a0ba7685601f104 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Thu, 15 Sep 2022 13:16:53 +0200 Subject: [PATCH 175/248] clippy: Deny integer arithmetic, add allows where needed (#3606) --- program/src/lib.rs | 1 + program/tests/close_account.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/program/src/lib.rs b/program/src/lib.rs index 2252ac1..2019474 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -1,3 +1,4 @@ +#![allow(clippy::integer_arithmetic)] #![deny(missing_docs)] #![cfg_attr(not(test), forbid(unsafe_code))] diff --git a/program/tests/close_account.rs b/program/tests/close_account.rs index 25adda2..8c9cc4f 100644 --- a/program/tests/close_account.rs +++ b/program/tests/close_account.rs @@ -36,10 +36,10 @@ async fn setup_mint_and_account( &mint.pubkey(), rent.minimum_balance(space), space as u64, - &token_program_id, + token_program_id, ), instruction::initialize_mint( - &token_program_id, + token_program_id, &mint.pubkey(), &mint_authority_pubkey, None, @@ -60,18 +60,18 @@ async fn setup_mint_and_account( &token_account.pubkey(), rent.minimum_balance(space), space as u64, - &token_program_id, + token_program_id, ), instruction::initialize_account( - &token_program_id, + token_program_id, &token_account.pubkey(), &mint.pubkey(), - &owner, + owner, ) .unwrap(), ], Some(&context.payer.pubkey()), - &[&context.payer, &token_account], + &[&context.payer, token_account], context.last_blockhash, ); context.banks_client.process_transaction(tx).await.unwrap(); From 78580554b7860c8ad627a20082679003e85e3021 Mon Sep 17 00:00:00 2001 From: timkoopmans Date: Mon, 3 Oct 2022 19:57:36 +1000 Subject: [PATCH 176/248] typo --- program/src/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/src/error.rs b/program/src/error.rs index 454aa9e..72461a0 100644 --- a/program/src/error.rs +++ b/program/src/error.rs @@ -42,7 +42,7 @@ pub enum TokenError { #[error("Invalid number of required signers")] InvalidNumberOfRequiredSigners, /// State is uninitialized. - #[error("State is unititialized")] + #[error("State is uninitialized")] UninitializedState, // 10 From e9e3d79360598d601fc5c88e427c44e7d4a11078 Mon Sep 17 00:00:00 2001 From: hanako mumei <81144685+2501babe@users.noreply.github.com> Date: Tue, 11 Oct 2022 12:04:28 -0700 Subject: [PATCH 177/248] update solana to 1.14.4 --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 80543d0..c2f8527 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,15 +18,15 @@ bytemuck = "1.7.2" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.4" -solana-program = "1.11.6" +solana-program = "1.14.4" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" proptest = "1.0" serial_test = "0.5.1" -solana-program-test = "1.11.6" -solana-sdk = "1.11.6" +solana-program-test = "1.14.4" +solana-sdk = "1.14.4" [lib] crate-type = ["cdylib", "lib"] From cbf1959a9bd627d50bcebf320d28055e75ef5101 Mon Sep 17 00:00:00 2001 From: samkim-crypto Date: Thu, 27 Oct 2022 07:59:25 +0900 Subject: [PATCH 178/248] upgrade solana-program to 1.14.6 (#3765) --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index c2f8527..dc6d459 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,15 +18,15 @@ bytemuck = "1.7.2" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.4" -solana-program = "1.14.4" +solana-program = "1.14.6" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" proptest = "1.0" serial_test = "0.5.1" -solana-program-test = "1.14.4" -solana-sdk = "1.14.4" +solana-program-test = "1.14.6" +solana-sdk = "1.14.6" [lib] crate-type = ["cdylib", "lib"] From 889a954c785b1bcee041414a00ca498f15231ddc Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Fri, 28 Oct 2022 18:03:20 -0400 Subject: [PATCH 179/248] token: Fix some spelling mistakes (#3777) --- program/src/instruction.rs | 2 +- program/src/lib.rs | 4 ++-- program/src/processor.rs | 2 +- program/src/state.rs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 9dccdfb..897be5b 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -466,7 +466,7 @@ pub enum TokenInstruction<'a> { }, // Any new variants also need to be added to program-2022 `TokenInstruction`, so that the // latter remains a superset of this instruction set. New variants also need to be added to - // token/js/src/instructions/types.ts to maintain @solana/spl-token compatability + // token/js/src/instructions/types.ts to maintain @solana/spl-token compatibility } impl<'a> TokenInstruction<'a> { /// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html). diff --git a/program/src/lib.rs b/program/src/lib.rs index 2019474..e449506 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -53,12 +53,12 @@ pub fn amount_to_ui_amount_string_trimmed(amount: u64, decimals: u8) -> String { s } -/// Try to convert a UI represenation of a token amount to its raw amount using the given decimals +/// Try to convert a UI representation of a token amount to its raw amount using the given decimals /// field pub fn try_ui_amount_into_amount(ui_amount: String, decimals: u8) -> Result { let decimals = decimals as usize; let mut parts = ui_amount.split('.'); - let mut amount_str = parts.next().unwrap().to_string(); // splitting a string, even an empty one, will always yield an iterator of at least len == 1 + let mut amount_str = parts.next().unwrap().to_string(); // splitting a string, even an empty one, will always yield an iterator of at least length == 1 let after_decimal = parts.next().unwrap_or(""); let after_decimal = after_decimal.trim_end_matches('0'); if (amount_str.is_empty() && after_decimal.is_empty()) diff --git a/program/src/processor.rs b/program/src/processor.rs index fbfe6bd..6da8d80 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -6066,7 +6066,7 @@ mod tests { let account = Account::unpack_unchecked(&account_account.data).unwrap(); assert_eq!(account.amount, u64::MAX); - // atttempt to mint one more to the other account + // attempt to mint one more to the other account assert_eq!( Err(TokenError::Overflow.into()), do_process_instruction( diff --git a/program/src/state.rs b/program/src/state.rs index 6070141..ba4b193 100644 --- a/program/src/state.rs +++ b/program/src/state.rs @@ -435,7 +435,7 @@ mod tests { let result = Account::unpack_account_owner(&src); assert_eq!(result, Option::None); - // The right account data size and intialized, unpack will return some key + // The right account data size and initialized, unpack will return some key let mut src: [u8; Account::LEN] = [0; Account::LEN]; src[ACCOUNT_INITIALIZED_INDEX] = AccountState::Initialized as u8; let result = Account::unpack_account_owner(&src); From b7eb7fb2b243258f04de30eaa79c9193009960a5 Mon Sep 17 00:00:00 2001 From: samkim-crypto Date: Tue, 6 Dec 2022 12:59:00 +0900 Subject: [PATCH 180/248] update solana to 1.14.10 (#3872) --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index dc6d459..be8dc96 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,15 +18,15 @@ bytemuck = "1.7.2" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.4" -solana-program = "1.14.6" +solana-program = "1.14.10" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" proptest = "1.0" serial_test = "0.5.1" -solana-program-test = "1.14.6" -solana-sdk = "1.14.6" +solana-program-test = "1.14.10" +solana-sdk = "1.14.10" [lib] crate-type = ["cdylib", "lib"] From 01ebdaf45ba7f9f7ccfb2a7c2b40ebee8e25241b Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Tue, 13 Dec 2022 15:56:12 +0100 Subject: [PATCH 181/248] clippy: Remove `useless_conversion` (#3899) --- program/tests/close_account.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/program/tests/close_account.rs b/program/tests/close_account.rs index 8c9cc4f..8425fe2 100644 --- a/program/tests/close_account.rs +++ b/program/tests/close_account.rs @@ -181,14 +181,12 @@ async fn fail_init_after_close_account() { &[&context.payer, &owner], context.last_blockhash, ); - #[allow(clippy::useless_conversion)] - let error: TransactionError = context + let error = context .banks_client .process_transaction(tx) .await .unwrap_err() - .unwrap() - .into(); + .unwrap(); assert_eq!( error, TransactionError::InstructionError(2, InstructionError::InvalidAccountData) From d26a44b7dab89ae7c9641895120e3666eecc8346 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Tue, 13 Dec 2022 23:46:26 +0100 Subject: [PATCH 182/248] Update repo to `edition = "2021"` (#3900) --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index be8dc96..0c0d5fa 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -5,7 +5,7 @@ description = "Solana Program Library Token" authors = ["Solana Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" license = "Apache-2.0" -edition = "2018" +edition = "2021" exclude = ["js/**"] [features] From 7ceb182fb5922befc501c20da93b68b317424d93 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Thu, 19 Jan 2023 21:47:03 +0100 Subject: [PATCH 183/248] ci: Update repo to Solana 1.14.12 (#3989) --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 0c0d5fa..8edd4dd 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,15 +18,15 @@ bytemuck = "1.7.2" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.4" -solana-program = "1.14.10" +solana-program = "1.14.12" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" proptest = "1.0" serial_test = "0.5.1" -solana-program-test = "1.14.10" -solana-sdk = "1.14.10" +solana-program-test = "1.14.12" +solana-sdk = "1.14.12" [lib] crate-type = ["cdylib", "lib"] From acab8f136b8da3758832b10e6c78c4d2d23fc008 Mon Sep 17 00:00:00 2001 From: joeaba <77398477+joeaba@users.noreply.github.com> Date: Tue, 31 Jan 2023 08:06:36 -0500 Subject: [PATCH 184/248] chore: update maintainer references (#4008) --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 8edd4dd..f7303e6 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -2,7 +2,7 @@ name = "spl-token" version = "3.5.0" description = "Solana Program Library Token" -authors = ["Solana Maintainers "] +authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" license = "Apache-2.0" edition = "2021" From e51f09c92703be54b70a56212c6bf30ddb7a6071 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 2 Feb 2023 16:12:15 +0100 Subject: [PATCH 185/248] build(deps): bump num_enum from 0.5.7 to 0.5.9 (#4020) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index f7303e6..e3bbd0a 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -17,7 +17,7 @@ arrayref = "0.3.6" bytemuck = "1.7.2" num-derive = "0.3" num-traits = "0.2" -num_enum = "0.5.4" +num_enum = "0.5.9" solana-program = "1.14.12" thiserror = "1.0" From 42e7247fb5a52aed0bc2d3e2dfae6be10d6e67eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 6 Feb 2023 20:27:18 +0100 Subject: [PATCH 186/248] build(deps): bump bytemuck from 1.12.1 to 1.13.0 (#4027) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index e3bbd0a..93bdc4f 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -14,7 +14,7 @@ test-sbf = [] [dependencies] arrayref = "0.3.6" -bytemuck = "1.7.2" +bytemuck = "1.13.0" num-derive = "0.3" num-traits = "0.2" num_enum = "0.5.9" From 3ce0abc1cf55d0c0be54e9adecc8e60b2e7544fa Mon Sep 17 00:00:00 2001 From: Pierre Date: Tue, 4 Apr 2023 08:30:52 +1000 Subject: [PATCH 187/248] More direct delegate test (#4092) --- program/src/processor.rs | 75 ++++++++++++++++++++++++++++++++-------- 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/program/src/processor.rs b/program/src/processor.rs index 6da8d80..452b16f 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -2115,6 +2115,48 @@ mod tests { ) .unwrap(); + // not a delegate of source account + assert_eq!( + Err(TokenError::OwnerMismatch.into()), + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &owner2_key, // <-- incorrect owner or delegate + &[], + 1, + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut owner2_account, + ], + ) + ); + + // insufficient funds approved via delegate + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + do_process_instruction( + transfer( + &program_id, + &account_key, + &account2_key, + &delegate_key, + &[], + 101 + ) + .unwrap(), + vec![ + &mut account_account, + &mut account2_account, + &mut delegate_account, + ], + ) + ); + // transfer via delegate do_process_instruction( transfer( @@ -2144,7 +2186,7 @@ mod tests { &account2_key, &delegate_key, &[], - 100 + 1 ) .unwrap(), vec![ @@ -4503,18 +4545,31 @@ mod tests { // not a delegate of source account assert_eq!( - Err(TokenError::InsufficientFunds.into()), + Err(TokenError::OwnerMismatch.into()), do_process_instruction( burn( &program_id, &account_key, &mint_key, - &owner_key, + &owner2_key, // <-- incorrect owner or delegate &[], - 100_000_000 + 1, ) .unwrap(), - vec![&mut account_account, &mut mint_account, &mut owner_account], + vec![&mut account_account, &mut mint_account, &mut owner2_account], + ) + ); + + // insufficient funds approved via delegate + assert_eq!( + Err(TokenError::InsufficientFunds.into()), + do_process_instruction( + burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 85).unwrap(), + vec![ + &mut account_account, + &mut mint_account, + &mut delegate_account + ], ) ); @@ -4539,15 +4594,7 @@ mod tests { assert_eq!( Err(TokenError::OwnerMismatch.into()), do_process_instruction( - burn( - &program_id, - &account_key, - &mint_key, - &delegate_key, - &[], - 100 - ) - .unwrap(), + burn(&program_id, &account_key, &mint_key, &delegate_key, &[], 1).unwrap(), vec![ &mut account_account, &mut mint_account, From 9b67d89774707109734ed3b89dab5c9bc39b5e0d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 4 May 2023 13:31:57 +0200 Subject: [PATCH 188/248] build(deps): bump num_enum from 0.5.9 to 0.6.1 (#4156) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 93bdc4f..6720365 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -17,7 +17,7 @@ arrayref = "0.3.6" bytemuck = "1.13.0" num-derive = "0.3" num-traits = "0.2" -num_enum = "0.5.9" +num_enum = "0.6.1" solana-program = "1.14.12" thiserror = "1.0" From 32afcadc37201e12fd4e7e199552163488c44159 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 5 May 2023 22:53:57 +0200 Subject: [PATCH 189/248] build(deps): bump proptest from 1.0.0 to 1.1.0 (#4184) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 6720365..2c3aa2a 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -23,7 +23,7 @@ thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" -proptest = "1.0" +proptest = "1.1" serial_test = "0.5.1" solana-program-test = "1.14.12" solana-sdk = "1.14.12" From b6437594f34b4c347d9ee4c9f579ffceef723dc3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 9 May 2023 20:51:46 +0200 Subject: [PATCH 190/248] build(deps): bump serial_test from 0.5.1 to 0.8.0 (#4206) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 2c3aa2a..adc1311 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -24,7 +24,7 @@ thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" proptest = "1.1" -serial_test = "0.5.1" +serial_test = "0.8.0" solana-program-test = "1.14.12" solana-sdk = "1.14.12" From f442f4d76462711bcd6a4cde9eebcb109ec04af3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 10 May 2023 13:53:18 +0200 Subject: [PATCH 191/248] build(deps): bump arrayref from 0.3.6 to 0.3.7 (#4233) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index adc1311..37c81f7 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -13,7 +13,7 @@ no-entrypoint = [] test-sbf = [] [dependencies] -arrayref = "0.3.6" +arrayref = "0.3.7" bytemuck = "1.13.0" num-derive = "0.3" num-traits = "0.2" From 0e852efb96d69335d8805d8c747b3488730d4d5a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 11 May 2023 13:23:23 +0200 Subject: [PATCH 192/248] build(deps): bump serial_test from 0.8.0 to 2.0.0 (#4253) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 37c81f7..cd6d97d 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -24,7 +24,7 @@ thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" proptest = "1.1" -serial_test = "0.8.0" +serial_test = "2.0.0" solana-program-test = "1.14.12" solana-sdk = "1.14.12" From d4c3ec55232adbfb4d073d8fa2349fb72146d746 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 12 May 2023 12:42:34 +0200 Subject: [PATCH 193/248] build(deps): bump bytemuck from 1.13.0 to 1.13.1 (#4272) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index cd6d97d..6242804 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -14,7 +14,7 @@ test-sbf = [] [dependencies] arrayref = "0.3.7" -bytemuck = "1.13.0" +bytemuck = "1.13.1" num-derive = "0.3" num-traits = "0.2" num_enum = "0.6.1" From 5154f483b41396b4dab80a8c7b9d7f37b1e060c0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 24 May 2023 23:10:53 +0200 Subject: [PATCH 194/248] build(deps): bump proptest from 1.1.0 to 1.2.0 (#4378) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 6242804..7afb888 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -23,7 +23,7 @@ thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" -proptest = "1.1" +proptest = "1.2" serial_test = "2.0.0" solana-program-test = "1.14.12" solana-sdk = "1.14.12" From 8ebb46bc26e81fbe276c3b45638db26e318b49f3 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Thu, 1 Jun 2023 03:00:40 +0200 Subject: [PATCH 195/248] docs: Be really clear that token-2022 is still under audit (#4435) --- program/README.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 program/README.md diff --git a/program/README.md b/program/README.md new file mode 100644 index 0000000..02cbb7c --- /dev/null +++ b/program/README.md @@ -0,0 +1,13 @@ +# Token program + +A token program on the Solana blockchain, usable for fungible and non-fungible tokens. + +This program provides an interface and implementation that third parties can +utilize to create and use their tokens. + +Full documentation is available at https://spl.solana.com/token + +## Audit + +The repository [README](https://github.com/solana-labs/solana-program-library#audits) +contains information about program audits. From ee8daa6c548768c29360c884b10e613661a78085 Mon Sep 17 00:00:00 2001 From: Sergey Kaunov Date: Mon, 5 Jun 2023 14:19:10 +0300 Subject: [PATCH 196/248] Add link to doc: instruction.rs (#4464) --- program/src/instruction.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 897be5b..4e7fbba 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -397,7 +397,7 @@ pub enum TokenInstruction<'a> { /// account. m: u8, }, - /// Like InitializeMint, but does not require the Rent sysvar to be provided + /// Like [`InitializeMint`], but does not require the Rent sysvar to be provided /// /// Accounts expected by this instruction: /// From 795a918c686a9d367aa4dd6faa8cd2af5de2c2d9 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Mon, 26 Jun 2023 14:42:02 +0200 Subject: [PATCH 197/248] Update Solana to 1.16.1 and Rust to 1.69 (#4592) #### Problem The 1.16 Solana crates are out, but SPL is still on 1.14 and needs the new functionality. #### Solution Update the: * rust version to 1.69 * nightly to 2023-04-19 * solana tools to 1.16.1 * solana crates to 1.16.1 * borsh to 0.10 And fix: * new clippy warnings * deprecated warnings in the new solana crates --- program/Cargo.toml | 6 +++--- program/src/instruction.rs | 26 +++++++++++++------------- program/src/processor.rs | 14 +++++++------- program/src/state.rs | 11 +++-------- 4 files changed, 26 insertions(+), 31 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 7afb888..87bdde7 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,15 +18,15 @@ bytemuck = "1.13.1" num-derive = "0.3" num-traits = "0.2" num_enum = "0.6.1" -solana-program = "1.14.12" +solana-program = "1.16.1" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" proptest = "1.2" serial_test = "2.0.0" -solana-program-test = "1.14.12" -solana-sdk = "1.14.12" +solana-program-test = "1.16.1" +solana-sdk = "1.16.1" [lib] crate-type = ["cdylib", "lib"] diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 4e7fbba..e798abd 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -487,7 +487,7 @@ impl<'a> TokenInstruction<'a> { } 1 => Self::InitializeAccount, 2 => { - let &m = rest.get(0).ok_or(InvalidInstruction)?; + let &m = rest.first().ok_or(InvalidInstruction)?; Self::InitializeMultisig { m } } 3 | 4 | 7 | 8 => { @@ -546,7 +546,7 @@ impl<'a> TokenInstruction<'a> { Self::InitializeAccount3 { owner } } 19 => { - let &m = rest.get(0).ok_or(InvalidInstruction)?; + let &m = rest.first().ok_or(InvalidInstruction)?; Self::InitializeMultisig2 { m } } 20 => { @@ -686,7 +686,7 @@ impl<'a> TokenInstruction<'a> { fn unpack_pubkey(input: &[u8]) -> Result<(Pubkey, &[u8]), ProgramError> { if input.len() >= 32 { let (key, rest) = input.split_at(32); - let pk = Pubkey::new(key); + let pk = Pubkey::try_from(key).map_err(|_| TokenError::InvalidInstruction)?; Ok((pk, rest)) } else { Err(TokenError::InvalidInstruction.into()) @@ -698,7 +698,7 @@ impl<'a> TokenInstruction<'a> { Option::Some((&0, rest)) => Ok((COption::None, rest)), Option::Some((&1, rest)) if rest.len() >= 32 => { let (key, rest) = rest.split_at(32); - let pk = Pubkey::new(key); + let pk = Pubkey::try_from(key).map_err(|_| TokenError::InvalidInstruction)?; Ok((COption::Some(pk), rest)) } _ => Err(TokenError::InvalidInstruction.into()), @@ -1437,7 +1437,7 @@ mod test { fn test_instruction_packing() { let check = TokenInstruction::InitializeMint { decimals: 2, - mint_authority: Pubkey::new(&[1u8; 32]), + mint_authority: Pubkey::new_from_array([1u8; 32]), freeze_authority: COption::None, }; let packed = check.pack(); @@ -1450,8 +1450,8 @@ mod test { let check = TokenInstruction::InitializeMint { decimals: 2, - mint_authority: Pubkey::new(&[2u8; 32]), - freeze_authority: COption::Some(Pubkey::new(&[3u8; 32])), + mint_authority: Pubkey::new_from_array([2u8; 32]), + freeze_authority: COption::Some(Pubkey::new_from_array([3u8; 32])), }; let packed = check.pack(); let mut expect = vec![0u8, 2]; @@ -1499,7 +1499,7 @@ mod test { let check = TokenInstruction::SetAuthority { authority_type: AuthorityType::FreezeAccount, - new_authority: COption::Some(Pubkey::new(&[4u8; 32])), + new_authority: COption::Some(Pubkey::new_from_array([4u8; 32])), }; let packed = check.pack(); let mut expect = Vec::from([6u8, 1]); @@ -1585,7 +1585,7 @@ mod test { assert_eq!(unpacked, check); let check = TokenInstruction::InitializeAccount2 { - owner: Pubkey::new(&[2u8; 32]), + owner: Pubkey::new_from_array([2u8; 32]), }; let packed = check.pack(); let mut expect = vec![16u8]; @@ -1602,7 +1602,7 @@ mod test { assert_eq!(unpacked, check); let check = TokenInstruction::InitializeAccount3 { - owner: Pubkey::new(&[2u8; 32]), + owner: Pubkey::new_from_array([2u8; 32]), }; let packed = check.pack(); let mut expect = vec![18u8]; @@ -1620,7 +1620,7 @@ mod test { let check = TokenInstruction::InitializeMint2 { decimals: 2, - mint_authority: Pubkey::new(&[1u8; 32]), + mint_authority: Pubkey::new_from_array([1u8; 32]), freeze_authority: COption::None, }; let packed = check.pack(); @@ -1633,8 +1633,8 @@ mod test { let check = TokenInstruction::InitializeMint2 { decimals: 2, - mint_authority: Pubkey::new(&[2u8; 32]), - freeze_authority: COption::Some(Pubkey::new(&[3u8; 32])), + mint_authority: Pubkey::new_from_array([2u8; 32]), + freeze_authority: COption::Some(Pubkey::new_from_array([3u8; 32])), }; let packed = check.pack(); let mut expect = vec![20u8, 2]; diff --git a/program/src/processor.rs b/program/src/processor.rs index 452b16f..7056f2e 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -1166,11 +1166,11 @@ mod tests { fn test_pack_unpack() { // Mint let check = Mint { - mint_authority: COption::Some(Pubkey::new(&[1; 32])), + mint_authority: COption::Some(Pubkey::new_from_array([1; 32])), supply: 42, decimals: 7, is_initialized: true, - freeze_authority: COption::Some(Pubkey::new(&[2; 32])), + freeze_authority: COption::Some(Pubkey::new_from_array([2; 32])), }; let mut packed = vec![0; Mint::get_packed_len() + 1]; assert_eq!( @@ -1195,14 +1195,14 @@ mod tests { // Account let check = Account { - mint: Pubkey::new(&[1; 32]), - owner: Pubkey::new(&[2; 32]), + mint: Pubkey::new_from_array([1; 32]), + owner: Pubkey::new_from_array([2; 32]), amount: 3, - delegate: COption::Some(Pubkey::new(&[4; 32])), + delegate: COption::Some(Pubkey::new_from_array([4; 32])), state: AccountState::Frozen, is_native: COption::Some(5), delegated_amount: 6, - close_authority: COption::Some(Pubkey::new(&[7; 32])), + close_authority: COption::Some(Pubkey::new_from_array([7; 32])), }; let mut packed = vec![0; Account::get_packed_len() + 1]; assert_eq!( @@ -1233,7 +1233,7 @@ mod tests { m: 1, n: 2, is_initialized: true, - signers: [Pubkey::new(&[3; 32]); MAX_SIGNERS], + signers: [Pubkey::new_from_array([3; 32]); MAX_SIGNERS], }; let mut packed = vec![0; Multisig::get_packed_len() + 1]; assert_eq!( diff --git a/program/src/state.rs b/program/src/state.rs index ba4b193..8723065 100644 --- a/program/src/state.rs +++ b/program/src/state.rs @@ -178,9 +178,10 @@ impl Pack for Account { /// Account state. #[repr(u8)] -#[derive(Clone, Copy, Debug, PartialEq, TryFromPrimitive)] +#[derive(Clone, Copy, Debug, Default, PartialEq, TryFromPrimitive)] pub enum AccountState { /// Account is not yet initialized + #[default] Uninitialized, /// Account is initialized; the account owner and/or delegate may perform permitted operations /// on this account @@ -190,12 +191,6 @@ pub enum AccountState { Frozen, } -impl Default for AccountState { - fn default() -> Self { - AccountState::Uninitialized - } -} - /// Multisignature data. #[repr(C)] #[derive(Clone, Copy, Debug, Default, PartialEq)] @@ -232,7 +227,7 @@ impl Pack for Multisig { signers: [Pubkey::new_from_array([0u8; 32]); MAX_SIGNERS], }; for (src, dst) in signers_flat.chunks(32).zip(result.signers.iter_mut()) { - *dst = Pubkey::new(src); + *dst = Pubkey::try_from(src).map_err(|_| ProgramError::InvalidAccountData)?; } Ok(result) } From 61107af399af146dc529b0fb9e11ae75e42564fd Mon Sep 17 00:00:00 2001 From: Joe C Date: Tue, 27 Jun 2023 08:34:11 -0400 Subject: [PATCH 198/248] bump token & co. (#4612) * bump token & token cli * token version fix --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 87bdde7..4233259 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-token" -version = "3.5.0" +version = "4.0.0" description = "Solana Program Library Token" authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" From c99fd2c27cee3e966c4b62e3f7ebcffb60b615e8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 30 Jun 2023 14:03:21 +0200 Subject: [PATCH 199/248] build(deps): bump num-derive from 0.3.3 to 0.4.0 (#4659) * build(deps): bump num-derive from 0.3.3 to 0.4.0 Bumps [num-derive](https://github.com/rust-num/num-derive) from 0.3.3 to 0.4.0. - [Changelog](https://github.com/rust-num/num-derive/blob/master/RELEASES.md) - [Commits](https://github.com/rust-num/num-derive/compare/num-derive-0.3.3...num-derive-0.4.0) --- updated-dependencies: - dependency-name: num-derive dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Unbump hashbrown in borsh --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jon Cinque --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 4233259..ea22d01 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -15,7 +15,7 @@ test-sbf = [] [dependencies] arrayref = "0.3.7" bytemuck = "1.13.1" -num-derive = "0.3" +num-derive = "0.4" num-traits = "0.2" num_enum = "0.6.1" solana-program = "1.16.1" From d71692b88b3e26d3b70fc3b9575780a2e7741bed Mon Sep 17 00:00:00 2001 From: samkim-crypto Date: Thu, 13 Jul 2023 11:06:07 +0900 Subject: [PATCH 200/248] Upgrade to solana 1.16.3 (#4679) * upgrade to solana 1.16.3 * bring down hashbrown dependency to 0.12.3 --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index ea22d01..593124a 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,15 +18,15 @@ bytemuck = "1.13.1" num-derive = "0.4" num-traits = "0.2" num_enum = "0.6.1" -solana-program = "1.16.1" +solana-program = "1.16.3" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" proptest = "1.2" serial_test = "2.0.0" -solana-program-test = "1.16.1" -solana-sdk = "1.16.1" +solana-program-test = "1.16.3" +solana-sdk = "1.16.3" [lib] crate-type = ["cdylib", "lib"] From 999b44d436da6f02d976497a0dcdddc249513627 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 12:04:17 +0200 Subject: [PATCH 201/248] build(deps): bump num_enum from 0.6.1 to 0.7.0 (#5003) Bumps [num_enum](https://github.com/illicitonion/num_enum) from 0.6.1 to 0.7.0. - [Commits](https://github.com/illicitonion/num_enum/commits) --- updated-dependencies: - dependency-name: num_enum dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 593124a..ee099eb 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -17,7 +17,7 @@ arrayref = "0.3.7" bytemuck = "1.13.1" num-derive = "0.4" num-traits = "0.2" -num_enum = "0.6.1" +num_enum = "0.7.0" solana-program = "1.16.3" thiserror = "1.0" From 3584fb50dab18cf62c525157356f78d077787801 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 09:05:46 -0400 Subject: [PATCH 202/248] build(deps): bump bytemuck from 1.13.1 to 1.14.0 (#5210) Bumps [bytemuck](https://github.com/Lokathor/bytemuck) from 1.13.1 to 1.14.0. - [Changelog](https://github.com/Lokathor/bytemuck/blob/main/changelog.md) - [Commits](https://github.com/Lokathor/bytemuck/compare/v1.13.1...v1.14.0) --- updated-dependencies: - dependency-name: bytemuck dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index ee099eb..bdf8061 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -14,7 +14,7 @@ test-sbf = [] [dependencies] arrayref = "0.3.7" -bytemuck = "1.13.1" +bytemuck = "1.14.0" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.0" From dcc6f6c9528d5d9fb7ec03ae6ed0a07697765be7 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Thu, 21 Sep 2023 10:44:57 +0200 Subject: [PATCH 203/248] Bump repo to 1.16.13 (#5324) * Run update script * Update everything to use non-deprecated functions --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index bdf8061..00542bf 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,15 +18,15 @@ bytemuck = "1.14.0" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.0" -solana-program = "1.16.3" +solana-program = "1.16.13" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" proptest = "1.2" serial_test = "2.0.0" -solana-program-test = "1.16.3" -solana-sdk = "1.16.3" +solana-program-test = "1.16.13" +solana-sdk = "1.16.13" [lib] crate-type = ["cdylib", "lib"] From cf1f0106ec125987332b886b3ad1c7b008311a7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 12:49:41 -0400 Subject: [PATCH 204/248] build(deps): bump proptest from 1.2.0 to 1.3.1 (#5398) Bumps [proptest](https://github.com/proptest-rs/proptest) from 1.2.0 to 1.3.1. - [Release notes](https://github.com/proptest-rs/proptest/releases) - [Changelog](https://github.com/proptest-rs/proptest/blob/master/CHANGELOG.md) - [Commits](https://github.com/proptest-rs/proptest/compare/v1.2.0...v1.3.1) --- updated-dependencies: - dependency-name: proptest dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 00542bf..0352df8 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -23,7 +23,7 @@ thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" -proptest = "1.2" +proptest = "1.3" serial_test = "2.0.0" solana-program-test = "1.16.13" solana-sdk = "1.16.13" From 756a689c22b0f263bfdb09686680a2b6de52c851 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Tue, 10 Oct 2023 21:02:47 -0400 Subject: [PATCH 205/248] chore: Bump Solana crates to 1.16.16 (#5494) --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 0352df8..3fdf234 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,15 +18,15 @@ bytemuck = "1.14.0" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.0" -solana-program = "1.16.13" +solana-program = "1.16.16" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" proptest = "1.3" serial_test = "2.0.0" -solana-program-test = "1.16.13" -solana-sdk = "1.16.13" +solana-program-test = "1.16.16" +solana-sdk = "1.16.16" [lib] crate-type = ["cdylib", "lib"] From 7232a3a70c6ac008d363a725da7bac5aaba61837 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Thu, 19 Oct 2023 18:04:18 +0200 Subject: [PATCH 206/248] ci: Bump repo to Solana 1.17 (#5575) * Update workspace for new cargo resolver, remove ntapi patch * Update Rust versions * Update dependencies with `./update-solana-dependencies.sh 1.17.2` * Update lockfile * Fix build errors * Run clippy with `--fix` * concurrent-merkle-tree: Fix function to not use mutable ref * Replace StakeState with StakeStateV2 * governance: Fix clippy lint * governance: Fix unnecessary mut * Allow `clippy::items_after_module` * token: Make error tests clearer * token-2022: Fix private glob re-export * token-upgrade-cli: Replace validator with parser * single-pool-cli: Fix parsers * ci: Update clippy command * Update anchor version * token-metadata: Use `no-entrypoint` feature in token-2022 * ci: Add protobuf-compiler to build deps * discriminator-syn: *Don't* specify type of lib to build * ci: Blast directories in cargo-build-test * account-compression: Update build and lockfile * Update token-group and feature-gate * single-pool: revert WrongStakeStateV2 * stake-pool: revert WrongStakeStateV2 * stake-pool-py: revert StakeStateV2 --------- Co-authored-by: hanako mumei <81144685+2501babe@users.noreply.github.com> --- program/Cargo.toml | 6 +++--- program/src/lib.rs | 2 +- program/src/processor.rs | 8 +++++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 3fdf234..51fac3e 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,15 +18,15 @@ bytemuck = "1.14.0" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.0" -solana-program = "1.16.16" +solana-program = "1.17.2" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" proptest = "1.3" serial_test = "2.0.0" -solana-program-test = "1.16.16" -solana-sdk = "1.16.16" +solana-program-test = "1.17.2" +solana-sdk = "1.17.2" [lib] crate-type = ["cdylib", "lib"] diff --git a/program/src/lib.rs b/program/src/lib.rs index e449506..1eb3855 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -1,4 +1,4 @@ -#![allow(clippy::integer_arithmetic)] +#![allow(clippy::arithmetic_side_effects)] #![deny(missing_docs)] #![cfg_attr(not(test), forbid(unsafe_code))] diff --git a/program/src/processor.rs b/program/src/processor.rs index 7056f2e..6c9b0f4 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -1147,9 +1147,11 @@ mod tests { } #[test] - #[should_panic(expected = "Custom(3)")] - fn test_error_unwrap() { - Err::<(), ProgramError>(return_token_error_as_program_error()).unwrap(); + fn test_error_as_custom() { + assert_eq!( + return_token_error_as_program_error(), + ProgramError::Custom(3) + ); } #[test] From cce633e93f8d9d53440e4ece47c883d4d11b9609 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 28 Oct 2023 22:03:45 +0200 Subject: [PATCH 207/248] build(deps): bump num_enum from 0.7.0 to 0.7.1 (#5687) Bumps [num_enum](https://github.com/illicitonion/num_enum) from 0.7.0 to 0.7.1. - [Commits](https://github.com/illicitonion/num_enum/commits) --- updated-dependencies: - dependency-name: num_enum dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 51fac3e..17c1b4d 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -17,7 +17,7 @@ arrayref = "0.3.7" bytemuck = "1.14.0" num-derive = "0.4" num-traits = "0.2" -num_enum = "0.7.0" +num_enum = "0.7.1" solana-program = "1.17.2" thiserror = "1.0" From 17bfeedd409cd8091f59792569a65b6d844fb5ad Mon Sep 17 00:00:00 2001 From: Joe C Date: Wed, 8 Nov 2023 20:36:07 +0000 Subject: [PATCH 208/248] rustfmt: use entrypoint full path This PR swaps any calls to the `entrypoint!` macro with the full path, ie: `solana_program::entrypoint!`. This will play a role in the effort to introduce a linting standard to SPL. --- program/src/entrypoint.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/src/entrypoint.rs b/program/src/entrypoint.rs index 5331575..6d9ad5d 100644 --- a/program/src/entrypoint.rs +++ b/program/src/entrypoint.rs @@ -2,11 +2,11 @@ use crate::{error::TokenError, processor::Processor}; use solana_program::{ - account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, - program_error::PrintProgramError, pubkey::Pubkey, + account_info::AccountInfo, entrypoint::ProgramResult, program_error::PrintProgramError, + pubkey::Pubkey, }; -entrypoint!(process_instruction); +solana_program::entrypoint!(process_instruction); fn process_instruction( program_id: &Pubkey, accounts: &[AccountInfo], From 0881db4c323de859692bf1ef3c90a00385aa85ce Mon Sep 17 00:00:00 2001 From: Joe C Date: Wed, 8 Nov 2023 20:38:31 +0000 Subject: [PATCH 209/248] rustfmt: format imports This PR adds import formatting configurations to the repository's `rustfmt.toml` file, and the associated changes from `cargo +nightly fmt --all`. --- program/src/entrypoint.rs | 10 +++--- program/src/error.rs | 14 ++++---- program/src/instruction.rs | 19 ++++++----- program/src/native_mint.rs | 3 +- program/src/processor.rs | 68 ++++++++++++++++++++------------------ program/src/state.rs | 18 +++++----- 6 files changed, 71 insertions(+), 61 deletions(-) diff --git a/program/src/entrypoint.rs b/program/src/entrypoint.rs index 6d9ad5d..366ee78 100644 --- a/program/src/entrypoint.rs +++ b/program/src/entrypoint.rs @@ -1,9 +1,11 @@ //! Program entrypoint -use crate::{error::TokenError, processor::Processor}; -use solana_program::{ - account_info::AccountInfo, entrypoint::ProgramResult, program_error::PrintProgramError, - pubkey::Pubkey, +use { + crate::{error::TokenError, processor::Processor}, + solana_program::{ + account_info::AccountInfo, entrypoint::ProgramResult, program_error::PrintProgramError, + pubkey::Pubkey, + }, }; solana_program::entrypoint!(process_instruction); diff --git a/program/src/error.rs b/program/src/error.rs index 72461a0..a758d2c 100644 --- a/program/src/error.rs +++ b/program/src/error.rs @@ -1,12 +1,14 @@ //! Error types -use num_derive::FromPrimitive; -use solana_program::{ - decode_error::DecodeError, - msg, - program_error::{PrintProgramError, ProgramError}, +use { + num_derive::FromPrimitive, + solana_program::{ + decode_error::DecodeError, + msg, + program_error::{PrintProgramError, ProgramError}, + }, + thiserror::Error, }; -use thiserror::Error; /// Errors that may be returned by the Token program. #[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)] diff --git a/program/src/instruction.rs b/program/src/instruction.rs index e798abd..067fde5 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -1,15 +1,16 @@ //! Instruction types -use crate::{check_program_account, error::TokenError}; -use solana_program::{ - instruction::{AccountMeta, Instruction}, - program_error::ProgramError, - program_option::COption, - pubkey::Pubkey, - sysvar, +use { + crate::{check_program_account, error::TokenError}, + solana_program::{ + instruction::{AccountMeta, Instruction}, + program_error::ProgramError, + program_option::COption, + pubkey::Pubkey, + sysvar, + }, + std::{convert::TryInto, mem::size_of}, }; -use std::convert::TryInto; -use std::mem::size_of; /// Minimum number of multisignature signers (min N) pub const MIN_SIGNERS: usize = 1; diff --git a/program/src/native_mint.rs b/program/src/native_mint.rs index 0502f9f..37f7555 100644 --- a/program/src/native_mint.rs +++ b/program/src/native_mint.rs @@ -8,8 +8,7 @@ solana_program::declare_id!("So11111111111111111111111111111111111111112"); #[cfg(test)] mod tests { - use super::*; - use solana_program::native_token::*; + use {super::*, solana_program::native_token::*}; #[test] fn test_decimals() { diff --git a/program/src/processor.rs b/program/src/processor.rs index 6c9b0f4..a849382 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -1,24 +1,26 @@ //! Program state processor -use crate::{ - amount_to_ui_amount_string_trimmed, - error::TokenError, - instruction::{is_valid_signer_index, AuthorityType, TokenInstruction, MAX_SIGNERS}, - state::{Account, AccountState, Mint, Multisig}, - try_ui_amount_into_amount, -}; -use solana_program::{ - account_info::{next_account_info, AccountInfo}, - entrypoint::ProgramResult, - msg, - program::set_return_data, - program_error::ProgramError, - program_memory::sol_memcmp, - program_option::COption, - program_pack::{IsInitialized, Pack}, - pubkey::{Pubkey, PUBKEY_BYTES}, - system_program, - sysvar::{rent::Rent, Sysvar}, +use { + crate::{ + amount_to_ui_amount_string_trimmed, + error::TokenError, + instruction::{is_valid_signer_index, AuthorityType, TokenInstruction, MAX_SIGNERS}, + state::{Account, AccountState, Mint, Multisig}, + try_ui_amount_into_amount, + }, + solana_program::{ + account_info::{next_account_info, AccountInfo}, + entrypoint::ProgramResult, + msg, + program::set_return_data, + program_error::ProgramError, + program_memory::sol_memcmp, + program_option::COption, + program_pack::{IsInitialized, Pack}, + pubkey::{Pubkey, PUBKEY_BYTES}, + system_program, + sysvar::{rent::Rent, Sysvar}, + }, }; /// Program state handler. @@ -1028,20 +1030,22 @@ fn delete_account(account_info: &AccountInfo) -> Result<(), ProgramError> { #[cfg(test)] mod tests { - use super::*; - use crate::instruction::*; - use serial_test::serial; - use solana_program::{ - account_info::IntoAccountInfo, - clock::Epoch, - instruction::Instruction, - program_error::{self, PrintProgramError}, - sysvar::rent, - }; - use solana_sdk::account::{ - create_account_for_test, create_is_signer_account_infos, Account as SolanaAccount, + use { + super::*, + crate::instruction::*, + serial_test::serial, + solana_program::{ + account_info::IntoAccountInfo, + clock::Epoch, + instruction::Instruction, + program_error::{self, PrintProgramError}, + sysvar::rent, + }, + solana_sdk::account::{ + create_account_for_test, create_is_signer_account_infos, Account as SolanaAccount, + }, + std::sync::{Arc, RwLock}, }; - use std::sync::{Arc, RwLock}; lazy_static::lazy_static! { static ref EXPECTED_DATA: Arc>> = Arc::new(RwLock::new(Vec::new())); diff --git a/program/src/state.rs b/program/src/state.rs index 8723065..062daab 100644 --- a/program/src/state.rs +++ b/program/src/state.rs @@ -1,13 +1,15 @@ //! State transition types -use crate::instruction::MAX_SIGNERS; -use arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs}; -use num_enum::TryFromPrimitive; -use solana_program::{ - program_error::ProgramError, - program_option::COption, - program_pack::{IsInitialized, Pack, Sealed}, - pubkey::{Pubkey, PUBKEY_BYTES}, +use { + crate::instruction::MAX_SIGNERS, + arrayref::{array_mut_ref, array_ref, array_refs, mut_array_refs}, + num_enum::TryFromPrimitive, + solana_program::{ + program_error::ProgramError, + program_option::COption, + program_pack::{IsInitialized, Pack, Sealed}, + pubkey::{Pubkey, PUBKEY_BYTES}, + }, }; /// Mint data. From 70b89ae1fbcc132629449c3435c2e9e1d68cefd5 Mon Sep 17 00:00:00 2001 From: Joe C Date: Wed, 8 Nov 2023 20:41:36 +0000 Subject: [PATCH 210/248] rustfmt: format comments This PR adds comment formatting configurations to the repository's `rustfmt.toml` file, and the associated changes from `cargo +nightly fmt --all`. Comment width 80. --- program/src/instruction.rs | 56 +++++++++++++++++++++----------------- program/src/lib.rs | 21 ++++++++------ program/src/processor.rs | 18 ++++++++---- program/src/state.rs | 41 ++++++++++++++++------------ 4 files changed, 80 insertions(+), 56 deletions(-) diff --git a/program/src/instruction.rs b/program/src/instruction.rs index 067fde5..ede7308 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -36,7 +36,6 @@ pub enum TokenInstruction<'a> { /// /// 0. `[writable]` The mint to initialize. /// 1. `[]` Rent sysvar - /// InitializeMint { /// Number of base 10 digits to the right of the decimal place. decimals: u8, @@ -352,10 +351,10 @@ pub enum TokenInstruction<'a> { /// Expected number of base 10 digits to the right of the decimal place. decimals: u8, }, - /// Like InitializeAccount, but the owner pubkey is passed via instruction data - /// rather than the accounts list. This variant may be preferable when using - /// Cross Program Invocation from an instruction that does not need the owner's - /// `AccountInfo` otherwise. + /// Like InitializeAccount, but the owner pubkey is passed via instruction + /// data rather than the accounts list. This variant may be preferable + /// when using Cross Program Invocation from an instruction that does + /// not need the owner's `AccountInfo` otherwise. /// /// Accounts expected by this instruction: /// @@ -368,15 +367,17 @@ pub enum TokenInstruction<'a> { }, /// Given a wrapped / native token account (a token account containing SOL) /// updates its amount field based on the account's underlying `lamports`. - /// This is useful if a non-wrapped SOL account uses `system_instruction::transfer` - /// to move lamports to a wrapped token account, and needs to have its token - /// `amount` field updated. + /// This is useful if a non-wrapped SOL account uses + /// `system_instruction::transfer` to move lamports to a wrapped token + /// account, and needs to have its token `amount` field updated. /// /// Accounts expected by this instruction: /// - /// 0. `[writable]` The native token account to sync with its underlying lamports. + /// 0. `[writable]` The native token account to sync with its underlying + /// lamports. SyncNative, - /// Like InitializeAccount2, but does not require the Rent sysvar to be provided + /// Like InitializeAccount2, but does not require the Rent sysvar to be + /// provided /// /// Accounts expected by this instruction: /// @@ -386,7 +387,8 @@ pub enum TokenInstruction<'a> { /// The new account's owner/multisignature. owner: Pubkey, }, - /// Like InitializeMultisig, but does not require the Rent sysvar to be provided + /// Like InitializeMultisig, but does not require the Rent sysvar to be + /// provided /// /// Accounts expected by this instruction: /// @@ -398,12 +400,12 @@ pub enum TokenInstruction<'a> { /// account. m: u8, }, - /// Like [`InitializeMint`], but does not require the Rent sysvar to be provided + /// Like [`InitializeMint`], but does not require the Rent sysvar to be + /// provided /// /// Accounts expected by this instruction: /// /// 0. `[writable]` The mint to initialize. - /// InitializeMint2 { /// Number of base 10 digits to the right of the decimal place. decimals: u8, @@ -412,8 +414,8 @@ pub enum TokenInstruction<'a> { /// The freeze authority/multisignature of the mint. freeze_authority: COption, }, - /// Gets the required size of an account for the given mint as a little-endian - /// `u64`. + /// Gets the required size of an account for the given mint as a + /// little-endian `u64`. /// /// Return data can be fetched using `sol_get_return_data` and deserializing /// the return data as a little-endian `u64`. @@ -424,8 +426,8 @@ pub enum TokenInstruction<'a> { GetAccountDataSize, // typically, there's also data, but this program ignores it /// Initialize the Immutable Owner extension for the given token account /// - /// Fails if the account has already been initialized, so must be called before - /// `InitializeAccount`. + /// Fails if the account has already been initialized, so must be called + /// before `InitializeAccount`. /// /// No-ops in this version of the program, but is included for compatibility /// with the Associated Token Account program. @@ -437,13 +439,14 @@ pub enum TokenInstruction<'a> { /// Data expected by this instruction: /// None InitializeImmutableOwner, - /// Convert an Amount of tokens to a UiAmount `string`, using the given mint. - /// In this version of the program, the mint can only specify the number of decimals. + /// Convert an Amount of tokens to a UiAmount `string`, using the given + /// mint. In this version of the program, the mint can only specify the + /// number of decimals. /// /// Fails on an invalid mint. /// - /// Return data can be fetched using `sol_get_return_data` and deserialized with - /// `String::from_utf8`. + /// Return data can be fetched using `sol_get_return_data` and deserialized + /// with `String::from_utf8`. /// /// Accounts expected by this instruction: /// @@ -452,8 +455,9 @@ pub enum TokenInstruction<'a> { /// The amount of tokens to reformat. amount: u64, }, - /// Convert a UiAmount of tokens to a little-endian `u64` raw Amount, using the given mint. - /// In this version of the program, the mint can only specify the number of decimals. + /// Convert a UiAmount of tokens to a little-endian `u64` raw Amount, using + /// the given mint. In this version of the program, the mint can only + /// specify the number of decimals. /// /// Return data can be fetched using `sol_get_return_data` and deserializing /// the return data as a little-endian `u64`. @@ -470,7 +474,8 @@ pub enum TokenInstruction<'a> { // token/js/src/instructions/types.ts to maintain @solana/spl-token compatibility } impl<'a> TokenInstruction<'a> { - /// Unpacks a byte buffer into a [TokenInstruction](enum.TokenInstruction.html). + /// Unpacks a byte buffer into a + /// [TokenInstruction](enum.TokenInstruction.html). pub fn unpack(input: &'a [u8]) -> Result { use TokenError::InvalidInstruction; @@ -574,7 +579,8 @@ impl<'a> TokenInstruction<'a> { }) } - /// Packs a [TokenInstruction](enum.TokenInstruction.html) into a byte buffer. + /// Packs a [TokenInstruction](enum.TokenInstruction.html) into a byte + /// buffer. pub fn pack(&self) -> Vec { let mut buf = Vec::with_capacity(size_of::()); match self { diff --git a/program/src/lib.rs b/program/src/lib.rs index 1eb3855..25175df 100644 --- a/program/src/lib.rs +++ b/program/src/lib.rs @@ -13,22 +13,25 @@ pub mod state; #[cfg(not(feature = "no-entrypoint"))] mod entrypoint; -// Export current sdk types for downstream users building with a different sdk version +// Export current sdk types for downstream users building with a different sdk +// version pub use solana_program; use solana_program::{entrypoint::ProgramResult, program_error::ProgramError, pubkey::Pubkey}; -/// Convert the UI representation of a token amount (using the decimals field defined in its mint) -/// to the raw amount +/// Convert the UI representation of a token amount (using the decimals field +/// defined in its mint) to the raw amount pub fn ui_amount_to_amount(ui_amount: f64, decimals: u8) -> u64 { (ui_amount * 10_usize.pow(decimals as u32) as f64) as u64 } -/// Convert a raw amount to its UI representation (using the decimals field defined in its mint) +/// Convert a raw amount to its UI representation (using the decimals field +/// defined in its mint) pub fn amount_to_ui_amount(amount: u64, decimals: u8) -> f64 { amount as f64 / 10_usize.pow(decimals as u32) as f64 } -/// Convert a raw amount to its UI representation (using the decimals field defined in its mint) +/// Convert a raw amount to its UI representation (using the decimals field +/// defined in its mint) pub fn amount_to_ui_amount_string(amount: u64, decimals: u8) -> String { let decimals = decimals as usize; if decimals > 0 { @@ -53,12 +56,14 @@ pub fn amount_to_ui_amount_string_trimmed(amount: u64, decimals: u8) -> String { s } -/// Try to convert a UI representation of a token amount to its raw amount using the given decimals -/// field +/// Try to convert a UI representation of a token amount to its raw amount using +/// the given decimals field pub fn try_ui_amount_into_amount(ui_amount: String, decimals: u8) -> Result { let decimals = decimals as usize; let mut parts = ui_amount.split('.'); - let mut amount_str = parts.next().unwrap().to_string(); // splitting a string, even an empty one, will always yield an iterator of at least length == 1 + // splitting a string, even an empty one, will always yield an iterator of + // at least length == 1 + let mut amount_str = parts.next().unwrap().to_string(); let after_decimal = parts.next().unwrap_or(""); let after_decimal = after_decimal.trim_end_matches('0'); if (amount_str.is_empty() && after_decimal.is_empty()) diff --git a/program/src/processor.rs b/program/src/processor.rs index a849382..550df44 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -141,7 +141,8 @@ impl Processor { Ok(()) } - /// Processes an [InitializeAccount](enum.TokenInstruction.html) instruction. + /// Processes an [InitializeAccount](enum.TokenInstruction.html) + /// instruction. pub fn process_initialize_account( program_id: &Pubkey, accounts: &[AccountInfo], @@ -149,7 +150,8 @@ impl Processor { Self::_process_initialize_account(program_id, accounts, None, true) } - /// Processes an [InitializeAccount2](enum.TokenInstruction.html) instruction. + /// Processes an [InitializeAccount2](enum.TokenInstruction.html) + /// instruction. pub fn process_initialize_account2( program_id: &Pubkey, accounts: &[AccountInfo], @@ -158,7 +160,8 @@ impl Processor { Self::_process_initialize_account(program_id, accounts, Some(&owner), true) } - /// Processes an [InitializeAccount3](enum.TokenInstruction.html) instruction. + /// Processes an [InitializeAccount3](enum.TokenInstruction.html) + /// instruction. pub fn process_initialize_account3( program_id: &Pubkey, accounts: &[AccountInfo], @@ -209,12 +212,14 @@ impl Processor { Ok(()) } - /// Processes a [InitializeMultisig](enum.TokenInstruction.html) instruction. + /// Processes a [InitializeMultisig](enum.TokenInstruction.html) + /// instruction. pub fn process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult { Self::_process_initialize_multisig(accounts, m, true) } - /// Processes a [InitializeMultisig2](enum.TokenInstruction.html) instruction. + /// Processes a [InitializeMultisig2](enum.TokenInstruction.html) + /// instruction. pub fn process_initialize_multisig2(accounts: &[AccountInfo], m: u8) -> ProgramResult { Self::_process_initialize_multisig(accounts, m, false) } @@ -789,7 +794,8 @@ impl Processor { Ok(()) } - /// Processes an [InitializeImmutableOwner](enum.TokenInstruction.html) instruction + /// Processes an [InitializeImmutableOwner](enum.TokenInstruction.html) + /// instruction pub fn process_initialize_immutable_owner(accounts: &[AccountInfo]) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let token_account_info = next_account_info(account_info_iter)?; diff --git a/program/src/state.rs b/program/src/state.rs index 062daab..fe23fba 100644 --- a/program/src/state.rs +++ b/program/src/state.rs @@ -16,9 +16,10 @@ use { #[repr(C)] #[derive(Clone, Copy, Debug, Default, PartialEq)] pub struct Mint { - /// Optional authority used to mint new tokens. The mint authority may only be provided during - /// mint creation. If no mint authority is present then the mint has a fixed supply and no - /// further tokens may be minted. + /// Optional authority used to mint new tokens. The mint authority may only + /// be provided during mint creation. If no mint authority is present + /// then the mint has a fixed supply and no further tokens may be + /// minted. pub mint_authority: COption, /// Total supply of tokens. pub supply: u64, @@ -97,9 +98,10 @@ pub struct Account { pub delegate: COption, /// The account's state pub state: AccountState, - /// If is_native.is_some, this is a native token, and the value logs the rent-exempt reserve. An - /// Account is required to be rent-exempt, so the value is used by the Processor to ensure that - /// wrapped SOL accounts do not drop below this threshold. + /// If is_native.is_some, this is a native token, and the value logs the + /// rent-exempt reserve. An Account is required to be rent-exempt, so + /// the value is used by the Processor to ensure that wrapped SOL + /// accounts do not drop below this threshold. pub is_native: COption, /// The amount delegated pub delegated_amount: u64, @@ -115,7 +117,8 @@ impl Account { pub fn is_native(&self) -> bool { self.is_native.is_some() } - /// Checks if a token Account's owner is the system_program or the incinerator + /// Checks if a token Account's owner is the system_program or the + /// incinerator pub fn is_owned_by_system_program_or_incinerator(&self) -> bool { solana_program::system_program::check_id(&self.owner) || solana_program::incinerator::check_id(&self.owner) @@ -185,11 +188,12 @@ pub enum AccountState { /// Account is not yet initialized #[default] Uninitialized, - /// Account is initialized; the account owner and/or delegate may perform permitted operations - /// on this account + /// Account is initialized; the account owner and/or delegate may perform + /// permitted operations on this account Initialized, - /// Account has been frozen by the mint freeze authority. Neither the account owner nor - /// the delegate are able to perform operations on this account. + /// Account has been frozen by the mint freeze authority. Neither the + /// account owner nor the delegate are able to perform operations on + /// this account. Frozen, } @@ -292,24 +296,27 @@ fn unpack_coption_u64(src: &[u8; 12]) -> Result, ProgramError> { const SPL_TOKEN_ACCOUNT_MINT_OFFSET: usize = 0; const SPL_TOKEN_ACCOUNT_OWNER_OFFSET: usize = 32; -/// A trait for token Account structs to enable efficiently unpacking various fields -/// without unpacking the complete state. +/// A trait for token Account structs to enable efficiently unpacking various +/// fields without unpacking the complete state. pub trait GenericTokenAccount { /// Check if the account data is a valid token account fn valid_account_data(account_data: &[u8]) -> bool; - /// Call after account length has already been verified to unpack the account owner + /// Call after account length has already been verified to unpack the + /// account owner fn unpack_account_owner_unchecked(account_data: &[u8]) -> &Pubkey { Self::unpack_pubkey_unchecked(account_data, SPL_TOKEN_ACCOUNT_OWNER_OFFSET) } - /// Call after account length has already been verified to unpack the account mint + /// Call after account length has already been verified to unpack the + /// account mint fn unpack_account_mint_unchecked(account_data: &[u8]) -> &Pubkey { Self::unpack_pubkey_unchecked(account_data, SPL_TOKEN_ACCOUNT_MINT_OFFSET) } - /// Call after account length has already been verified to unpack a Pubkey at - /// the specified offset. Panics if `account_data.len()` is less than `PUBKEY_BYTES` + /// Call after account length has already been verified to unpack a Pubkey + /// at the specified offset. Panics if `account_data.len()` is less than + /// `PUBKEY_BYTES` fn unpack_pubkey_unchecked(account_data: &[u8], offset: usize) -> &Pubkey { bytemuck::from_bytes(&account_data[offset..offset + PUBKEY_BYTES]) } From 66c51aa81b0718b923c3c724eef9b7c103d34edc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 20:55:40 +0100 Subject: [PATCH 211/248] build(deps): bump proptest from 1.3.1 to 1.4.0 (#5812) Bumps [proptest](https://github.com/proptest-rs/proptest) from 1.3.1 to 1.4.0. - [Release notes](https://github.com/proptest-rs/proptest/releases) - [Changelog](https://github.com/proptest-rs/proptest/blob/master/CHANGELOG.md) - [Commits](https://github.com/proptest-rs/proptest/compare/v1.3.1...v1.4.0) --- updated-dependencies: - dependency-name: proptest dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 17c1b4d..a8ec01b 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -23,7 +23,7 @@ thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" -proptest = "1.3" +proptest = "1.4" serial_test = "2.0.0" solana-program-test = "1.17.2" solana-sdk = "1.17.2" From e32879aea33eee4385441369614759a75baa6a63 Mon Sep 17 00:00:00 2001 From: Jon Cinque Date: Wed, 29 Nov 2023 12:55:07 +0100 Subject: [PATCH 212/248] repo: Update to 1.17.6 (#5863) * repo: Update to 1.17.6 with script * Update lockfile * Remove disabling feature in token-cli tests --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index a8ec01b..ca9207a 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,15 +18,15 @@ bytemuck = "1.14.0" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.1" -solana-program = "1.17.2" +solana-program = "1.17.6" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" proptest = "1.4" serial_test = "2.0.0" -solana-program-test = "1.17.2" -solana-sdk = "1.17.2" +solana-program-test = "1.17.6" +solana-sdk = "1.17.6" [lib] crate-type = ["cdylib", "lib"] From d6ebde89aca74d0f2cfa114be54eac6ac96488fa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 13:02:56 +0100 Subject: [PATCH 213/248] build(deps): bump num_enum from 0.7.1 to 0.7.2 (#6072) Bumps [num_enum](https://github.com/illicitonion/num_enum) from 0.7.1 to 0.7.2. - [Commits](https://github.com/illicitonion/num_enum/compare/0.7.1...0.7.2) --- updated-dependencies: - dependency-name: num_enum dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index ca9207a..0ffa2f1 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -17,7 +17,7 @@ arrayref = "0.3.7" bytemuck = "1.14.0" num-derive = "0.4" num-traits = "0.2" -num_enum = "0.7.1" +num_enum = "0.7.2" solana-program = "1.17.6" thiserror = "1.0" From 5f604063b38051b8c44f282bc2b4d4e2581f6cf6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 13:56:47 +0100 Subject: [PATCH 214/248] build(deps): bump serial_test from 2.0.0 to 3.0.0 (#6073) Bumps [serial_test](https://github.com/palfrey/serial_test) from 2.0.0 to 3.0.0. - [Release notes](https://github.com/palfrey/serial_test/releases) - [Commits](https://github.com/palfrey/serial_test/compare/v2.0.0...v3.0.0) --- updated-dependencies: - dependency-name: serial_test dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 0ffa2f1..1131aa2 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -24,7 +24,7 @@ thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" proptest = "1.4" -serial_test = "2.0.0" +serial_test = "3.0.0" solana-program-test = "1.17.6" solana-sdk = "1.17.6" From 7d2abc66d736f8d1e42afd769f54649020ac5782 Mon Sep 17 00:00:00 2001 From: Emanuele Cesena Date: Wed, 17 Jan 2024 17:03:22 -0800 Subject: [PATCH 215/248] token 2022: upgrade sdk to 1.17.13 (#6147) * token 2022: upgrade sdk to 1.17.13 * Upgrade all remaining packages --------- Co-authored-by: Jon C --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 1131aa2..6aca0f2 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,15 +18,15 @@ bytemuck = "1.14.0" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.2" -solana-program = "1.17.6" +solana-program = "1.17.13" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" proptest = "1.4" serial_test = "3.0.0" -solana-program-test = "1.17.6" -solana-sdk = "1.17.6" +solana-program-test = "1.17.13" +solana-sdk = "1.17.13" [lib] crate-type = ["cdylib", "lib"] From 65122b7f7799d2ac144a71b49c793c2eb4ba569b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 25 Jan 2024 11:58:50 +0100 Subject: [PATCH 216/248] build(deps): bump bytemuck from 1.14.0 to 1.14.1 (#6179) Bumps [bytemuck](https://github.com/Lokathor/bytemuck) from 1.14.0 to 1.14.1. - [Changelog](https://github.com/Lokathor/bytemuck/blob/main/changelog.md) - [Commits](https://github.com/Lokathor/bytemuck/compare/v1.14.0...v1.14.1) --- updated-dependencies: - dependency-name: bytemuck dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 6aca0f2..c72c9a0 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -14,7 +14,7 @@ test-sbf = [] [dependencies] arrayref = "0.3.7" -bytemuck = "1.14.0" +bytemuck = "1.14.1" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.2" From 0a9a4a301956ddd2042d1012a4d34e49e5aaaf07 Mon Sep 17 00:00:00 2001 From: Will Hickey Date: Fri, 26 Jan 2024 15:31:55 -0600 Subject: [PATCH 217/248] Update solana dependency version to allow 2.0.0 (#6182) * Update solana dependency version to allow 2.0.0 * Fix version number in solana-version.sh * Fix version numbers * Revert solana-version.sh with note * Revert Anchor version change * Relax dependency version upper bound to 2 --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index c72c9a0..7c5c43d 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,15 +18,15 @@ bytemuck = "1.14.1" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.2" -solana-program = "1.17.13" +solana-program = ">=1.17.13,<=2" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" proptest = "1.4" serial_test = "3.0.0" -solana-program-test = "1.17.13" -solana-sdk = "1.17.13" +solana-program-test = ">=1.17.13,<=2" +solana-sdk = ">=1.17.13,<=2" [lib] crate-type = ["cdylib", "lib"] From 8c1f145dca8bda03fa2d5bfa39afc105162f7719 Mon Sep 17 00:00:00 2001 From: samkim-crypto Date: Thu, 1 Feb 2024 10:09:45 +0900 Subject: [PATCH 218/248] Upgrade to solana 1.17.17 (#6189) * upgrade to solana version 1.17.17 * add display for the group extensions * upgrade to solana version 1.17.17 * update cargo lock --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 7c5c43d..012e97d 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,15 +18,15 @@ bytemuck = "1.14.1" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.2" -solana-program = ">=1.17.13,<=2" +solana-program = ">=1.17.17,<=2" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" proptest = "1.4" serial_test = "3.0.0" -solana-program-test = ">=1.17.13,<=2" -solana-sdk = ">=1.17.13,<=2" +solana-program-test = ">=1.17.17,<=2" +solana-sdk = ">=1.17.17,<=2" [lib] crate-type = ["cdylib", "lib"] From 5ce35f27d04ff9e545ad949f3677d3e1a7633d06 Mon Sep 17 00:00:00 2001 From: Will Hickey Date: Mon, 5 Feb 2024 14:52:18 -0600 Subject: [PATCH 219/248] Bump SPL crate versions (#6221) * Update associated-token-account version to 2.3.1 * Update discriminator version to 0.1.1 * Update discriminator-derive version to 0.1.2 * Update discriminator-syn version to 0.1.2 * Update instruction-padding version to 0.1.1 * Update memo version to 4.0.1 * Update pod version to 0.1.1 * Update program-error version to 0.3.1 * Update program-error-derive version to 0.3.2 * Update tlv-account-resolution version to 0.5.2 * Update token version to 4.0.1 * Cargo.lock * Update token-group-interface version to 0.1.1 * Update token-metadata-interface version to 0.2.1 * Update transfer-hook-interface version to 0.5.1 * Update type-length-value version to 0.3.1 * Cargo.lock * Remove extraneous whitespace --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 012e97d..2961ad4 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-token" -version = "4.0.0" +version = "4.0.1" description = "Solana Program Library Token" authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" From d058ff4bea9b00045d7f7d319230db0f1878c8e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 7 Feb 2024 12:03:05 -0500 Subject: [PATCH 220/248] build(deps): bump bytemuck from 1.14.1 to 1.14.2 (#6231) Bumps [bytemuck](https://github.com/Lokathor/bytemuck) from 1.14.1 to 1.14.2. - [Changelog](https://github.com/Lokathor/bytemuck/blob/main/changelog.md) - [Commits](https://github.com/Lokathor/bytemuck/compare/v1.14.1...v1.14.2) --- updated-dependencies: - dependency-name: bytemuck dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 2961ad4..68f6653 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -14,7 +14,7 @@ test-sbf = [] [dependencies] arrayref = "0.3.7" -bytemuck = "1.14.1" +bytemuck = "1.14.2" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.2" From 6be17fec0a732a97ea5c2d8680447238bb818bde Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 9 Feb 2024 19:01:35 -0500 Subject: [PATCH 221/248] build(deps): bump bytemuck from 1.14.2 to 1.14.3 (#6240) Bumps [bytemuck](https://github.com/Lokathor/bytemuck) from 1.14.2 to 1.14.3. - [Changelog](https://github.com/Lokathor/bytemuck/blob/main/changelog.md) - [Commits](https://github.com/Lokathor/bytemuck/compare/v1.14.2...v1.14.3) --- updated-dependencies: - dependency-name: bytemuck dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 68f6653..4636f5c 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -14,7 +14,7 @@ test-sbf = [] [dependencies] arrayref = "0.3.7" -bytemuck = "1.14.2" +bytemuck = "1.14.3" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.2" From 7db846d307201e6281dabda4047c8f9b54c35b94 Mon Sep 17 00:00:00 2001 From: samkim-crypto Date: Thu, 29 Feb 2024 10:06:27 +0900 Subject: [PATCH 222/248] Upgrade to Solana 1.18.2 (#6278) * upgrade to solana 1.18.2 * upgrade rust version to 1.76.0 * update borsh to 1.2.1 * replace deprecated `try_to_vec` with `borsh::to_vec` * temporarily allow deprecated functions in cli that uses clap-v3 * use `repr(u32)` for enums in token lending --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 4636f5c..8f9a059 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,15 +18,15 @@ bytemuck = "1.14.3" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.2" -solana-program = ">=1.17.17,<=2" +solana-program = ">=1.18.2,<=2" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" proptest = "1.4" serial_test = "3.0.0" -solana-program-test = ">=1.17.17,<=2" -solana-sdk = ">=1.17.17,<=2" +solana-program-test = ">=1.18.2,<=2" +solana-sdk = ">=1.18.2,<=2" [lib] crate-type = ["cdylib", "lib"] From 56f154e9fdae9ac27aee0aab28b65aeebea8be53 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Mar 2024 12:38:01 +0100 Subject: [PATCH 223/248] build(deps): bump bytemuck from 1.14.3 to 1.15.0 (#6415) Bumps [bytemuck](https://github.com/Lokathor/bytemuck) from 1.14.3 to 1.15.0. - [Changelog](https://github.com/Lokathor/bytemuck/blob/main/changelog.md) - [Commits](https://github.com/Lokathor/bytemuck/compare/v1.14.3...v1.15.0) --- updated-dependencies: - dependency-name: bytemuck dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 8f9a059..90f6941 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -14,7 +14,7 @@ test-sbf = [] [dependencies] arrayref = "0.3.7" -bytemuck = "1.14.3" +bytemuck = "1.15.0" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.2" From b98e0c639ea25319c5842ba1f502bb811bec3806 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 13:26:59 +0200 Subject: [PATCH 224/248] build(deps): bump serial_test from 3.0.0 to 3.1.0 (#6606) Bumps [serial_test](https://github.com/palfrey/serial_test) from 3.0.0 to 3.1.0. - [Release notes](https://github.com/palfrey/serial_test/releases) - [Commits](https://github.com/palfrey/serial_test/compare/v3.0.0...v3.1.0) --- updated-dependencies: - dependency-name: serial_test dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 90f6941..47053b9 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -24,7 +24,7 @@ thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" proptest = "1.4" -serial_test = "3.0.0" +serial_test = "3.1.0" solana-program-test = ">=1.18.2,<=2" solana-sdk = ">=1.18.2,<=2" From 1004806752a0be56436edd57828eb74a99a8ba89 Mon Sep 17 00:00:00 2001 From: samkim-crypto Date: Wed, 24 Apr 2024 12:01:47 +0900 Subject: [PATCH 225/248] Bump solana version to 1.18.11 (#6624) upgrade solana version to 1.18.11 --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 47053b9..afa5627 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,15 +18,15 @@ bytemuck = "1.15.0" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.2" -solana-program = ">=1.18.2,<=2" +solana-program = ">=1.18.11,<=2" thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" proptest = "1.4" serial_test = "3.1.0" -solana-program-test = ">=1.18.2,<=2" -solana-sdk = ">=1.18.2,<=2" +solana-program-test = ">=1.18.11,<=2" +solana-sdk = ">=1.18.11,<=2" [lib] crate-type = ["cdylib", "lib"] From 1b1810a563815d60867429c7a489239e554b44ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 12:51:53 +0200 Subject: [PATCH 226/248] build(deps): bump serial_test from 3.1.0 to 3.1.1 (#6649) Bumps [serial_test](https://github.com/palfrey/serial_test) from 3.1.0 to 3.1.1. - [Release notes](https://github.com/palfrey/serial_test/releases) - [Commits](https://github.com/palfrey/serial_test/compare/v3.1.0...v3.1.1) --- updated-dependencies: - dependency-name: serial_test dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index afa5627..e310bf3 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -24,7 +24,7 @@ thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" proptest = "1.4" -serial_test = "3.1.0" +serial_test = "3.1.1" solana-program-test = ">=1.18.11,<=2" solana-sdk = ">=1.18.11,<=2" From 077265b7d366f1a3614e4becb7733f258a52356f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 14 May 2024 22:20:09 +0200 Subject: [PATCH 227/248] build(deps): bump bytemuck from 1.15.0 to 1.16.0 (#6729) Bumps [bytemuck](https://github.com/Lokathor/bytemuck) from 1.15.0 to 1.16.0. - [Changelog](https://github.com/Lokathor/bytemuck/blob/main/changelog.md) - [Commits](https://github.com/Lokathor/bytemuck/compare/v1.15.0...v1.16.0) --- updated-dependencies: - dependency-name: bytemuck dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index e310bf3..1ff5b08 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -14,7 +14,7 @@ test-sbf = [] [dependencies] arrayref = "0.3.7" -bytemuck = "1.15.0" +bytemuck = "1.16.0" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.2" From a0492c77609fd93133749f947d030f6f40ce1c8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Jun 2024 15:04:54 +0200 Subject: [PATCH 228/248] build(deps): bump bytemuck from 1.16.0 to 1.16.1 (#6875) Bumps [bytemuck](https://github.com/Lokathor/bytemuck) from 1.16.0 to 1.16.1. - [Changelog](https://github.com/Lokathor/bytemuck/blob/main/changelog.md) - [Commits](https://github.com/Lokathor/bytemuck/compare/v1.16.0...v1.16.1) --- updated-dependencies: - dependency-name: bytemuck dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 1ff5b08..07f8803 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -14,7 +14,7 @@ test-sbf = [] [dependencies] arrayref = "0.3.7" -bytemuck = "1.16.0" +bytemuck = "1.16.1" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.2" From 5c61290a9ed198f60cb54a8bb05d5f281f517319 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 13:29:48 +0200 Subject: [PATCH 229/248] build(deps): bump proptest from 1.4.0 to 1.5.0 (#6899) Bumps [proptest](https://github.com/proptest-rs/proptest) from 1.4.0 to 1.5.0. - [Release notes](https://github.com/proptest-rs/proptest/releases) - [Changelog](https://github.com/proptest-rs/proptest/blob/master/CHANGELOG.md) - [Commits](https://github.com/proptest-rs/proptest/compare/v1.4.0...v1.5.0) --- updated-dependencies: - dependency-name: proptest dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 07f8803..a8c3101 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -23,7 +23,7 @@ thiserror = "1.0" [dev-dependencies] lazy_static = "1.4.0" -proptest = "1.4" +proptest = "1.5" serial_test = "3.1.1" solana-program-test = ">=1.18.11,<=2" solana-sdk = ">=1.18.11,<=2" From 3084c4f5dbbb5c14b50b0d1f75913929e1d7656e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 13:59:52 +0200 Subject: [PATCH 230/248] build(deps): bump lazy_static from 1.4.0 to 1.5.0 (#6900) Bumps [lazy_static](https://github.com/rust-lang-nursery/lazy-static.rs) from 1.4.0 to 1.5.0. - [Release notes](https://github.com/rust-lang-nursery/lazy-static.rs/releases) - [Commits](https://github.com/rust-lang-nursery/lazy-static.rs/compare/1.4.0...1.5.0) --- updated-dependencies: - dependency-name: lazy_static dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index a8c3101..16a99e1 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -22,7 +22,7 @@ solana-program = ">=1.18.11,<=2" thiserror = "1.0" [dev-dependencies] -lazy_static = "1.4.0" +lazy_static = "1.5.0" proptest = "1.5" serial_test = "3.1.1" solana-program-test = ">=1.18.11,<=2" From 45f05507b478515a6ccbb1704fb06e35f29108ec Mon Sep 17 00:00:00 2001 From: Jon C Date: Mon, 24 Jun 2024 14:37:09 +0200 Subject: [PATCH 231/248] token: Update to v5 for solana-program 2.0 compatibility (#6907) Updating to allow for version <= 2.0 of the solana crates can appear as a breaking change for new projects, since cargo pulls in the latest version of all dependencies, and once there's an attempt to mix v1 and v2, downstream users will see errors. This is made worse that the patch versions will quietly pick up the v2 dependency, so if someone doesn't know how to manipulate a cargo lockfile, they will get build errors and won't know how to resolve them. All other SPL crates contain a new breaking version (new major for crates on v1 or more, new minor for crates on v0.X), except for spl-token. Bump spl-token to v5. --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 16a99e1..c4336be 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-token" -version = "4.0.1" +version = "5.0.0" description = "Solana Program Library Token" authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" From 6d5f4ee7a74a5d2c21b8425350526fa28efc64d6 Mon Sep 17 00:00:00 2001 From: Jon C Date: Tue, 25 Jun 2024 11:11:23 +0200 Subject: [PATCH 232/248] deps: Upgrade to Solana v2 (#6908) * solana: Update deps to 2.0.0 * Update lockfile * Update toolchain version * Update nightly version * Fix check issues * Fix clippy * Revert upgrade for account compression libs * Install build deps for serde test * Fixup versions * Fixup twoxtx build * Revert solana install version * Actually, get version 2.0.0 from the correct place --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index c4336be..ffc5c1b 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,15 +18,15 @@ bytemuck = "1.16.1" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.2" -solana-program = ">=1.18.11,<=2" +solana-program = "2.0.0" thiserror = "1.0" [dev-dependencies] lazy_static = "1.5.0" proptest = "1.5" serial_test = "3.1.1" -solana-program-test = ">=1.18.11,<=2" -solana-sdk = ">=1.18.11,<=2" +solana-program-test = "2.0.0" +solana-sdk = "2.0.0" [lib] crate-type = ["cdylib", "lib"] From 5f518eb584f7e6abaa5a95a3862efa8ac8c4c173 Mon Sep 17 00:00:00 2001 From: Jon C Date: Tue, 25 Jun 2024 13:17:22 +0200 Subject: [PATCH 233/248] token: Bump to v6 for Solana v2 compatibility (#6918) --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index ffc5c1b..a375400 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-token" -version = "5.0.0" +version = "6.0.0" description = "Solana Program Library Token" authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" From 3a964cbc1ed0bfe3bfbad786c17b4054b20443c5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 13:14:12 +0200 Subject: [PATCH 234/248] build(deps): bump arrayref from 0.3.7 to 0.3.8 (#7034) Bumps [arrayref](https://github.com/droundy/arrayref) from 0.3.7 to 0.3.8. - [Commits](https://github.com/droundy/arrayref/commits) --- updated-dependencies: - dependency-name: arrayref dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index a375400..077bdfe 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -13,7 +13,7 @@ no-entrypoint = [] test-sbf = [] [dependencies] -arrayref = "0.3.7" +arrayref = "0.3.8" bytemuck = "1.16.1" num-derive = "0.4" num-traits = "0.2" From fa46561194c0d15827a740442b96618e55a0d6d3 Mon Sep 17 00:00:00 2001 From: Jon C Date: Thu, 25 Jul 2024 16:53:13 +0200 Subject: [PATCH 235/248] ci: Bump crates to Solana 2.0.3 (#7047) * Run script * Update lockfile * Use "processed" instead of deprecated "recent" * Fixup account compression tests * account-compression: Remove `only` in test --- program/Cargo.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 077bdfe..fbc2575 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,15 +18,15 @@ bytemuck = "1.16.1" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.2" -solana-program = "2.0.0" +solana-program = "2.0.3" thiserror = "1.0" [dev-dependencies] lazy_static = "1.5.0" proptest = "1.5" serial_test = "3.1.1" -solana-program-test = "2.0.0" -solana-sdk = "2.0.0" +solana-program-test = "2.0.3" +solana-sdk = "2.0.3" [lib] crate-type = ["cdylib", "lib"] From 2365a60b141097ba183351ccf198e5a8bb54fe2c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 30 Jul 2024 11:33:42 +0200 Subject: [PATCH 236/248] build(deps): bump num_enum from 0.7.2 to 0.7.3 (#7071) Bumps [num_enum](https://github.com/illicitonion/num_enum) from 0.7.2 to 0.7.3. - [Commits](https://github.com/illicitonion/num_enum/compare/0.7.2...0.7.3) --- updated-dependencies: - dependency-name: num_enum dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index fbc2575..183e081 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -17,7 +17,7 @@ arrayref = "0.3.8" bytemuck = "1.16.1" num-derive = "0.4" num-traits = "0.2" -num_enum = "0.7.2" +num_enum = "0.7.3" solana-program = "2.0.3" thiserror = "1.0" From f89f880017a82cde3b7e8e5a0ed3cd3e76101149 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Jul 2024 11:36:08 +0200 Subject: [PATCH 237/248] build(deps): bump bytemuck from 1.16.1 to 1.16.3 (#7077) Bumps [bytemuck](https://github.com/Lokathor/bytemuck) from 1.16.1 to 1.16.3. - [Changelog](https://github.com/Lokathor/bytemuck/blob/main/changelog.md) - [Commits](https://github.com/Lokathor/bytemuck/compare/v1.16.1...v1.16.3) --- updated-dependencies: - dependency-name: bytemuck dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 183e081..2c46629 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -14,7 +14,7 @@ test-sbf = [] [dependencies] arrayref = "0.3.8" -bytemuck = "1.16.1" +bytemuck = "1.16.3" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.3" From a67fe61d08a7cde69eacc2c0a989e477a1194780 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 Aug 2024 08:48:26 -0400 Subject: [PATCH 238/248] build(deps): bump bytemuck from 1.16.3 to 1.17.0 (#7159) Bumps [bytemuck](https://github.com/Lokathor/bytemuck) from 1.16.3 to 1.17.0. - [Changelog](https://github.com/Lokathor/bytemuck/blob/main/changelog.md) - [Commits](https://github.com/Lokathor/bytemuck/compare/v1.16.3...v1.17.0) --- updated-dependencies: - dependency-name: bytemuck dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 2c46629..50d5de2 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -14,7 +14,7 @@ test-sbf = [] [dependencies] arrayref = "0.3.8" -bytemuck = "1.16.3" +bytemuck = "1.17.0" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.3" From bbc8ddf7a7662a82313f54caf22493f649161ea7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 11:46:28 +0200 Subject: [PATCH 239/248] build(deps): bump bytemuck from 1.17.0 to 1.17.1 (#7209) Bumps [bytemuck](https://github.com/Lokathor/bytemuck) from 1.17.0 to 1.17.1. - [Changelog](https://github.com/Lokathor/bytemuck/blob/main/changelog.md) - [Commits](https://github.com/Lokathor/bytemuck/compare/v1.17.0...v1.17.1) --- updated-dependencies: - dependency-name: bytemuck dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 50d5de2..7276e2f 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -14,7 +14,7 @@ test-sbf = [] [dependencies] arrayref = "0.3.8" -bytemuck = "1.17.0" +bytemuck = "1.17.1" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.3" From e88da38f13dc95fc30957ed810d6217589f674f6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Sep 2024 12:27:55 +0200 Subject: [PATCH 240/248] build(deps): bump bytemuck from 1.17.1 to 1.18.0 (#7244) Bumps [bytemuck](https://github.com/Lokathor/bytemuck) from 1.17.1 to 1.18.0. - [Changelog](https://github.com/Lokathor/bytemuck/blob/main/changelog.md) - [Commits](https://github.com/Lokathor/bytemuck/compare/v1.17.1...v1.18.0) --- updated-dependencies: - dependency-name: bytemuck dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 7276e2f..f3e3658 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -14,7 +14,7 @@ test-sbf = [] [dependencies] arrayref = "0.3.8" -bytemuck = "1.17.1" +bytemuck = "1.18.0" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.3" From 462b879a3433ce463cddb78fade59e4a314f44ab Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2024 00:38:10 +0800 Subject: [PATCH 241/248] build(deps): bump arrayref from 0.3.8 to 0.3.9 (#7278) Bumps [arrayref](https://github.com/droundy/arrayref) from 0.3.8 to 0.3.9. - [Commits](https://github.com/droundy/arrayref/commits) --- updated-dependencies: - dependency-name: arrayref dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index f3e3658..a912314 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -13,7 +13,7 @@ no-entrypoint = [] test-sbf = [] [dependencies] -arrayref = "0.3.8" +arrayref = "0.3.9" bytemuck = "1.18.0" num-derive = "0.4" num-traits = "0.2" From f76cc719660087846b9a821e53291a658c19df57 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 14 Oct 2024 12:59:51 +0200 Subject: [PATCH 242/248] build(deps): bump bytemuck from 1.18.0 to 1.19.0 (#7345) Bumps [bytemuck](https://github.com/Lokathor/bytemuck) from 1.18.0 to 1.19.0. - [Changelog](https://github.com/Lokathor/bytemuck/blob/main/changelog.md) - [Commits](https://github.com/Lokathor/bytemuck/compare/v1.18.0...v1.19.0) --- updated-dependencies: - dependency-name: bytemuck dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index a912314..67b92f3 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -14,7 +14,7 @@ test-sbf = [] [dependencies] arrayref = "0.3.9" -bytemuck = "1.18.0" +bytemuck = "1.19.0" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.3" From b6053f02a5b2c9a434f5214c5f7acb60a2744b78 Mon Sep 17 00:00:00 2001 From: Jon C Date: Thu, 31 Oct 2024 12:25:30 +0100 Subject: [PATCH 243/248] CI: Update to Solana v2.1 crates (#7416) * Run update script, update curve25519-dalek dep, rust * Run clippy + fmt * Add workspace lints, start fixing doc comments * Update doc comments for clippy * Re-run cargo fmt after doc comment update * Update solana-version * Update CI jobs --- program/Cargo.toml | 9 ++++++--- program/tests/assert_instruction_count.rs | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 67b92f3..3b363fb 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -18,18 +18,21 @@ bytemuck = "1.19.0" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.3" -solana-program = "2.0.3" +solana-program = "2.1.0" thiserror = "1.0" [dev-dependencies] lazy_static = "1.5.0" proptest = "1.5" serial_test = "3.1.1" -solana-program-test = "2.0.3" -solana-sdk = "2.0.3" +solana-program-test = "2.1.0" +solana-sdk = "2.1.0" [lib] crate-type = ["cdylib", "lib"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] + +[lints] +workspace = true diff --git a/program/tests/assert_instruction_count.rs b/program/tests/assert_instruction_count.rs index a6df337..009fb1e 100644 --- a/program/tests/assert_instruction_count.rs +++ b/program/tests/assert_instruction_count.rs @@ -23,7 +23,7 @@ const TRANSFER_AMOUNT: u64 = 1_000_000_000_000_000; async fn initialize_mint() { let mut pt = ProgramTest::new("spl_token", id(), processor!(Processor::process)); pt.set_compute_max_units(5_000); // last known 2252 - let (mut banks_client, payer, recent_blockhash) = pt.start().await; + let (banks_client, payer, recent_blockhash) = pt.start().await; let owner_key = Pubkey::new_unique(); let mint = Keypair::new(); From c96c2362f7c5c6bcb6584b3f8e50e556e88407c9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2024 15:31:50 +0000 Subject: [PATCH 244/248] Publish spl-token v7.0.0 --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 3b363fb..c424044 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "spl-token" -version = "6.0.0" +version = "7.0.0" description = "Solana Program Library Token" authors = ["Solana Labs Maintainers "] repository = "https://github.com/solana-labs/solana-program-library" From fb6732de2fb33210f6255c7adaeacd118867e5e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 6 Nov 2024 11:44:32 +0100 Subject: [PATCH 245/248] build(deps): bump thiserror from 1.0.68 to 2.0.0 (#7462) Bumps [thiserror](https://github.com/dtolnay/thiserror) from 1.0.68 to 2.0.0. - [Release notes](https://github.com/dtolnay/thiserror/releases) - [Commits](https://github.com/dtolnay/thiserror/compare/1.0.68...2.0.0) --- updated-dependencies: - dependency-name: thiserror dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index c424044..c336eda 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -19,7 +19,7 @@ num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.3" solana-program = "2.1.0" -thiserror = "1.0" +thiserror = "2.0" [dev-dependencies] lazy_static = "1.5.0" From 8050ce7289eca65e6ae39d4f238b93c8e29e5b65 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Nov 2024 15:21:44 +0100 Subject: [PATCH 246/248] build(deps): bump serial_test from 3.1.1 to 3.2.0 (#7481) Bumps [serial_test](https://github.com/palfrey/serial_test) from 3.1.1 to 3.2.0. - [Release notes](https://github.com/palfrey/serial_test/releases) - [Commits](https://github.com/palfrey/serial_test/compare/v3.1.1...v3.2.0) --- updated-dependencies: - dependency-name: serial_test dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index c336eda..93aea49 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -24,7 +24,7 @@ thiserror = "2.0" [dev-dependencies] lazy_static = "1.5.0" proptest = "1.5" -serial_test = "3.1.1" +serial_test = "3.2.0" solana-program-test = "2.1.0" solana-sdk = "2.1.0" From eaa6c81ca040c5b7d168630c29d8a43ef3df59b2 Mon Sep 17 00:00:00 2001 From: Jon C Date: Mon, 18 Nov 2024 23:23:54 +0100 Subject: [PATCH 247/248] token: Fix typos for cargo-spellcheck (#7503) * token: Fix typos #### Problem There are typos in the token code, and people sometimes fix them, but mostly don't. #### Summary of changes Starting with the `token/*` directory, fix all typos or properly put them between backticks if they're code. These were all found using `cargo spellcheck` and a specialized dictionary with programming / Solana / ZK terminology. Once all of the typos are fixed, then we can add the spellchecking to CI. * Update doctests --- program/README.md | 2 +- program/src/instruction.rs | 75 +++++++++++++++++++------------------- program/src/native_mint.rs | 2 +- program/src/processor.rs | 48 +++++++++++++----------- program/src/state.rs | 6 +-- 5 files changed, 69 insertions(+), 64 deletions(-) diff --git a/program/README.md b/program/README.md index 02cbb7c..81167bf 100644 --- a/program/README.md +++ b/program/README.md @@ -5,7 +5,7 @@ A token program on the Solana blockchain, usable for fungible and non-fungible t This program provides an interface and implementation that third parties can utilize to create and use their tokens. -Full documentation is available at https://spl.solana.com/token +Full documentation is available at the [SPL Token docs](https://spl.solana.com/token). ## Audit diff --git a/program/src/instruction.rs b/program/src/instruction.rs index ede7308..b797a7d 100644 --- a/program/src/instruction.rs +++ b/program/src/instruction.rs @@ -16,7 +16,7 @@ use { pub const MIN_SIGNERS: usize = 1; /// Maximum number of multisignature signers (max N) pub const MAX_SIGNERS: usize = 11; -/// Serialized length of a u64, for unpacking +/// Serialized length of a `u64`, for unpacking const U64_BYTES: usize = 8; /// Instructions supported by the token program. @@ -80,8 +80,8 @@ pub enum TokenInstruction<'a> { /// /// 0. `[writable]` The multisignature account to initialize. /// 1. `[]` Rent sysvar - /// 2. ..2+N. `[]` The signer accounts, must equal to N where 1 <= N <= - /// 11. + /// 2. ..`2+N`. `[]` The signer accounts, must equal to N where `1 <= N <= + /// 11`. InitializeMultisig { /// The number of signers (M) required to validate this multisignature /// account. @@ -103,7 +103,7 @@ pub enum TokenInstruction<'a> { /// 0. `[writable]` The source account. /// 1. `[writable]` The destination account. /// 2. `[]` The source account's multisignature owner/delegate. - /// 3. ..3+M `[signer]` M signer accounts. + /// 3. ..`3+M` `[signer]` M signer accounts. Transfer { /// The amount of tokens to transfer. amount: u64, @@ -122,7 +122,7 @@ pub enum TokenInstruction<'a> { /// 0. `[writable]` The source account. /// 1. `[]` The delegate. /// 2. `[]` The source account's multisignature owner. - /// 3. ..3+M `[signer]` M signer accounts + /// 3. ..`3+M` `[signer]` M signer accounts Approve { /// The amount of tokens the delegate is approved for. amount: u64, @@ -138,7 +138,7 @@ pub enum TokenInstruction<'a> { /// * Multisignature owner /// 0. `[writable]` The source account. /// 1. `[]` The source account's multisignature owner. - /// 2. ..2+M `[signer]` M signer accounts + /// 2. ..`2+M` `[signer]` M signer accounts Revoke, /// Sets a new authority of a mint or account. /// @@ -151,7 +151,7 @@ pub enum TokenInstruction<'a> { /// * Multisignature authority /// 0. `[writable]` The mint or account to change the authority of. /// 1. `[]` The mint's or account's current multisignature authority. - /// 2. ..2+M `[signer]` M signer accounts + /// 2. ..`2+M` `[signer]` M signer accounts SetAuthority { /// The type of authority to update. authority_type: AuthorityType, @@ -172,7 +172,7 @@ pub enum TokenInstruction<'a> { /// 0. `[writable]` The mint. /// 1. `[writable]` The account to mint tokens to. /// 2. `[]` The mint's multisignature mint-tokens authority. - /// 3. ..3+M `[signer]` M signer accounts. + /// 3. ..`3+M` `[signer]` M signer accounts. MintTo { /// The amount of new tokens to mint. amount: u64, @@ -191,7 +191,7 @@ pub enum TokenInstruction<'a> { /// 0. `[writable]` The account to burn from. /// 1. `[writable]` The token mint. /// 2. `[]` The account's multisignature owner/delegate. - /// 3. ..3+M `[signer]` M signer accounts. + /// 3. ..`3+M` `[signer]` M signer accounts. Burn { /// The amount of tokens to burn. amount: u64, @@ -210,9 +210,9 @@ pub enum TokenInstruction<'a> { /// 0. `[writable]` The account to close. /// 1. `[writable]` The destination account. /// 2. `[]` The account's multisignature owner. - /// 3. ..3+M `[signer]` M signer accounts. + /// 3. ..`3+M` `[signer]` M signer accounts. CloseAccount, - /// Freeze an Initialized account using the Mint's freeze_authority (if + /// Freeze an Initialized account using the Mint's `freeze_authority` (if /// set). /// /// Accounts expected by this instruction: @@ -226,9 +226,9 @@ pub enum TokenInstruction<'a> { /// 0. `[writable]` The account to freeze. /// 1. `[]` The token mint. /// 2. `[]` The mint's multisignature freeze authority. - /// 3. ..3+M `[signer]` M signer accounts. + /// 3. ..`3+M` `[signer]` M signer accounts. FreezeAccount, - /// Thaw a Frozen account using the Mint's freeze_authority (if set). + /// Thaw a Frozen account using the Mint's `freeze_authority` (if set). /// /// Accounts expected by this instruction: /// @@ -241,7 +241,7 @@ pub enum TokenInstruction<'a> { /// 0. `[writable]` The account to freeze. /// 1. `[]` The token mint. /// 2. `[]` The mint's multisignature freeze authority. - /// 3. ..3+M `[signer]` M signer accounts. + /// 3. ..`3+M` `[signer]` M signer accounts. ThawAccount, /// Transfers tokens from one account to another either directly or via a @@ -266,7 +266,7 @@ pub enum TokenInstruction<'a> { /// 1. `[]` The token mint. /// 2. `[writable]` The destination account. /// 3. `[]` The source account's multisignature owner/delegate. - /// 4. ..4+M `[signer]` M signer accounts. + /// 4. ..`4+M` `[signer]` M signer accounts. TransferChecked { /// The amount of tokens to transfer. amount: u64, @@ -293,7 +293,7 @@ pub enum TokenInstruction<'a> { /// 1. `[]` The token mint. /// 2. `[]` The delegate. /// 3. `[]` The source account's multisignature owner. - /// 4. ..4+M `[signer]` M signer accounts + /// 4. ..`4+M` `[signer]` M signer accounts ApproveChecked { /// The amount of tokens the delegate is approved for. amount: u64, @@ -303,8 +303,8 @@ pub enum TokenInstruction<'a> { /// Mints new tokens to an account. The native mint does not support /// minting. /// - /// This instruction differs from MintTo in that the decimals value is - /// checked by the caller. This may be useful when creating transactions + /// This instruction differs from `MintTo` in that the decimals value is + /// checked by the caller. This may be useful when creating transactions /// offline or within a hardware wallet. /// /// Accounts expected by this instruction: @@ -318,7 +318,7 @@ pub enum TokenInstruction<'a> { /// 0. `[writable]` The mint. /// 1. `[writable]` The account to mint tokens to. /// 2. `[]` The mint's multisignature mint-tokens authority. - /// 3. ..3+M `[signer]` M signer accounts. + /// 3. ..`3+M` `[signer]` M signer accounts. MintToChecked { /// The amount of new tokens to mint. amount: u64, @@ -344,17 +344,17 @@ pub enum TokenInstruction<'a> { /// 0. `[writable]` The account to burn from. /// 1. `[writable]` The token mint. /// 2. `[]` The account's multisignature owner/delegate. - /// 3. ..3+M `[signer]` M signer accounts. + /// 3. ..`3+M` `[signer]` M signer accounts. BurnChecked { /// The amount of tokens to burn. amount: u64, /// Expected number of base 10 digits to the right of the decimal place. decimals: u8, }, - /// Like InitializeAccount, but the owner pubkey is passed via instruction - /// data rather than the accounts list. This variant may be preferable - /// when using Cross Program Invocation from an instruction that does - /// not need the owner's `AccountInfo` otherwise. + /// Like [`InitializeAccount`], but the owner pubkey is passed via + /// instruction data rather than the accounts list. This variant may be + /// preferable when using Cross Program Invocation from an instruction + /// that does not need the owner's `AccountInfo` otherwise. /// /// Accounts expected by this instruction: /// @@ -376,7 +376,7 @@ pub enum TokenInstruction<'a> { /// 0. `[writable]` The native token account to sync with its underlying /// lamports. SyncNative, - /// Like InitializeAccount2, but does not require the Rent sysvar to be + /// Like [`InitializeAccount2`], but does not require the Rent sysvar to be /// provided /// /// Accounts expected by this instruction: @@ -387,14 +387,14 @@ pub enum TokenInstruction<'a> { /// The new account's owner/multisignature. owner: Pubkey, }, - /// Like InitializeMultisig, but does not require the Rent sysvar to be + /// Like [`InitializeMultisig`], but does not require the Rent sysvar to be /// provided /// /// Accounts expected by this instruction: /// /// 0. `[writable]` The multisignature account to initialize. - /// 1. ..1+N. `[]` The signer accounts, must equal to N where 1 <= N <= - /// 11. + /// 1. ..`1+N` `[]` The signer accounts, must equal to N where `1 <= N <= + /// 11`. InitializeMultisig2 { /// The number of signers (M) required to validate this multisignature /// account. @@ -439,7 +439,7 @@ pub enum TokenInstruction<'a> { /// Data expected by this instruction: /// None InitializeImmutableOwner, - /// Convert an Amount of tokens to a UiAmount `string`, using the given + /// Convert an Amount of tokens to a `UiAmount` string, using the given /// mint. In this version of the program, the mint can only specify the /// number of decimals. /// @@ -455,9 +455,9 @@ pub enum TokenInstruction<'a> { /// The amount of tokens to reformat. amount: u64, }, - /// Convert a UiAmount of tokens to a little-endian `u64` raw Amount, using - /// the given mint. In this version of the program, the mint can only - /// specify the number of decimals. + /// Convert a `UiAmount` of tokens to a little-endian `u64` raw Amount, + /// using the given mint. In this version of the program, the mint can + /// only specify the number of decimals. /// /// Return data can be fetched using `sol_get_return_data` and deserializing /// the return data as a little-endian `u64`. @@ -466,7 +466,7 @@ pub enum TokenInstruction<'a> { /// /// 0. `[]` The mint to calculate for UiAmountToAmount { - /// The ui_amount of tokens to reformat. + /// The `ui_amount` of tokens to reformat. ui_amount: &'a str, }, // Any new variants also need to be added to program-2022 `TokenInstruction`, so that the @@ -475,7 +475,7 @@ pub enum TokenInstruction<'a> { } impl<'a> TokenInstruction<'a> { /// Unpacks a byte buffer into a - /// [TokenInstruction](enum.TokenInstruction.html). + /// [`TokenInstruction`](enum.TokenInstruction.html). pub fn unpack(input: &'a [u8]) -> Result { use TokenError::InvalidInstruction; @@ -579,7 +579,7 @@ impl<'a> TokenInstruction<'a> { }) } - /// Packs a [TokenInstruction](enum.TokenInstruction.html) into a byte + /// Packs a [`TokenInstruction`](enum.TokenInstruction.html) into a byte /// buffer. pub fn pack(&self) -> Vec { let mut buf = Vec::with_capacity(size_of::()); @@ -738,7 +738,7 @@ impl<'a> TokenInstruction<'a> { } } -/// Specifies the authority type for SetAuthority instructions +/// Specifies the authority type for `SetAuthority` instructions #[repr(u8)] #[derive(Clone, Debug, PartialEq)] pub enum AuthorityType { @@ -1431,7 +1431,8 @@ pub fn ui_amount_to_amount( }) } -/// Utility function that checks index is between MIN_SIGNERS and MAX_SIGNERS +/// Utility function that checks index is between `MIN_SIGNERS` and +/// `MAX_SIGNERS` pub fn is_valid_signer_index(index: usize) -> bool { (MIN_SIGNERS..=MAX_SIGNERS).contains(&index) } diff --git a/program/src/native_mint.rs b/program/src/native_mint.rs index 37f7555..bd489ee 100644 --- a/program/src/native_mint.rs +++ b/program/src/native_mint.rs @@ -1,6 +1,6 @@ //! The Mint that represents the native token -/// There are 10^9 lamports in one SOL +/// There are `10^9` lamports in one SOL pub const DECIMALS: u8 = 9; // The Mint for native SOL Token accounts diff --git a/program/src/processor.rs b/program/src/processor.rs index 550df44..4702693 100644 --- a/program/src/processor.rs +++ b/program/src/processor.rs @@ -61,7 +61,7 @@ impl Processor { Ok(()) } - /// Processes an [InitializeMint](enum.TokenInstruction.html) instruction. + /// Processes an [`InitializeMint`](enum.TokenInstruction.html) instruction. pub fn process_initialize_mint( accounts: &[AccountInfo], decimals: u8, @@ -71,7 +71,8 @@ impl Processor { Self::_process_initialize_mint(accounts, decimals, mint_authority, freeze_authority, true) } - /// Processes an [InitializeMint2](enum.TokenInstruction.html) instruction. + /// Processes an [`InitializeMint2`](enum.TokenInstruction.html) + /// instruction. pub fn process_initialize_mint2( accounts: &[AccountInfo], decimals: u8, @@ -141,7 +142,7 @@ impl Processor { Ok(()) } - /// Processes an [InitializeAccount](enum.TokenInstruction.html) + /// Processes an [`InitializeAccount`](enum.TokenInstruction.html) /// instruction. pub fn process_initialize_account( program_id: &Pubkey, @@ -150,7 +151,7 @@ impl Processor { Self::_process_initialize_account(program_id, accounts, None, true) } - /// Processes an [InitializeAccount2](enum.TokenInstruction.html) + /// Processes an [`InitializeAccount2`](enum.TokenInstruction.html) /// instruction. pub fn process_initialize_account2( program_id: &Pubkey, @@ -160,7 +161,7 @@ impl Processor { Self::_process_initialize_account(program_id, accounts, Some(&owner), true) } - /// Processes an [InitializeAccount3](enum.TokenInstruction.html) + /// Processes an [`InitializeAccount3`](enum.TokenInstruction.html) /// instruction. pub fn process_initialize_account3( program_id: &Pubkey, @@ -212,19 +213,19 @@ impl Processor { Ok(()) } - /// Processes a [InitializeMultisig](enum.TokenInstruction.html) + /// Processes a [`InitializeMultisig`](enum.TokenInstruction.html) /// instruction. pub fn process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult { Self::_process_initialize_multisig(accounts, m, true) } - /// Processes a [InitializeMultisig2](enum.TokenInstruction.html) + /// Processes a [`InitializeMultisig2`](enum.TokenInstruction.html) /// instruction. pub fn process_initialize_multisig2(accounts: &[AccountInfo], m: u8) -> ProgramResult { Self::_process_initialize_multisig(accounts, m, false) } - /// Processes a [Transfer](enum.TokenInstruction.html) instruction. + /// Processes a [`Transfer`](enum.TokenInstruction.html) instruction. pub fn process_transfer( program_id: &Pubkey, accounts: &[AccountInfo], @@ -341,7 +342,7 @@ impl Processor { Ok(()) } - /// Processes an [Approve](enum.TokenInstruction.html) instruction. + /// Processes an [`Approve`](enum.TokenInstruction.html) instruction. pub fn process_approve( program_id: &Pubkey, accounts: &[AccountInfo], @@ -392,7 +393,7 @@ impl Processor { Ok(()) } - /// Processes an [Revoke](enum.TokenInstruction.html) instruction. + /// Processes an [`Revoke`](enum.TokenInstruction.html) instruction. pub fn process_revoke(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let source_account_info = next_account_info(account_info_iter)?; @@ -420,7 +421,7 @@ impl Processor { Ok(()) } - /// Processes a [SetAuthority](enum.TokenInstruction.html) instruction. + /// Processes a [`SetAuthority`](enum.TokenInstruction.html) instruction. pub fn process_set_authority( program_id: &Pubkey, accounts: &[AccountInfo], @@ -518,7 +519,7 @@ impl Processor { Ok(()) } - /// Processes a [MintTo](enum.TokenInstruction.html) instruction. + /// Processes a [`MintTo`](enum.TokenInstruction.html) instruction. pub fn process_mint_to( program_id: &Pubkey, accounts: &[AccountInfo], @@ -583,7 +584,7 @@ impl Processor { Ok(()) } - /// Processes a [Burn](enum.TokenInstruction.html) instruction. + /// Processes a [`Burn`](enum.TokenInstruction.html) instruction. pub fn process_burn( program_id: &Pubkey, accounts: &[AccountInfo], @@ -668,7 +669,7 @@ impl Processor { Ok(()) } - /// Processes a [CloseAccount](enum.TokenInstruction.html) instruction. + /// Processes a [`CloseAccount`](enum.TokenInstruction.html) instruction. pub fn process_close_account(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let source_account_info = next_account_info(account_info_iter)?; @@ -709,8 +710,8 @@ impl Processor { Ok(()) } - /// Processes a [FreezeAccount](enum.TokenInstruction.html) or a - /// [ThawAccount](enum.TokenInstruction.html) instruction. + /// Processes a [`FreezeAccount`](enum.TokenInstruction.html) or a + /// [`ThawAccount`](enum.TokenInstruction.html) instruction. pub fn process_toggle_freeze_account( program_id: &Pubkey, accounts: &[AccountInfo], @@ -754,7 +755,7 @@ impl Processor { Ok(()) } - /// Processes a [SyncNative](enum.TokenInstruction.html) instruction + /// Processes a [`SyncNative`](enum.TokenInstruction.html) instruction pub fn process_sync_native(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { let account_info_iter = &mut accounts.iter(); let native_account_info = next_account_info(account_info_iter)?; @@ -779,7 +780,8 @@ impl Processor { Ok(()) } - /// Processes a [GetAccountDataSize](enum.TokenInstruction.html) instruction + /// Processes a [`GetAccountDataSize`](enum.TokenInstruction.html) + /// instruction pub fn process_get_account_data_size( program_id: &Pubkey, accounts: &[AccountInfo], @@ -794,7 +796,7 @@ impl Processor { Ok(()) } - /// Processes an [InitializeImmutableOwner](enum.TokenInstruction.html) + /// Processes an [`InitializeImmutableOwner`](enum.TokenInstruction.html) /// instruction pub fn process_initialize_immutable_owner(accounts: &[AccountInfo]) -> ProgramResult { let account_info_iter = &mut accounts.iter(); @@ -807,7 +809,8 @@ impl Processor { Ok(()) } - /// Processes an [AmountToUiAmount](enum.TokenInstruction.html) instruction + /// Processes an [`AmountToUiAmount`](enum.TokenInstruction.html) + /// instruction pub fn process_amount_to_ui_amount( program_id: &Pubkey, accounts: &[AccountInfo], @@ -825,7 +828,8 @@ impl Processor { Ok(()) } - /// Processes an [AmountToUiAmount](enum.TokenInstruction.html) instruction + /// Processes an [`AmountToUiAmount`](enum.TokenInstruction.html) + /// instruction pub fn process_ui_amount_to_amount( program_id: &Pubkey, accounts: &[AccountInfo], @@ -843,7 +847,7 @@ impl Processor { Ok(()) } - /// Processes an [Instruction](enum.Instruction.html). + /// Processes an [`Instruction`](enum.Instruction.html). pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult { let instruction = TokenInstruction::unpack(input)?; diff --git a/program/src/state.rs b/program/src/state.rs index fe23fba..f7595e7 100644 --- a/program/src/state.rs +++ b/program/src/state.rs @@ -98,7 +98,7 @@ pub struct Account { pub delegate: COption, /// The account's state pub state: AccountState, - /// If is_native.is_some, this is a native token, and the value logs the + /// If `is_native.is_some`, this is a native token, and the value logs the /// rent-exempt reserve. An Account is required to be rent-exempt, so /// the value is used by the Processor to ensure that wrapped SOL /// accounts do not drop below this threshold. @@ -117,7 +117,7 @@ impl Account { pub fn is_native(&self) -> bool { self.is_native.is_some() } - /// Checks if a token Account's owner is the system_program or the + /// Checks if a token Account's owner is the `system_program` or the /// incinerator pub fn is_owned_by_system_program_or_incinerator(&self) -> bool { solana_program::system_program::check_id(&self.owner) @@ -344,7 +344,7 @@ pub trait GenericTokenAccount { pub const ACCOUNT_INITIALIZED_INDEX: usize = 108; /// Check if the account data buffer represents an initialized account. -/// This is checking the `state` (AccountState) field of an Account object. +/// This is checking the `state` (`AccountState`) field of an Account object. pub fn is_initialized_account(account_data: &[u8]) -> bool { *account_data .get(ACCOUNT_INITIALIZED_INDEX) From b123a8e24be641c2987eeaa58fe1afbeb062c25e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Nov 2024 12:04:56 +0100 Subject: [PATCH 248/248] build(deps): bump bytemuck from 1.19.0 to 1.20.0 (#7507) Bumps [bytemuck](https://github.com/Lokathor/bytemuck) from 1.19.0 to 1.20.0. - [Changelog](https://github.com/Lokathor/bytemuck/blob/main/changelog.md) - [Commits](https://github.com/Lokathor/bytemuck/compare/v1.19.0...v1.20.0) --- updated-dependencies: - dependency-name: bytemuck dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- program/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/program/Cargo.toml b/program/Cargo.toml index 93aea49..5a7f867 100644 --- a/program/Cargo.toml +++ b/program/Cargo.toml @@ -14,7 +14,7 @@ test-sbf = [] [dependencies] arrayref = "0.3.9" -bytemuck = "1.19.0" +bytemuck = "1.20.0" num-derive = "0.4" num-traits = "0.2" num_enum = "0.7.3"