From 4ef5a52ab8208c628e05688f1256fc21287caa36 Mon Sep 17 00:00:00 2001 From: Christian Kamm Date: Thu, 14 Jul 2022 11:21:39 +0200 Subject: [PATCH] Hack: make ts/ into the repo root, so yarn can work --- .gitmodules | 18 - CHANGELOG.md | 608 --- CODE_OF_CONDUCT.md | 3 - CONTRIBUTING.md | 41 - Cargo.lock | 4097 ----------------- Cargo.toml | 21 - LICENSE | 202 - Makefile | 38 - README.md | 119 +- VERSION | 1 - avm/Cargo.lock | 1174 ----- avm/Cargo.toml | 28 - avm/src/anchor/main.rs | 24 - avm/src/lib.rs | 316 -- avm/src/main.rs | 65 - cli/Cargo.toml | 44 - cli/npm-package/anchor.js | 97 - cli/npm-package/package.json | 23 - cli/src/bin/main.rs | 7 - cli/src/config.rs | 1116 ----- cli/src/lib.rs | 3102 ------------- cli/src/path.rs | 36 - cli/src/template.rs | 376 -- client/Cargo.toml | 22 - client/example/Cargo.toml | 20 - client/example/run-test.sh | 66 - client/example/src/main.rs | 229 - client/src/cluster.rs | 169 - client/src/lib.rs | 591 --- docker/Makefile | 34 - docker/build/Dockerfile | 49 - docs/.gitignore | 12 - docs/package.json | 24 - docs/src/.vuepress/components/Foo/Bar.vue | 15 - .../.vuepress/components/OtherComponent.vue | 3 - .../.vuepress/components/demo-component.vue | 15 - docs/src/.vuepress/config.js | 104 - docs/src/.vuepress/enhanceApp.js | 15 - docs/src/.vuepress/styles/index.styl | 8 - docs/src/.vuepress/styles/palette.styl | 10 - docs/src/cli/commands.md | 245 - docs/src/getting-started/installation.md | 74 - docs/src/getting-started/introduction.md | 18 - docs/src/getting-started/projects.md | 24 - docs/src/getting-started/publishing.md | 88 - docs/src/getting-started/verification.md | 52 - docs/src/index.md | 23 - docs/src/tutorials/tutorial-0.md | 189 - docs/src/tutorials/tutorial-1.md | 106 - docs/src/tutorials/tutorial-2.md | 77 - docs/src/tutorials/tutorial-3.md | 84 - docs/src/tutorials/tutorial-4.md | 56 - examples/tutorial/basic-0/Anchor.toml | 9 - examples/tutorial/basic-0/Cargo.toml | 4 - examples/tutorial/basic-0/client.js | 29 - examples/tutorial/basic-0/package.json | 19 - .../basic-0/programs/basic-0/Cargo.toml | 17 - .../basic-0/programs/basic-0/Xargo.toml | 2 - .../basic-0/programs/basic-0/src/lib.rs | 14 - examples/tutorial/basic-0/tests/basic-0.js | 16 - examples/tutorial/basic-1/Anchor.toml | 9 - examples/tutorial/basic-1/Cargo.toml | 4 - examples/tutorial/basic-1/package.json | 19 - .../basic-1/programs/basic-1/Cargo.toml | 17 - .../basic-1/programs/basic-1/Xargo.toml | 2 - .../basic-1/programs/basic-1/src/lib.rs | 40 - examples/tutorial/basic-1/tests/basic-1.js | 65 - examples/tutorial/basic-2/Anchor.toml | 9 - examples/tutorial/basic-2/Cargo.toml | 4 - examples/tutorial/basic-2/package.json | 19 - .../basic-2/programs/basic-2/Cargo.toml | 17 - .../basic-2/programs/basic-2/Xargo.toml | 2 - .../basic-2/programs/basic-2/src/lib.rs | 43 - examples/tutorial/basic-2/tests/basic-2.js | 48 - examples/tutorial/basic-3/Anchor.toml | 10 - examples/tutorial/basic-3/Cargo.toml | 4 - examples/tutorial/basic-3/package.json | 19 - .../basic-3/programs/puppet-master/Cargo.toml | 18 - .../basic-3/programs/puppet-master/Xargo.toml | 2 - .../basic-3/programs/puppet-master/src/lib.rs | 28 - .../basic-3/programs/puppet/Cargo.toml | 17 - .../basic-3/programs/puppet/Xargo.toml | 2 - .../basic-3/programs/puppet/src/lib.rs | 37 - examples/tutorial/basic-3/tests/basic-3.js | 38 - examples/tutorial/basic-4/Anchor.toml | 9 - examples/tutorial/basic-4/Cargo.toml | 4 - examples/tutorial/basic-4/package.json | 19 - .../basic-4/programs/basic-4/Cargo.toml | 17 - .../basic-4/programs/basic-4/Xargo.toml | 2 - .../basic-4/programs/basic-4/src/lib.rs | 44 - examples/tutorial/basic-4/tests/basic-4.js | 41 - examples/tutorial/package.json | 22 - examples/tutorial/yarn.lock | 1071 ----- examples/yarn.lock | 4 - ts/jest.config.js => jest.config.js | 0 lang/Cargo.toml | 44 - lang/attribute/access-control/Cargo.toml | 23 - lang/attribute/access-control/src/lib.rs | 81 - lang/attribute/account/Cargo.toml | 24 - lang/attribute/account/src/id.rs | 296 -- lang/attribute/account/src/lib.rs | 321 -- lang/attribute/constant/Cargo.toml | 20 - lang/attribute/constant/src/lib.rs | 11 - lang/attribute/error/Cargo.toml | 21 - lang/attribute/error/src/lib.rs | 116 - lang/attribute/event/Cargo.toml | 22 - lang/attribute/event/src/lib.rs | 92 - lang/attribute/interface/Cargo.toml | 23 - lang/attribute/interface/src/lib.rs | 243 - lang/attribute/program/Cargo.toml | 22 - lang/attribute/program/src/lib.rs | 16 - lang/attribute/state/Cargo.toml | 22 - lang/attribute/state/src/lib.rs | 92 - lang/derive/accounts/Cargo.toml | 24 - lang/derive/accounts/src/lib.rs | 548 --- lang/src/account_meta.rs | 8 - lang/src/accounts/account.rs | 425 -- lang/src/accounts/account_info.rs | 51 - lang/src/accounts/account_loader.rs | 286 -- lang/src/accounts/boxed.rs | 56 - lang/src/accounts/cpi_account.rs | 128 - lang/src/accounts/cpi_state.rs | 148 - lang/src/accounts/loader.rs | 234 - lang/src/accounts/mod.rs | 26 - lang/src/accounts/program.rs | 197 - lang/src/accounts/program_account.rs | 195 - lang/src/accounts/signer.rs | 111 - lang/src/accounts/state.rs | 170 - lang/src/accounts/system_account.rs | 90 - lang/src/accounts/sysvar.rs | 122 - lang/src/accounts/unchecked_account.rs | 76 - lang/src/bpf_upgradeable_state.rs | 74 - lang/src/bpf_writer.rs | 46 - lang/src/common.rs | 22 - lang/src/context.rs | 325 -- lang/src/ctor.rs | 27 - lang/src/error.rs | 478 -- lang/src/idl.rs | 111 - lang/src/lib.rs | 580 --- lang/src/system_program.rs | 381 -- lang/src/vec.rs | 96 - lang/syn/Cargo.toml | 30 - .../src/codegen/accounts/__client_accounts.rs | 157 - .../codegen/accounts/__cpi_client_accounts.rs | 185 - lang/syn/src/codegen/accounts/constraints.rs | 871 ---- lang/syn/src/codegen/accounts/exit.rs | 59 - lang/syn/src/codegen/accounts/mod.rs | 102 - .../src/codegen/accounts/to_account_infos.rs | 40 - .../src/codegen/accounts/to_account_metas.rs | 42 - lang/syn/src/codegen/accounts/try_accounts.rs | 178 - lang/syn/src/codegen/error.rs | 100 - lang/syn/src/codegen/mod.rs | 3 - lang/syn/src/codegen/program/accounts.rs | 65 - lang/syn/src/codegen/program/common.rs | 83 - lang/syn/src/codegen/program/cpi.rs | 207 - lang/syn/src/codegen/program/dispatch.rs | 187 - lang/syn/src/codegen/program/entry.rs | 91 - lang/syn/src/codegen/program/handlers.rs | 814 ---- lang/syn/src/codegen/program/instruction.rs | 194 - lang/syn/src/codegen/program/mod.rs | 35 - lang/syn/src/hash.rs | 117 - lang/syn/src/idl/file.rs | 570 --- lang/syn/src/idl/mod.rs | 313 -- lang/syn/src/idl/pda.rs | 322 -- lang/syn/src/lib.rs | 925 ---- lang/syn/src/parser/accounts/constraints.rs | 1043 ----- lang/syn/src/parser/accounts/mod.rs | 420 -- lang/syn/src/parser/context.rs | 247 - lang/syn/src/parser/error.rs | 90 - lang/syn/src/parser/mod.rs | 10 - lang/syn/src/parser/program/instructions.rs | 126 - lang/syn/src/parser/program/mod.rs | 54 - lang/syn/src/parser/program/state.rs | 310 -- lang/tests/generics_test.rs | 55 - ts/package.json => package.json | 0 ts/rollup.config.ts => rollup.config.ts | 0 spl/Cargo.toml | 25 - spl/src/associated_token.rs | 48 - spl/src/dex.rs | 299 -- spl/src/governance.rs | 58 - spl/src/lib.rs | 17 - spl/src/mint.rs | 13 - spl/src/shmem.rs | 50 - spl/src/token.rs | 432 -- {ts/src => src}/coder/borsh/accounts.ts | 0 {ts/src => src}/coder/borsh/event.ts | 0 {ts/src => src}/coder/borsh/idl.ts | 0 {ts/src => src}/coder/borsh/index.ts | 0 {ts/src => src}/coder/borsh/instruction.ts | 0 {ts/src => src}/coder/borsh/state.ts | 0 {ts/src => src}/coder/common.ts | 0 {ts/src => src}/coder/index.ts | 0 {ts/src => src}/coder/spl-token/accounts.ts | 0 .../coder/spl-token/buffer-layout.ts | 0 {ts/src => src}/coder/spl-token/events.ts | 0 {ts/src => src}/coder/spl-token/index.ts | 0 .../coder/spl-token/instruction.ts | 0 {ts/src => src}/coder/spl-token/state.ts | 0 {ts/src => src}/error.ts | 0 {ts/src => src}/idl.ts | 0 {ts/src => src}/index.ts | 0 {ts/src => src}/nodewallet.ts | 0 {ts/src => src}/program/accounts-resolver.ts | 0 {ts/src => src}/program/common.ts | 0 {ts/src => src}/program/context.ts | 0 {ts/src => src}/program/event.ts | 0 {ts/src => src}/program/index.ts | 0 {ts/src => src}/program/namespace/account.ts | 0 {ts/src => src}/program/namespace/index.ts | 0 .../program/namespace/instruction.ts | 0 {ts/src => src}/program/namespace/methods.ts | 0 {ts/src => src}/program/namespace/rpc.ts | 0 {ts/src => src}/program/namespace/simulate.ts | 0 {ts/src => src}/program/namespace/state.ts | 0 .../program/namespace/transaction.ts | 0 {ts/src => src}/program/namespace/types.ts | 0 {ts/src => src}/program/namespace/views.ts | 0 {ts/src => src}/provider.ts | 0 {ts/src => src}/spl/index.ts | 0 {ts/src => src}/spl/token.ts | 0 {ts/src => src}/utils/bytes/base64.ts | 0 {ts/src => src}/utils/bytes/bs58.ts | 0 {ts/src => src}/utils/bytes/hex.ts | 0 {ts/src => src}/utils/bytes/index.ts | 0 {ts/src => src}/utils/bytes/utf8.ts | 0 {ts/src => src}/utils/common.ts | 0 {ts/src => src}/utils/features.ts | 0 {ts/src => src}/utils/index.ts | 0 {ts/src => src}/utils/pubkey.ts | 0 {ts/src => src}/utils/registry.ts | 0 {ts/src => src}/utils/rpc.ts | 0 {ts/src => src}/utils/sha256.ts | 0 {ts/src => src}/utils/token.ts | 0 {ts/src => src}/workspace.ts | 0 tests/.prettierignore | 3 - tests/README.md | 7 - tests/auction-house | 1 - tests/bpf-upgradeable-state/.gitignore | 1 - tests/bpf-upgradeable-state/Anchor.toml | 12 - tests/bpf-upgradeable-state/Cargo.toml | 4 - .../bpf_upgradeable_state-keypair.json | 1 - .../migrations/deploy.ts | 12 - tests/bpf-upgradeable-state/package.json | 16 - .../program_with_different_programdata.json | 1 - .../programs/bpf-upgradeable-state/Cargo.toml | 19 - .../programs/bpf-upgradeable-state/Xargo.toml | 2 - .../programs/bpf-upgradeable-state/src/lib.rs | 80 - .../tests/bpf-upgradable-state.ts | 203 - tests/bpf-upgradeable-state/tsconfig.json | 10 - tests/cashiers-check/Anchor.toml | 11 - tests/cashiers-check/Cargo.toml | 4 - tests/cashiers-check/migrations/deploy.js | 12 - tests/cashiers-check/package.json | 19 - .../programs/cashiers-check/Cargo.toml | 20 - .../programs/cashiers-check/Xargo.toml | 2 - .../programs/cashiers-check/src/lib.rs | 180 - tests/cashiers-check/tests/cashiers-check.js | 113 - tests/cfo/Anchor.toml | 45 - tests/cfo/Cargo.toml | 9 - tests/cfo/deps/serum-dex | 1 - tests/cfo/deps/stake | 1 - tests/cfo/deps/swap | 1 - tests/cfo/migrations/deploy.js | 12 - tests/cfo/package.json | 19 - tests/cfo/programs/cfo/Cargo.toml | 26 - tests/cfo/programs/cfo/Xargo.toml | 2 - tests/cfo/programs/cfo/src/lib.rs | 995 ---- tests/cfo/scripts/common.sh | 19 - tests/cfo/scripts/fees.js | 34 - tests/cfo/scripts/list-market.js | 23 - tests/cfo/scripts/localnet.sh | 58 - tests/cfo/scripts/market-maker.json | 1 - tests/cfo/scripts/trade-bot.js | 16 - tests/cfo/tests/cfo.js | 515 --- tests/cfo/tests/utils/index.js | 599 --- tests/cfo/tests/utils/stake.js | 187 - tests/chat/Anchor.toml | 11 - tests/chat/Cargo.toml | 4 - tests/chat/migrations/deploy.js | 12 - tests/chat/package.json | 19 - tests/chat/programs/chat/Cargo.toml | 19 - tests/chat/programs/chat/Xargo.toml | 2 - tests/chat/programs/chat/src/lib.rs | 113 - tests/chat/tests/chat.js | 106 - tests/composite/Anchor.toml | 9 - tests/composite/Cargo.toml | 4 - tests/composite/package.json | 19 - tests/composite/programs/composite/Cargo.toml | 17 - tests/composite/programs/composite/Xargo.toml | 2 - tests/composite/programs/composite/src/lib.rs | 64 - tests/composite/tests/composite.js | 50 - tests/cpi-returns/.gitignore | 6 - tests/cpi-returns/Anchor.toml | 16 - tests/cpi-returns/Cargo.toml | 4 - tests/cpi-returns/migrations/deploy.ts | 12 - tests/cpi-returns/package.json | 19 - tests/cpi-returns/programs/callee/Cargo.toml | 19 - tests/cpi-returns/programs/callee/Xargo.toml | 2 - tests/cpi-returns/programs/callee/src/lib.rs | 57 - tests/cpi-returns/programs/caller/Cargo.toml | 20 - tests/cpi-returns/programs/caller/Xargo.toml | 2 - tests/cpi-returns/programs/caller/src/lib.rs | 75 - tests/cpi-returns/tests/cpi-return.ts | 220 - tests/cpi-returns/tsconfig.json | 10 - tests/custom-coder/Anchor.toml | 15 - tests/custom-coder/Cargo.toml | 4 - tests/custom-coder/migrations/deploy.ts | 12 - tests/custom-coder/package.json | 19 - .../programs/custom-coder/Cargo.toml | 20 - .../programs/custom-coder/Xargo.toml | 2 - .../programs/custom-coder/src/lib.rs | 14 - .../programs/spl-token/Cargo.toml | 20 - .../programs/spl-token/Xargo.toml | 2 - .../programs/spl-token/src/lib.rs | 296 -- tests/custom-coder/tests/custom-coder.ts | 137 - tests/custom-coder/tsconfig.json | 10 - tests/declare-id/Anchor.toml | 9 - tests/declare-id/Cargo.toml | 4 - tests/declare-id/package.json | 19 - .../declare-id/programs/declare-id/Cargo.toml | 16 - .../declare-id/programs/declare-id/Xargo.toml | 2 - .../declare-id/programs/declare-id/src/lib.rs | 17 - tests/declare-id/tests/declare-id.ts | 21 - tests/declare-id/tsconfig.json | 10 - tests/docs/Anchor.toml | 11 - tests/docs/Cargo.toml | 4 - tests/docs/package.json | 19 - tests/docs/programs/docs/Cargo.toml | 16 - tests/docs/programs/docs/Xargo.toml | 2 - tests/docs/programs/docs/src/lib.rs | 50 - {ts/tests => tests}/error.spec.ts | 0 tests/errors/Anchor.toml | 12 - tests/errors/Cargo.toml | 4 - tests/errors/package.json | 19 - tests/errors/programs/errors/Cargo.toml | 17 - tests/errors/programs/errors/Xargo.toml | 2 - tests/errors/programs/errors/src/lib.rs | 214 - tests/errors/tests/errors.ts | 625 --- tests/errors/tsconfig.json | 10 - tests/escrow/Anchor.toml | 11 - tests/escrow/Cargo.toml | 4 - tests/escrow/package.json | 19 - tests/escrow/programs/escrow/Cargo.toml | 21 - tests/escrow/programs/escrow/Xargo.toml | 2 - tests/escrow/programs/escrow/src/lib.rs | 240 - tests/escrow/tests/escrow.ts | 246 - tests/escrow/tsconfig.json | 10 - {ts/tests => tests}/events.spec.ts | 0 tests/events/Anchor.toml | 9 - tests/events/Cargo.toml | 4 - tests/events/migrations/deploy.js | 12 - tests/events/package.json | 19 - tests/events/programs/events/Cargo.toml | 19 - tests/events/programs/events/Xargo.toml | 2 - tests/events/programs/events/src/lib.rs | 46 - tests/events/tests/events.js | 61 - tests/floats/Anchor.toml | 12 - tests/floats/Cargo.toml | 4 - tests/floats/migrations/deploy.ts | 12 - tests/floats/package.json | 19 - tests/floats/programs/floats/Cargo.toml | 19 - tests/floats/programs/floats/Xargo.toml | 2 - tests/floats/programs/floats/src/lib.rs | 51 - tests/floats/tests/floats.ts | 67 - tests/floats/tsconfig.json | 10 - tests/ido-pool/Anchor.toml | 11 - tests/ido-pool/Cargo.toml | 4 - tests/ido-pool/migrations/deploy.js | 12 - tests/ido-pool/package.json | 19 - tests/ido-pool/programs/ido-pool/Cargo.toml | 20 - tests/ido-pool/programs/ido-pool/Xargo.toml | 2 - tests/ido-pool/programs/ido-pool/src/lib.rs | 684 --- tests/ido-pool/tests/ido-pool.js | 607 --- tests/ido-pool/tests/utils/index.js | 54 - tests/interface/Anchor.toml | 12 - tests/interface/Cargo.toml | 4 - tests/interface/package.json | 19 - .../programs/counter-auth/Cargo.toml | 20 - .../programs/counter-auth/Xargo.toml | 2 - .../programs/counter-auth/src/lib.rs | 35 - tests/interface/programs/counter/Cargo.toml | 19 - tests/interface/programs/counter/Xargo.toml | 2 - tests/interface/programs/counter/src/lib.rs | 73 - tests/interface/tests/interface.js | 46 - tests/lockup/Anchor.toml | 12 - tests/lockup/Cargo.toml | 4 - tests/lockup/docs/lockups.md | 127 - tests/lockup/docs/staking.md | 193 - tests/lockup/migrations/deploy.js | 177 - tests/lockup/package.json | 19 - tests/lockup/programs/lockup/Cargo.toml | 20 - tests/lockup/programs/lockup/Xargo.toml | 2 - .../lockup/programs/lockup/src/calculator.rs | 84 - tests/lockup/programs/lockup/src/lib.rs | 542 --- tests/lockup/programs/registry/Cargo.toml | 21 - tests/lockup/programs/registry/Xargo.toml | 2 - tests/lockup/programs/registry/src/lib.rs | 1326 ------ tests/lockup/tests/lockup.js | 968 ---- tests/lockup/tests/utils.js | 66 - tests/misc/Anchor.toml | 11 - tests/misc/Cargo.toml | 4 - tests/misc/ci.sh | 11 - tests/misc/migrations/deploy.js | 12 - tests/misc/miscNonRentExempt.ts | 135 - tests/misc/package.json | 22 - tests/misc/programs/init-if-needed/Cargo.toml | 19 - tests/misc/programs/init-if-needed/Xargo.toml | 2 - tests/misc/programs/init-if-needed/src/lib.rs | 61 - tests/misc/programs/misc/Cargo.toml | 22 - tests/misc/programs/misc/Xargo.toml | 2 - tests/misc/programs/misc/src/account.rs | 75 - tests/misc/programs/misc/src/context.rs | 578 --- tests/misc/programs/misc/src/event.rs | 34 - tests/misc/programs/misc/src/lib.rs | 356 -- tests/misc/programs/misc2/Cargo.toml | 19 - tests/misc/programs/misc2/Xargo.toml | 2 - tests/misc/programs/misc2/src/lib.rs | 38 - tests/misc/programs/shared/Cargo.toml | 9 - tests/misc/programs/shared/src/lib.rs | 7 - tests/misc/tests/init-if-needed/Test.toml | 2 - .../tests/init-if-needed/init-if-needed.ts | 101 - tests/misc/tests/misc/Test.toml | 6 - tests/misc/tests/misc/misc.ts | 2139 --------- tests/misc/tsconfig.json | 10 - tests/multiple-suites/Anchor.toml | 18 - tests/multiple-suites/Cargo.toml | 4 - tests/multiple-suites/migrations/deploy.ts | 12 - tests/multiple-suites/package.json | 19 - .../programs/multiple-suites/Cargo.toml | 19 - .../programs/multiple-suites/Xargo.toml | 2 - .../programs/multiple-suites/src/lib.rs | 16 - tests/multiple-suites/tests/Test.base.toml | 5 - .../multiple-suites/tests/Test.root.base.toml | 12 - .../tests/accounts/ANOTHER_ACC.json | 13 - .../tests/accounts/SOME_ACCOUNT.json | 13 - .../tests/accounts/SOME_TOKEN.json | 13 - .../tests/another-suite/Test.toml | 4 - .../tests/another-suite/another-suite.ts | 44 - .../fifth-suite/Test.toml | 4 - .../fifth-suite/fifthSuite.ts | 43 - .../forth-suite/Test.toml | 4 - .../forth-suite/forth-suite.ts | 43 - .../tests/multiple-suites/Test.toml | 4 - .../tests/multiple-suites/multiple-suites.ts | 32 - .../tests/third-suite/Test.toml | 4 - .../third-suite/sub-dir-one/subDirOne.ts | 32 - .../third-suite/sub-dir-two/subDirTwo.ts | 32 - tests/multiple-suites/tsconfig.json | 10 - tests/multisig/Anchor.toml | 11 - tests/multisig/Cargo.toml | 4 - tests/multisig/migrations/deploy.js | 12 - tests/multisig/package.json | 19 - tests/multisig/programs/multisig/Cargo.toml | 19 - tests/multisig/programs/multisig/Xargo.toml | 2 - tests/multisig/programs/multisig/src/lib.rs | 281 -- tests/multisig/tests/multisig.js | 135 - tests/package.json | 55 - tests/pda-derivation/Anchor.toml | 15 - tests/pda-derivation/Cargo.toml | 4 - tests/pda-derivation/migrations/deploy.ts | 22 - tests/pda-derivation/package.json | 19 - .../programs/pda-derivation/Cargo.toml | 19 - .../programs/pda-derivation/Xargo.toml | 2 - .../programs/pda-derivation/src/lib.rs | 84 - tests/pda-derivation/tests/typescript.spec.ts | 68 - tests/pda-derivation/tsconfig.json | 10 - {ts/tests => tests}/program-common.spec.ts | 0 tests/pyth/Anchor.toml | 11 - tests/pyth/Cargo.toml | 4 - tests/pyth/Xargo.toml | 2 - tests/pyth/package.json | 19 - tests/pyth/programs/pyth/Cargo.toml | 21 - tests/pyth/programs/pyth/Xargo.toml | 2 - tests/pyth/programs/pyth/src/lib.rs | 41 - tests/pyth/programs/pyth/src/pc.rs | 118 - tests/pyth/tests/oracleUtils.ts | 313 -- tests/pyth/tests/pyth.spec.ts | 39 - tests/pyth/tsconfig.json | 10 - tests/safety-checks/.gitignore | 6 - tests/safety-checks/Anchor.toml | 13 - tests/safety-checks/Cargo.toml | 4 - tests/safety-checks/migrations/deploy.ts | 12 - .../programs/account-info/Cargo.toml | 19 - .../programs/account-info/Xargo.toml | 2 - .../programs/account-info/src/lib.rs | 16 - .../programs/unchecked-account/Cargo.toml | 19 - .../programs/unchecked-account/Xargo.toml | 2 - .../programs/unchecked-account/src/lib.rs | 16 - tests/safety-checks/test.sh | 27 - tests/safety-checks/tests/safety-checks.ts | 16 - tests/safety-checks/tsconfig.json | 10 - tests/spl/token-proxy/Anchor.toml | 11 - tests/spl/token-proxy/Cargo.toml | 4 - tests/spl/token-proxy/package.json | 19 - .../programs/token-proxy/Cargo.toml | 19 - .../programs/token-proxy/Xargo.toml | 2 - .../programs/token-proxy/src/lib.rs | 151 - tests/spl/token-proxy/tests/token-proxy.js | 178 - tests/swap/Anchor.toml | 15 - tests/swap/Cargo.toml | 7 - tests/swap/README.md | 34 - tests/swap/deps/serum-dex | 1 - tests/swap/migrations/deploy.js | 12 - tests/swap/package.json | 19 - tests/swap/programs/swap/Cargo.toml | 21 - tests/swap/programs/swap/Xargo.toml | 2 - tests/swap/programs/swap/src/lib.rs | 496 -- tests/swap/tests/swap.js | 315 -- tests/swap/tests/utils/index.js | 513 --- tests/system-accounts/Anchor.toml | 9 - tests/system-accounts/Cargo.toml | 4 - tests/system-accounts/package.json | 19 - .../programs/system-accounts/Cargo.toml | 17 - .../programs/system-accounts/Xargo.toml | 2 - .../programs/system-accounts/src/lib.rs | 18 - .../system-accounts/tests/system-accounts.js | 59 - tests/sysvars/Anchor.toml | 9 - tests/sysvars/Cargo.toml | 4 - tests/sysvars/package.json | 19 - tests/sysvars/programs/sysvars/Cargo.toml | 18 - tests/sysvars/programs/sysvars/Xargo.toml | 2 - tests/sysvars/programs/sysvars/src/lib.rs | 18 - tests/sysvars/tests/sysvars.js | 38 - tests/tictactoe/Anchor.toml | 9 - tests/tictactoe/Cargo.toml | 4 - tests/tictactoe/migrations/deploy.js | 12 - tests/tictactoe/package.json | 19 - tests/tictactoe/programs/tictactoe/Cargo.toml | 20 - tests/tictactoe/programs/tictactoe/Xargo.toml | 2 - tests/tictactoe/programs/tictactoe/src/lib.rs | 213 - tests/tictactoe/tests/tictactoe.js | 157 - {ts/tests => tests}/transaction.spec.ts | 0 tests/typescript/Anchor.toml | 12 - tests/typescript/Cargo.toml | 4 - tests/typescript/migrations/deploy.ts | 22 - tests/typescript/package.json | 19 - tests/typescript/programs/shared/Cargo.toml | 9 - tests/typescript/programs/shared/src/lib.rs | 7 - .../typescript/programs/typescript/Cargo.toml | 19 - .../typescript/programs/typescript/Xargo.toml | 2 - .../typescript/programs/typescript/src/lib.rs | 17 - tests/typescript/tests/typescript.spec.ts | 13 - tests/typescript/tsconfig.json | 10 - tests/validator-clone/Anchor.toml | 38 - tests/validator-clone/Cargo.toml | 4 - tests/validator-clone/package.json | 19 - .../programs/validator-clone/Cargo.toml | 19 - .../programs/validator-clone/Xargo.toml | 2 - .../programs/validator-clone/src/lib.rs | 15 - .../validator-clone/tests/validator-clone.ts | 70 - tests/validator-clone/tsconfig.json | 10 - tests/yarn.lock | 1350 ------ tests/zero-copy/Anchor.toml | 16 - tests/zero-copy/Cargo.toml | 4 - tests/zero-copy/migrations/deploy.js | 12 - tests/zero-copy/package.json | 19 - tests/zero-copy/programs/shared/Cargo.toml | 9 - tests/zero-copy/programs/shared/src/lib.rs | 7 - tests/zero-copy/programs/zero-copy/Cargo.toml | 25 - tests/zero-copy/programs/zero-copy/Xargo.toml | 2 - tests/zero-copy/programs/zero-copy/src/lib.rs | 191 - .../zero-copy/tests/compute_unit_test.rs | 70 - tests/zero-copy/programs/zero-cpi/Cargo.toml | 20 - tests/zero-copy/programs/zero-cpi/Xargo.toml | 2 - tests/zero-copy/programs/zero-cpi/src/lib.rs | 35 - tests/zero-copy/tests/zero-copy.js | 264 -- ts/README.md | 10 - ts/tsconfig.base.json => tsconfig.base.json | 0 ts/tsconfig.cjs.json => tsconfig.cjs.json | 0 ts/tsconfig.json => tsconfig.json | 0 {ts/types => types}/buffer-layout/index.d.ts | 0 version-bump.sh | 44 - ts/yarn.lock => yarn.lock | 0 573 files changed, 5 insertions(+), 53776 deletions(-) delete mode 100644 CHANGELOG.md delete mode 100644 CODE_OF_CONDUCT.md delete mode 100644 CONTRIBUTING.md delete mode 100644 Cargo.lock delete mode 100644 Cargo.toml delete mode 100644 LICENSE delete mode 100644 Makefile delete mode 100644 VERSION delete mode 100644 avm/Cargo.lock delete mode 100644 avm/Cargo.toml delete mode 100644 avm/src/anchor/main.rs delete mode 100644 avm/src/lib.rs delete mode 100644 avm/src/main.rs delete mode 100644 cli/Cargo.toml delete mode 100644 cli/npm-package/anchor.js delete mode 100644 cli/npm-package/package.json delete mode 100644 cli/src/bin/main.rs delete mode 100644 cli/src/config.rs delete mode 100644 cli/src/lib.rs delete mode 100644 cli/src/path.rs delete mode 100644 cli/src/template.rs delete mode 100644 client/Cargo.toml delete mode 100644 client/example/Cargo.toml delete mode 100755 client/example/run-test.sh delete mode 100644 client/example/src/main.rs delete mode 100644 client/src/cluster.rs delete mode 100644 client/src/lib.rs delete mode 100644 docker/Makefile delete mode 100644 docker/build/Dockerfile delete mode 100755 docs/.gitignore delete mode 100755 docs/package.json delete mode 100755 docs/src/.vuepress/components/Foo/Bar.vue delete mode 100755 docs/src/.vuepress/components/OtherComponent.vue delete mode 100755 docs/src/.vuepress/components/demo-component.vue delete mode 100755 docs/src/.vuepress/config.js delete mode 100755 docs/src/.vuepress/enhanceApp.js delete mode 100755 docs/src/.vuepress/styles/index.styl delete mode 100755 docs/src/.vuepress/styles/palette.styl delete mode 100644 docs/src/cli/commands.md delete mode 100644 docs/src/getting-started/installation.md delete mode 100644 docs/src/getting-started/introduction.md delete mode 100644 docs/src/getting-started/projects.md delete mode 100644 docs/src/getting-started/publishing.md delete mode 100644 docs/src/getting-started/verification.md delete mode 100644 docs/src/index.md delete mode 100644 docs/src/tutorials/tutorial-0.md delete mode 100644 docs/src/tutorials/tutorial-1.md delete mode 100644 docs/src/tutorials/tutorial-2.md delete mode 100644 docs/src/tutorials/tutorial-3.md delete mode 100644 docs/src/tutorials/tutorial-4.md delete mode 100644 examples/tutorial/basic-0/Anchor.toml delete mode 100644 examples/tutorial/basic-0/Cargo.toml delete mode 100644 examples/tutorial/basic-0/client.js delete mode 100644 examples/tutorial/basic-0/package.json delete mode 100644 examples/tutorial/basic-0/programs/basic-0/Cargo.toml delete mode 100644 examples/tutorial/basic-0/programs/basic-0/Xargo.toml delete mode 100644 examples/tutorial/basic-0/programs/basic-0/src/lib.rs delete mode 100644 examples/tutorial/basic-0/tests/basic-0.js delete mode 100644 examples/tutorial/basic-1/Anchor.toml delete mode 100644 examples/tutorial/basic-1/Cargo.toml delete mode 100644 examples/tutorial/basic-1/package.json delete mode 100644 examples/tutorial/basic-1/programs/basic-1/Cargo.toml delete mode 100644 examples/tutorial/basic-1/programs/basic-1/Xargo.toml delete mode 100644 examples/tutorial/basic-1/programs/basic-1/src/lib.rs delete mode 100644 examples/tutorial/basic-1/tests/basic-1.js delete mode 100644 examples/tutorial/basic-2/Anchor.toml delete mode 100644 examples/tutorial/basic-2/Cargo.toml delete mode 100644 examples/tutorial/basic-2/package.json delete mode 100644 examples/tutorial/basic-2/programs/basic-2/Cargo.toml delete mode 100644 examples/tutorial/basic-2/programs/basic-2/Xargo.toml delete mode 100644 examples/tutorial/basic-2/programs/basic-2/src/lib.rs delete mode 100644 examples/tutorial/basic-2/tests/basic-2.js delete mode 100644 examples/tutorial/basic-3/Anchor.toml delete mode 100644 examples/tutorial/basic-3/Cargo.toml delete mode 100644 examples/tutorial/basic-3/package.json delete mode 100644 examples/tutorial/basic-3/programs/puppet-master/Cargo.toml delete mode 100644 examples/tutorial/basic-3/programs/puppet-master/Xargo.toml delete mode 100644 examples/tutorial/basic-3/programs/puppet-master/src/lib.rs delete mode 100644 examples/tutorial/basic-3/programs/puppet/Cargo.toml delete mode 100644 examples/tutorial/basic-3/programs/puppet/Xargo.toml delete mode 100644 examples/tutorial/basic-3/programs/puppet/src/lib.rs delete mode 100644 examples/tutorial/basic-3/tests/basic-3.js delete mode 100644 examples/tutorial/basic-4/Anchor.toml delete mode 100644 examples/tutorial/basic-4/Cargo.toml delete mode 100644 examples/tutorial/basic-4/package.json delete mode 100644 examples/tutorial/basic-4/programs/basic-4/Cargo.toml delete mode 100644 examples/tutorial/basic-4/programs/basic-4/Xargo.toml delete mode 100644 examples/tutorial/basic-4/programs/basic-4/src/lib.rs delete mode 100644 examples/tutorial/basic-4/tests/basic-4.js delete mode 100644 examples/tutorial/package.json delete mode 100644 examples/tutorial/yarn.lock delete mode 100644 examples/yarn.lock rename ts/jest.config.js => jest.config.js (100%) delete mode 100644 lang/Cargo.toml delete mode 100644 lang/attribute/access-control/Cargo.toml delete mode 100644 lang/attribute/access-control/src/lib.rs delete mode 100644 lang/attribute/account/Cargo.toml delete mode 100644 lang/attribute/account/src/id.rs delete mode 100644 lang/attribute/account/src/lib.rs delete mode 100644 lang/attribute/constant/Cargo.toml delete mode 100644 lang/attribute/constant/src/lib.rs delete mode 100644 lang/attribute/error/Cargo.toml delete mode 100644 lang/attribute/error/src/lib.rs delete mode 100644 lang/attribute/event/Cargo.toml delete mode 100644 lang/attribute/event/src/lib.rs delete mode 100644 lang/attribute/interface/Cargo.toml delete mode 100644 lang/attribute/interface/src/lib.rs delete mode 100644 lang/attribute/program/Cargo.toml delete mode 100644 lang/attribute/program/src/lib.rs delete mode 100644 lang/attribute/state/Cargo.toml delete mode 100644 lang/attribute/state/src/lib.rs delete mode 100644 lang/derive/accounts/Cargo.toml delete mode 100644 lang/derive/accounts/src/lib.rs delete mode 100644 lang/src/account_meta.rs delete mode 100644 lang/src/accounts/account.rs delete mode 100644 lang/src/accounts/account_info.rs delete mode 100644 lang/src/accounts/account_loader.rs delete mode 100644 lang/src/accounts/boxed.rs delete mode 100644 lang/src/accounts/cpi_account.rs delete mode 100644 lang/src/accounts/cpi_state.rs delete mode 100644 lang/src/accounts/loader.rs delete mode 100644 lang/src/accounts/mod.rs delete mode 100644 lang/src/accounts/program.rs delete mode 100644 lang/src/accounts/program_account.rs delete mode 100644 lang/src/accounts/signer.rs delete mode 100644 lang/src/accounts/state.rs delete mode 100644 lang/src/accounts/system_account.rs delete mode 100644 lang/src/accounts/sysvar.rs delete mode 100644 lang/src/accounts/unchecked_account.rs delete mode 100644 lang/src/bpf_upgradeable_state.rs delete mode 100644 lang/src/bpf_writer.rs delete mode 100644 lang/src/common.rs delete mode 100644 lang/src/context.rs delete mode 100644 lang/src/ctor.rs delete mode 100644 lang/src/error.rs delete mode 100644 lang/src/idl.rs delete mode 100644 lang/src/lib.rs delete mode 100644 lang/src/system_program.rs delete mode 100644 lang/src/vec.rs delete mode 100644 lang/syn/Cargo.toml delete mode 100644 lang/syn/src/codegen/accounts/__client_accounts.rs delete mode 100644 lang/syn/src/codegen/accounts/__cpi_client_accounts.rs delete mode 100644 lang/syn/src/codegen/accounts/constraints.rs delete mode 100644 lang/syn/src/codegen/accounts/exit.rs delete mode 100644 lang/syn/src/codegen/accounts/mod.rs delete mode 100644 lang/syn/src/codegen/accounts/to_account_infos.rs delete mode 100644 lang/syn/src/codegen/accounts/to_account_metas.rs delete mode 100644 lang/syn/src/codegen/accounts/try_accounts.rs delete mode 100644 lang/syn/src/codegen/error.rs delete mode 100644 lang/syn/src/codegen/mod.rs delete mode 100644 lang/syn/src/codegen/program/accounts.rs delete mode 100644 lang/syn/src/codegen/program/common.rs delete mode 100644 lang/syn/src/codegen/program/cpi.rs delete mode 100644 lang/syn/src/codegen/program/dispatch.rs delete mode 100644 lang/syn/src/codegen/program/entry.rs delete mode 100644 lang/syn/src/codegen/program/handlers.rs delete mode 100644 lang/syn/src/codegen/program/instruction.rs delete mode 100644 lang/syn/src/codegen/program/mod.rs delete mode 100644 lang/syn/src/hash.rs delete mode 100644 lang/syn/src/idl/file.rs delete mode 100644 lang/syn/src/idl/mod.rs delete mode 100644 lang/syn/src/idl/pda.rs delete mode 100644 lang/syn/src/lib.rs delete mode 100644 lang/syn/src/parser/accounts/constraints.rs delete mode 100644 lang/syn/src/parser/accounts/mod.rs delete mode 100644 lang/syn/src/parser/context.rs delete mode 100644 lang/syn/src/parser/error.rs delete mode 100644 lang/syn/src/parser/mod.rs delete mode 100644 lang/syn/src/parser/program/instructions.rs delete mode 100644 lang/syn/src/parser/program/mod.rs delete mode 100644 lang/syn/src/parser/program/state.rs delete mode 100644 lang/tests/generics_test.rs rename ts/package.json => package.json (100%) rename ts/rollup.config.ts => rollup.config.ts (100%) delete mode 100644 spl/Cargo.toml delete mode 100644 spl/src/associated_token.rs delete mode 100644 spl/src/dex.rs delete mode 100644 spl/src/governance.rs delete mode 100644 spl/src/lib.rs delete mode 100644 spl/src/mint.rs delete mode 100644 spl/src/shmem.rs delete mode 100644 spl/src/token.rs rename {ts/src => src}/coder/borsh/accounts.ts (100%) rename {ts/src => src}/coder/borsh/event.ts (100%) rename {ts/src => src}/coder/borsh/idl.ts (100%) rename {ts/src => src}/coder/borsh/index.ts (100%) rename {ts/src => src}/coder/borsh/instruction.ts (100%) rename {ts/src => src}/coder/borsh/state.ts (100%) rename {ts/src => src}/coder/common.ts (100%) rename {ts/src => src}/coder/index.ts (100%) rename {ts/src => src}/coder/spl-token/accounts.ts (100%) rename {ts/src => src}/coder/spl-token/buffer-layout.ts (100%) rename {ts/src => src}/coder/spl-token/events.ts (100%) rename {ts/src => src}/coder/spl-token/index.ts (100%) rename {ts/src => src}/coder/spl-token/instruction.ts (100%) rename {ts/src => src}/coder/spl-token/state.ts (100%) rename {ts/src => src}/error.ts (100%) rename {ts/src => src}/idl.ts (100%) rename {ts/src => src}/index.ts (100%) rename {ts/src => src}/nodewallet.ts (100%) rename {ts/src => src}/program/accounts-resolver.ts (100%) rename {ts/src => src}/program/common.ts (100%) rename {ts/src => src}/program/context.ts (100%) rename {ts/src => src}/program/event.ts (100%) rename {ts/src => src}/program/index.ts (100%) rename {ts/src => src}/program/namespace/account.ts (100%) rename {ts/src => src}/program/namespace/index.ts (100%) rename {ts/src => src}/program/namespace/instruction.ts (100%) rename {ts/src => src}/program/namespace/methods.ts (100%) rename {ts/src => src}/program/namespace/rpc.ts (100%) rename {ts/src => src}/program/namespace/simulate.ts (100%) rename {ts/src => src}/program/namespace/state.ts (100%) rename {ts/src => src}/program/namespace/transaction.ts (100%) rename {ts/src => src}/program/namespace/types.ts (100%) rename {ts/src => src}/program/namespace/views.ts (100%) rename {ts/src => src}/provider.ts (100%) rename {ts/src => src}/spl/index.ts (100%) rename {ts/src => src}/spl/token.ts (100%) rename {ts/src => src}/utils/bytes/base64.ts (100%) rename {ts/src => src}/utils/bytes/bs58.ts (100%) rename {ts/src => src}/utils/bytes/hex.ts (100%) rename {ts/src => src}/utils/bytes/index.ts (100%) rename {ts/src => src}/utils/bytes/utf8.ts (100%) rename {ts/src => src}/utils/common.ts (100%) rename {ts/src => src}/utils/features.ts (100%) rename {ts/src => src}/utils/index.ts (100%) rename {ts/src => src}/utils/pubkey.ts (100%) rename {ts/src => src}/utils/registry.ts (100%) rename {ts/src => src}/utils/rpc.ts (100%) rename {ts/src => src}/utils/sha256.ts (100%) rename {ts/src => src}/utils/token.ts (100%) rename {ts/src => src}/workspace.ts (100%) delete mode 100644 tests/.prettierignore delete mode 100644 tests/README.md delete mode 160000 tests/auction-house delete mode 100644 tests/bpf-upgradeable-state/.gitignore delete mode 100644 tests/bpf-upgradeable-state/Anchor.toml delete mode 100644 tests/bpf-upgradeable-state/Cargo.toml delete mode 100644 tests/bpf-upgradeable-state/bpf_upgradeable_state-keypair.json delete mode 100644 tests/bpf-upgradeable-state/migrations/deploy.ts delete mode 100644 tests/bpf-upgradeable-state/package.json delete mode 100644 tests/bpf-upgradeable-state/program_with_different_programdata.json delete mode 100644 tests/bpf-upgradeable-state/programs/bpf-upgradeable-state/Cargo.toml delete mode 100644 tests/bpf-upgradeable-state/programs/bpf-upgradeable-state/Xargo.toml delete mode 100644 tests/bpf-upgradeable-state/programs/bpf-upgradeable-state/src/lib.rs delete mode 100644 tests/bpf-upgradeable-state/tests/bpf-upgradable-state.ts delete mode 100644 tests/bpf-upgradeable-state/tsconfig.json delete mode 100644 tests/cashiers-check/Anchor.toml delete mode 100644 tests/cashiers-check/Cargo.toml delete mode 100644 tests/cashiers-check/migrations/deploy.js delete mode 100644 tests/cashiers-check/package.json delete mode 100644 tests/cashiers-check/programs/cashiers-check/Cargo.toml delete mode 100644 tests/cashiers-check/programs/cashiers-check/Xargo.toml delete mode 100644 tests/cashiers-check/programs/cashiers-check/src/lib.rs delete mode 100644 tests/cashiers-check/tests/cashiers-check.js delete mode 100644 tests/cfo/Anchor.toml delete mode 100644 tests/cfo/Cargo.toml delete mode 160000 tests/cfo/deps/serum-dex delete mode 160000 tests/cfo/deps/stake delete mode 160000 tests/cfo/deps/swap delete mode 100644 tests/cfo/migrations/deploy.js delete mode 100644 tests/cfo/package.json delete mode 100644 tests/cfo/programs/cfo/Cargo.toml delete mode 100644 tests/cfo/programs/cfo/Xargo.toml delete mode 100644 tests/cfo/programs/cfo/src/lib.rs delete mode 100644 tests/cfo/scripts/common.sh delete mode 100755 tests/cfo/scripts/fees.js delete mode 100755 tests/cfo/scripts/list-market.js delete mode 100755 tests/cfo/scripts/localnet.sh delete mode 100644 tests/cfo/scripts/market-maker.json delete mode 100755 tests/cfo/scripts/trade-bot.js delete mode 100644 tests/cfo/tests/cfo.js delete mode 100644 tests/cfo/tests/utils/index.js delete mode 100644 tests/cfo/tests/utils/stake.js delete mode 100644 tests/chat/Anchor.toml delete mode 100644 tests/chat/Cargo.toml delete mode 100644 tests/chat/migrations/deploy.js delete mode 100644 tests/chat/package.json delete mode 100644 tests/chat/programs/chat/Cargo.toml delete mode 100644 tests/chat/programs/chat/Xargo.toml delete mode 100644 tests/chat/programs/chat/src/lib.rs delete mode 100644 tests/chat/tests/chat.js delete mode 100644 tests/composite/Anchor.toml delete mode 100644 tests/composite/Cargo.toml delete mode 100644 tests/composite/package.json delete mode 100644 tests/composite/programs/composite/Cargo.toml delete mode 100644 tests/composite/programs/composite/Xargo.toml delete mode 100644 tests/composite/programs/composite/src/lib.rs delete mode 100644 tests/composite/tests/composite.js delete mode 100644 tests/cpi-returns/.gitignore delete mode 100644 tests/cpi-returns/Anchor.toml delete mode 100644 tests/cpi-returns/Cargo.toml delete mode 100644 tests/cpi-returns/migrations/deploy.ts delete mode 100644 tests/cpi-returns/package.json delete mode 100644 tests/cpi-returns/programs/callee/Cargo.toml delete mode 100644 tests/cpi-returns/programs/callee/Xargo.toml delete mode 100644 tests/cpi-returns/programs/callee/src/lib.rs delete mode 100644 tests/cpi-returns/programs/caller/Cargo.toml delete mode 100644 tests/cpi-returns/programs/caller/Xargo.toml delete mode 100644 tests/cpi-returns/programs/caller/src/lib.rs delete mode 100644 tests/cpi-returns/tests/cpi-return.ts delete mode 100644 tests/cpi-returns/tsconfig.json delete mode 100644 tests/custom-coder/Anchor.toml delete mode 100644 tests/custom-coder/Cargo.toml delete mode 100644 tests/custom-coder/migrations/deploy.ts delete mode 100644 tests/custom-coder/package.json delete mode 100644 tests/custom-coder/programs/custom-coder/Cargo.toml delete mode 100644 tests/custom-coder/programs/custom-coder/Xargo.toml delete mode 100644 tests/custom-coder/programs/custom-coder/src/lib.rs delete mode 100644 tests/custom-coder/programs/spl-token/Cargo.toml delete mode 100644 tests/custom-coder/programs/spl-token/Xargo.toml delete mode 100644 tests/custom-coder/programs/spl-token/src/lib.rs delete mode 100644 tests/custom-coder/tests/custom-coder.ts delete mode 100644 tests/custom-coder/tsconfig.json delete mode 100644 tests/declare-id/Anchor.toml delete mode 100644 tests/declare-id/Cargo.toml delete mode 100644 tests/declare-id/package.json delete mode 100644 tests/declare-id/programs/declare-id/Cargo.toml delete mode 100644 tests/declare-id/programs/declare-id/Xargo.toml delete mode 100644 tests/declare-id/programs/declare-id/src/lib.rs delete mode 100644 tests/declare-id/tests/declare-id.ts delete mode 100644 tests/declare-id/tsconfig.json delete mode 100644 tests/docs/Anchor.toml delete mode 100644 tests/docs/Cargo.toml delete mode 100644 tests/docs/package.json delete mode 100644 tests/docs/programs/docs/Cargo.toml delete mode 100644 tests/docs/programs/docs/Xargo.toml delete mode 100644 tests/docs/programs/docs/src/lib.rs rename {ts/tests => tests}/error.spec.ts (100%) delete mode 100644 tests/errors/Anchor.toml delete mode 100644 tests/errors/Cargo.toml delete mode 100644 tests/errors/package.json delete mode 100644 tests/errors/programs/errors/Cargo.toml delete mode 100644 tests/errors/programs/errors/Xargo.toml delete mode 100644 tests/errors/programs/errors/src/lib.rs delete mode 100644 tests/errors/tests/errors.ts delete mode 100644 tests/errors/tsconfig.json delete mode 100644 tests/escrow/Anchor.toml delete mode 100644 tests/escrow/Cargo.toml delete mode 100644 tests/escrow/package.json delete mode 100644 tests/escrow/programs/escrow/Cargo.toml delete mode 100644 tests/escrow/programs/escrow/Xargo.toml delete mode 100644 tests/escrow/programs/escrow/src/lib.rs delete mode 100644 tests/escrow/tests/escrow.ts delete mode 100644 tests/escrow/tsconfig.json rename {ts/tests => tests}/events.spec.ts (100%) delete mode 100644 tests/events/Anchor.toml delete mode 100644 tests/events/Cargo.toml delete mode 100644 tests/events/migrations/deploy.js delete mode 100644 tests/events/package.json delete mode 100644 tests/events/programs/events/Cargo.toml delete mode 100644 tests/events/programs/events/Xargo.toml delete mode 100644 tests/events/programs/events/src/lib.rs delete mode 100644 tests/events/tests/events.js delete mode 100644 tests/floats/Anchor.toml delete mode 100644 tests/floats/Cargo.toml delete mode 100644 tests/floats/migrations/deploy.ts delete mode 100644 tests/floats/package.json delete mode 100644 tests/floats/programs/floats/Cargo.toml delete mode 100644 tests/floats/programs/floats/Xargo.toml delete mode 100644 tests/floats/programs/floats/src/lib.rs delete mode 100644 tests/floats/tests/floats.ts delete mode 100644 tests/floats/tsconfig.json delete mode 100644 tests/ido-pool/Anchor.toml delete mode 100644 tests/ido-pool/Cargo.toml delete mode 100644 tests/ido-pool/migrations/deploy.js delete mode 100644 tests/ido-pool/package.json delete mode 100644 tests/ido-pool/programs/ido-pool/Cargo.toml delete mode 100644 tests/ido-pool/programs/ido-pool/Xargo.toml delete mode 100644 tests/ido-pool/programs/ido-pool/src/lib.rs delete mode 100644 tests/ido-pool/tests/ido-pool.js delete mode 100644 tests/ido-pool/tests/utils/index.js delete mode 100644 tests/interface/Anchor.toml delete mode 100644 tests/interface/Cargo.toml delete mode 100644 tests/interface/package.json delete mode 100644 tests/interface/programs/counter-auth/Cargo.toml delete mode 100644 tests/interface/programs/counter-auth/Xargo.toml delete mode 100644 tests/interface/programs/counter-auth/src/lib.rs delete mode 100644 tests/interface/programs/counter/Cargo.toml delete mode 100644 tests/interface/programs/counter/Xargo.toml delete mode 100644 tests/interface/programs/counter/src/lib.rs delete mode 100644 tests/interface/tests/interface.js delete mode 100644 tests/lockup/Anchor.toml delete mode 100644 tests/lockup/Cargo.toml delete mode 100644 tests/lockup/docs/lockups.md delete mode 100644 tests/lockup/docs/staking.md delete mode 100644 tests/lockup/migrations/deploy.js delete mode 100644 tests/lockup/package.json delete mode 100644 tests/lockup/programs/lockup/Cargo.toml delete mode 100644 tests/lockup/programs/lockup/Xargo.toml delete mode 100644 tests/lockup/programs/lockup/src/calculator.rs delete mode 100644 tests/lockup/programs/lockup/src/lib.rs delete mode 100644 tests/lockup/programs/registry/Cargo.toml delete mode 100644 tests/lockup/programs/registry/Xargo.toml delete mode 100644 tests/lockup/programs/registry/src/lib.rs delete mode 100644 tests/lockup/tests/lockup.js delete mode 100644 tests/lockup/tests/utils.js delete mode 100644 tests/misc/Anchor.toml delete mode 100644 tests/misc/Cargo.toml delete mode 100755 tests/misc/ci.sh delete mode 100644 tests/misc/migrations/deploy.js delete mode 100644 tests/misc/miscNonRentExempt.ts delete mode 100644 tests/misc/package.json delete mode 100644 tests/misc/programs/init-if-needed/Cargo.toml delete mode 100644 tests/misc/programs/init-if-needed/Xargo.toml delete mode 100644 tests/misc/programs/init-if-needed/src/lib.rs delete mode 100644 tests/misc/programs/misc/Cargo.toml delete mode 100644 tests/misc/programs/misc/Xargo.toml delete mode 100644 tests/misc/programs/misc/src/account.rs delete mode 100644 tests/misc/programs/misc/src/context.rs delete mode 100644 tests/misc/programs/misc/src/event.rs delete mode 100644 tests/misc/programs/misc/src/lib.rs delete mode 100644 tests/misc/programs/misc2/Cargo.toml delete mode 100644 tests/misc/programs/misc2/Xargo.toml delete mode 100644 tests/misc/programs/misc2/src/lib.rs delete mode 100644 tests/misc/programs/shared/Cargo.toml delete mode 100644 tests/misc/programs/shared/src/lib.rs delete mode 100644 tests/misc/tests/init-if-needed/Test.toml delete mode 100644 tests/misc/tests/init-if-needed/init-if-needed.ts delete mode 100644 tests/misc/tests/misc/Test.toml delete mode 100644 tests/misc/tests/misc/misc.ts delete mode 100644 tests/misc/tsconfig.json delete mode 100644 tests/multiple-suites/Anchor.toml delete mode 100644 tests/multiple-suites/Cargo.toml delete mode 100644 tests/multiple-suites/migrations/deploy.ts delete mode 100644 tests/multiple-suites/package.json delete mode 100644 tests/multiple-suites/programs/multiple-suites/Cargo.toml delete mode 100644 tests/multiple-suites/programs/multiple-suites/Xargo.toml delete mode 100644 tests/multiple-suites/programs/multiple-suites/src/lib.rs delete mode 100644 tests/multiple-suites/tests/Test.base.toml delete mode 100644 tests/multiple-suites/tests/Test.root.base.toml delete mode 100644 tests/multiple-suites/tests/accounts/ANOTHER_ACC.json delete mode 100644 tests/multiple-suites/tests/accounts/SOME_ACCOUNT.json delete mode 100644 tests/multiple-suites/tests/accounts/SOME_TOKEN.json delete mode 100644 tests/multiple-suites/tests/another-suite/Test.toml delete mode 100644 tests/multiple-suites/tests/another-suite/another-suite.ts delete mode 100644 tests/multiple-suites/tests/fourth-and-fifth-suite/fifth-suite/Test.toml delete mode 100644 tests/multiple-suites/tests/fourth-and-fifth-suite/fifth-suite/fifthSuite.ts delete mode 100644 tests/multiple-suites/tests/fourth-and-fifth-suite/forth-suite/Test.toml delete mode 100644 tests/multiple-suites/tests/fourth-and-fifth-suite/forth-suite/forth-suite.ts delete mode 100644 tests/multiple-suites/tests/multiple-suites/Test.toml delete mode 100644 tests/multiple-suites/tests/multiple-suites/multiple-suites.ts delete mode 100644 tests/multiple-suites/tests/third-suite/Test.toml delete mode 100644 tests/multiple-suites/tests/third-suite/sub-dir-one/subDirOne.ts delete mode 100644 tests/multiple-suites/tests/third-suite/sub-dir-two/subDirTwo.ts delete mode 100644 tests/multiple-suites/tsconfig.json delete mode 100644 tests/multisig/Anchor.toml delete mode 100644 tests/multisig/Cargo.toml delete mode 100644 tests/multisig/migrations/deploy.js delete mode 100644 tests/multisig/package.json delete mode 100644 tests/multisig/programs/multisig/Cargo.toml delete mode 100644 tests/multisig/programs/multisig/Xargo.toml delete mode 100644 tests/multisig/programs/multisig/src/lib.rs delete mode 100644 tests/multisig/tests/multisig.js delete mode 100644 tests/package.json delete mode 100644 tests/pda-derivation/Anchor.toml delete mode 100644 tests/pda-derivation/Cargo.toml delete mode 100644 tests/pda-derivation/migrations/deploy.ts delete mode 100644 tests/pda-derivation/package.json delete mode 100644 tests/pda-derivation/programs/pda-derivation/Cargo.toml delete mode 100644 tests/pda-derivation/programs/pda-derivation/Xargo.toml delete mode 100644 tests/pda-derivation/programs/pda-derivation/src/lib.rs delete mode 100644 tests/pda-derivation/tests/typescript.spec.ts delete mode 100644 tests/pda-derivation/tsconfig.json rename {ts/tests => tests}/program-common.spec.ts (100%) delete mode 100644 tests/pyth/Anchor.toml delete mode 100644 tests/pyth/Cargo.toml delete mode 100644 tests/pyth/Xargo.toml delete mode 100644 tests/pyth/package.json delete mode 100644 tests/pyth/programs/pyth/Cargo.toml delete mode 100644 tests/pyth/programs/pyth/Xargo.toml delete mode 100644 tests/pyth/programs/pyth/src/lib.rs delete mode 100644 tests/pyth/programs/pyth/src/pc.rs delete mode 100644 tests/pyth/tests/oracleUtils.ts delete mode 100644 tests/pyth/tests/pyth.spec.ts delete mode 100644 tests/pyth/tsconfig.json delete mode 100644 tests/safety-checks/.gitignore delete mode 100644 tests/safety-checks/Anchor.toml delete mode 100644 tests/safety-checks/Cargo.toml delete mode 100644 tests/safety-checks/migrations/deploy.ts delete mode 100644 tests/safety-checks/programs/account-info/Cargo.toml delete mode 100644 tests/safety-checks/programs/account-info/Xargo.toml delete mode 100644 tests/safety-checks/programs/account-info/src/lib.rs delete mode 100644 tests/safety-checks/programs/unchecked-account/Cargo.toml delete mode 100644 tests/safety-checks/programs/unchecked-account/Xargo.toml delete mode 100644 tests/safety-checks/programs/unchecked-account/src/lib.rs delete mode 100755 tests/safety-checks/test.sh delete mode 100644 tests/safety-checks/tests/safety-checks.ts delete mode 100644 tests/safety-checks/tsconfig.json delete mode 100644 tests/spl/token-proxy/Anchor.toml delete mode 100644 tests/spl/token-proxy/Cargo.toml delete mode 100644 tests/spl/token-proxy/package.json delete mode 100644 tests/spl/token-proxy/programs/token-proxy/Cargo.toml delete mode 100644 tests/spl/token-proxy/programs/token-proxy/Xargo.toml delete mode 100644 tests/spl/token-proxy/programs/token-proxy/src/lib.rs delete mode 100644 tests/spl/token-proxy/tests/token-proxy.js delete mode 100644 tests/swap/Anchor.toml delete mode 100644 tests/swap/Cargo.toml delete mode 100644 tests/swap/README.md delete mode 160000 tests/swap/deps/serum-dex delete mode 100644 tests/swap/migrations/deploy.js delete mode 100644 tests/swap/package.json delete mode 100644 tests/swap/programs/swap/Cargo.toml delete mode 100644 tests/swap/programs/swap/Xargo.toml delete mode 100644 tests/swap/programs/swap/src/lib.rs delete mode 100644 tests/swap/tests/swap.js delete mode 100644 tests/swap/tests/utils/index.js delete mode 100644 tests/system-accounts/Anchor.toml delete mode 100644 tests/system-accounts/Cargo.toml delete mode 100644 tests/system-accounts/package.json delete mode 100644 tests/system-accounts/programs/system-accounts/Cargo.toml delete mode 100644 tests/system-accounts/programs/system-accounts/Xargo.toml delete mode 100644 tests/system-accounts/programs/system-accounts/src/lib.rs delete mode 100644 tests/system-accounts/tests/system-accounts.js delete mode 100644 tests/sysvars/Anchor.toml delete mode 100644 tests/sysvars/Cargo.toml delete mode 100644 tests/sysvars/package.json delete mode 100644 tests/sysvars/programs/sysvars/Cargo.toml delete mode 100644 tests/sysvars/programs/sysvars/Xargo.toml delete mode 100644 tests/sysvars/programs/sysvars/src/lib.rs delete mode 100644 tests/sysvars/tests/sysvars.js delete mode 100644 tests/tictactoe/Anchor.toml delete mode 100644 tests/tictactoe/Cargo.toml delete mode 100644 tests/tictactoe/migrations/deploy.js delete mode 100644 tests/tictactoe/package.json delete mode 100644 tests/tictactoe/programs/tictactoe/Cargo.toml delete mode 100644 tests/tictactoe/programs/tictactoe/Xargo.toml delete mode 100644 tests/tictactoe/programs/tictactoe/src/lib.rs delete mode 100644 tests/tictactoe/tests/tictactoe.js rename {ts/tests => tests}/transaction.spec.ts (100%) delete mode 100644 tests/typescript/Anchor.toml delete mode 100644 tests/typescript/Cargo.toml delete mode 100644 tests/typescript/migrations/deploy.ts delete mode 100644 tests/typescript/package.json delete mode 100644 tests/typescript/programs/shared/Cargo.toml delete mode 100644 tests/typescript/programs/shared/src/lib.rs delete mode 100644 tests/typescript/programs/typescript/Cargo.toml delete mode 100644 tests/typescript/programs/typescript/Xargo.toml delete mode 100644 tests/typescript/programs/typescript/src/lib.rs delete mode 100644 tests/typescript/tests/typescript.spec.ts delete mode 100644 tests/typescript/tsconfig.json delete mode 100644 tests/validator-clone/Anchor.toml delete mode 100644 tests/validator-clone/Cargo.toml delete mode 100644 tests/validator-clone/package.json delete mode 100644 tests/validator-clone/programs/validator-clone/Cargo.toml delete mode 100644 tests/validator-clone/programs/validator-clone/Xargo.toml delete mode 100644 tests/validator-clone/programs/validator-clone/src/lib.rs delete mode 100644 tests/validator-clone/tests/validator-clone.ts delete mode 100644 tests/validator-clone/tsconfig.json delete mode 100644 tests/yarn.lock delete mode 100644 tests/zero-copy/Anchor.toml delete mode 100644 tests/zero-copy/Cargo.toml delete mode 100644 tests/zero-copy/migrations/deploy.js delete mode 100644 tests/zero-copy/package.json delete mode 100644 tests/zero-copy/programs/shared/Cargo.toml delete mode 100644 tests/zero-copy/programs/shared/src/lib.rs delete mode 100644 tests/zero-copy/programs/zero-copy/Cargo.toml delete mode 100644 tests/zero-copy/programs/zero-copy/Xargo.toml delete mode 100644 tests/zero-copy/programs/zero-copy/src/lib.rs delete mode 100644 tests/zero-copy/programs/zero-copy/tests/compute_unit_test.rs delete mode 100644 tests/zero-copy/programs/zero-cpi/Cargo.toml delete mode 100644 tests/zero-copy/programs/zero-cpi/Xargo.toml delete mode 100644 tests/zero-copy/programs/zero-cpi/src/lib.rs delete mode 100644 tests/zero-copy/tests/zero-copy.js delete mode 100644 ts/README.md rename ts/tsconfig.base.json => tsconfig.base.json (100%) rename ts/tsconfig.cjs.json => tsconfig.cjs.json (100%) rename ts/tsconfig.json => tsconfig.json (100%) rename {ts/types => types}/buffer-layout/index.d.ts (100%) delete mode 100755 version-bump.sh rename ts/yarn.lock => yarn.lock (100%) diff --git a/.gitmodules b/.gitmodules index ec5d6737..b1357583 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,21 +1,3 @@ -[submodule "examples/swap/deps/serum-dex"] - path = tests/swap/deps/serum-dex - url = https://github.com/project-serum/serum-dex -[submodule "examples/cfo/deps/serum-dex"] - path = tests/cfo/deps/serum-dex - url = https://github.com/project-serum/serum-dex -[submodule "examples/cfo/deps/swap"] - path = tests/cfo/deps/swap - url = https://github.com/project-serum/swap.git - branch = armani/cfo -[submodule "examples/cfo/deps/stake"] - path = tests/cfo/deps/stake - url = https://github.com/project-serum/stake.git - branch = armani/cfo [submodule "examples/permissioned-markets/deps/serum-dex"] path = tests/permissioned-markets/deps/serum-dex url = https://github.com/project-serum/serum-dex -[submodule "tests/auction-house"] - path = tests/auction-house - url = https://github.com/armaniferrante/auction-house - branch = armani/pda diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index daea9104..00000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,608 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -**Note:** Version 0 of Semantic Versioning is handled differently from version 1 and above. -The minor version will be incremented upon a breaking change and the patch version will be incremented for features. - -## [Unreleased] - -## [0.24.2] - 2022-04-13 - -### Fixes - -* lang: Fix `returns` being serialized as `null` instead of `undefined` in IDL ([#1782](https://github.com/project-serum/anchor/pull/1782)). - -## [0.24.1] - 2022-04-12 - -### Fixes - -* lang: Fix `anchor build` failing if `Test.toml` included a relative path that didn't exist yet because it's created by `anchor build` ([#1772](https://github.com/project-serum/anchor/pull/1772)). -* cli: Update js/ts template to use new `AnchorProvider` class ([#1770](https://github.com/project-serum/anchor/pull/1770)). - -## [0.24.0] - 2022-04-12 - -### Features - -* lang: Add support for multiple test suites with separate local validators ([#1681](https://github.com/project-serum/anchor/pull/1681)). -* lang: Add return values to CPI client ([#1598](https://github.com/project-serum/anchor/pull/1598)). -* ts: Add view functions ([#1695](https://github.com/project-serum/anchor/pull/1695)). -* avm: New `avm update` command to update the Anchor CLI to the latest version ([#1670](https://github.com/project-serum/anchor/pull/1670)). -* cli: Update js/ts templates to use new `program.methods` syntax ([#1732](https://github.com/project-serum/anchor/pull/1732)). -* cli: Workspaces created with `anchor init` now come with the `prettier` formatter and scripts included ([#1741](https://github.com/project-serum/anchor/pull/1741)). -* ts: Add `pubkeys` function to methods builder to get all instruction account addresses ([#1733](https://github.com/project-serum/anchor/pull/1733)). -* ts: Export `LangErrorCode` and `LangErrorMessage` from `error.ts` ([#1756](https://github.com/project-serum/anchor/pull/1756)). - -### Fixes - -* avm: `avm install` no longer downloads the version if already installed in the machine ([#1670](https://github.com/project-serum/anchor/pull/1670)). -* cli: make `anchor test` fail when used with `--skip-deploy` option and without `--skip-local-validator` option but there already is a running validator ([#1675](https://github.com/project-serum/anchor/pull/1675)). -* lang: Return proper error instead of panicking if account length is smaller than discriminator in functions of `(Account)Loader` ([#1678](https://github.com/project-serum/anchor/pull/1678)). -* cli: Add `@types/bn.js` to `devDependencies` in cli template ([#1712](https://github.com/project-serum/anchor/pull/1712)). -* ts: Event listener no longer crashes on Program Upgrade or any other unexpected log ([#1757](https://github.com/project-serum/anchor/pull/1757)). - -### Breaking - -* avm: `avm install` switches to the newly installed version after installation finishes ([#1670](https://github.com/project-serum/anchor/pull/1670)). -* spl: Re-export the `spl_token` crate ([#1665](https://github.com/project-serum/anchor/pull/1665)). -* lang, cli, spl: Update solana toolchain to v1.9.13 ([#1653](https://github.com/project-serum/anchor/pull/1653) and [#1751](https://github.com/project-serum/anchor/pull/1751)). -* lang: `Program` type now deserializes `programdata_address` only on demand ([#1723](https://github.com/project-serum/anchor/pull/1723)). -* ts: Make `Provider` an interface and adjust its signatures and add `AnchorProvider` implementor class ([#1707](https://github.com/project-serum/anchor/pull/1707)). -* spl: Change "to" to "from" in `token::burn` ([#1080](https://github.com/project-serum/anchor/pull/1080)). - -## [0.23.0] - 2022-03-20 - -### Features - -* cli: Add `anchor clean` command that's the same as `cargo clean` but preserves keypairs inside `target/deploy` ([#1470](https://github.com/project-serum/anchor/issues/1470)). -* cli: Running `anchor init` now initializes a new git repository for the workspace. This can be disabled with the `--no-git` flag ([#1605](https://github.com/project-serum/anchor/pull/1605)). -* cli: Add support for `anchor idl fetch` to work outside anchor workspace ([#1509](https://github.com/project-serum/anchor/pull/1509)). -* cli: [[test.validator.clone]] also clones the program data account of programs owned by the bpf upgradeable loader ([#1481](https://github.com/project-serum/anchor/issues/1481)). -* lang: Add new `AccountSysvarMismatch` error code and test cases for sysvars ([#1535](https://github.com/project-serum/anchor/pull/1535)). -* lang: Replace `std::io::Cursor` with a custom `Write` impl that uses the Solana mem syscalls ([#1589](https://github.com/project-serum/anchor/pull/1589)). -* lang: Add `require_neq`, `require_keys_neq`, `require_gt`, and `require_gte` comparison macros ([#1622](https://github.com/project-serum/anchor/pull/1622)). -* lang: Handle arrays with const as size in instruction data ([#1623](https://github.com/project-serum/anchor/issues/1623). -* spl: Add support for revoke instruction ([#1493](https://github.com/project-serum/anchor/pull/1493)). -* ts: Add provider parameter to `Spl.token` factory method ([#1597](https://github.com/project-serum/anchor/pull/1597)). - -### Fixes - -* ts: Fix the loss of strict typing using the `methods` namespace on builder functions ([#1539](https://github.com/project-serum/anchor/pull/1539)). -* spl: Update `spl/governance` to use new errors ([#1582](https://github.com/project-serum/anchor/pull/1582)). -* client: Fix `Cluster`'s `FromStr` implementation ([#1362](https://github.com/project-serum/anchor/pull/1362)). -* lang: Implement `Key` for `Pubkey` again, so `associated_token::*` constraints can use pubkey targets again ([#1601](https://github.com/project-serum/anchor/pull/1601)). -* lang: Adjust error code so `#[error_code]` works with just importing `anchor_lang::error_code` ([#1610](https://github.com/project-serum/anchor/pull/1610)). -* ts: Fix `spl-token` coder account parsing ([#1604](https://github.com/project-serum/anchor/pull/1604)). -* cli: Fix `npm install` fallback if `yarn` install doesn't work ([#1643](https://github.com/project-serum/anchor/pull/1643)). -* lang: Fix bug where `owner = ` would not compile because of missing type annotation ([#1648](https://github.com/project-serum/anchor/pull/1648)). -* ts: Adjust `send` and `simulate` functions in `provider.ts`, so they use the return value of `Wallet.signTransaction`([#1527](https://github.com/project-serum/anchor/pull/1527)). - -### Breaking - -* ts: Mark `transaction`, `instruction`, `simulate` and `rpc` program namespaces as deprecated in favor of `methods` ([#1539](https://github.com/project-serum/anchor/pull/1539)). -* ts: No longer allow manual setting of globally resolvable program public keys in `methods#accounts()`. ([#1548][https://github.com/project-serum/anchor/pull/1548]) -* lang/ts: Events are now emitted using the `sol_log_data` syscall ([#1608](https://github.com/project-serum/anchor/pull/1608)). -* lang: Remove space calculation using `#[derive(Default)]` ([#1519](https://github.com/project-serum/anchor/pull/1519)). -* lang: Add support for logging expected and actual values and pubkeys. Add `require_eq` and `require_keys_eq` macros. Add default error code to `require` macro ([#1572](https://github.com/project-serum/anchor/pull/1572)). -* lang: Add `system_program` CPI wrapper functions. Make `system_program` module public instead of re-exporting `system_program::System`([#1629](https://github.com/project-serum/anchor/pull/1629)). -* cli: `avm use` no long prompts [y/n] if an install is needed first - it just tells the user to `avm install` ([#1565](https://github.com/project-serum/anchor/pull/1565)) -* ts: Add `AnchorError` with program stack and also a program stack for non-`AnchorError` errors ([#1640](https://github.com/project-serum/anchor/pull/1640)). `AnchorError` is not returned for `processed` tx that have `skipPreflight` set to `true` (it falls back to `ProgramError` or the raw solana library error). - -## [0.22.1] - 2022-02-28 - -### Fixes - -* cli: Fix rust template ([#1488](https://github.com/project-serum/anchor/pull/1488)). -* lang: Handle array sizes with variable sizes in events and array size casting in IDL parsing ([#1485](https://github.com/project-serum/anchor/pull/1485)) - - -## [0.22.0] - 2022-02-20 - -### Features - -* lang: Add check that declared id == program id ([#1451](https://github.com/project-serum/anchor/pull/1451)). -* ts: Added float types support ([#1425](https://github.com/project-serum/anchor/pull/1425)). -* cli: Add `--skip-lint` option to disable check linting introduced in ([#1452](https://github.com/project-serum/anchor/pull/1452)) for rapid prototyping ([#1482](https://github.com/project-serum/anchor/pull/1482)). - -### Fixes - -* ts: Allow nullable types for `Option` mapped types ([#1428](https://github.com/project-serum/anchor/pull/1428)). - -### Breaking - -* lang: Enforce that the payer for an init-ed account be marked `mut` ([#1271](https://github.com/project-serum/anchor/pull/1271)). -* lang: All error-related code is now in the error module ([#1426](https://github.com/project-serum/anchor/pull/1426)). -* lang: Require doc comments when using AccountInfo or UncheckedAccount types ([#1452](https://github.com/project-serum/anchor/pull/1452)). -* lang: add [`error!`](https://docs.rs/anchor-lang/latest/anchor_lang/prelude/macro.error.html) and [`err!`](https://docs.rs/anchor-lang/latest/anchor_lang/prelude/macro.err.html) macro and `Result` type ([#1462](https://github.com/project-serum/anchor/pull/1462)). -This change will break most programs. Do the following to upgrade: - * change all `ProgramResult`'s to `Result<()>` - * change `#[error]` to `#[error_code]` - * change all `Err(MyError::SomeError.into())` to `Err(error!(MyError::SomeError))` and all `Err(ProgramError::SomeProgramError)` to `Err(ProgramError::SomeProgramError.into())` or `Err(Error::from(ProgramError::SomeProgramError).with_source(source!()))` to provide file and line source of the error (`with_source` is most useful with `ProgramError`s. `error!` already adds source information for custom and anchor internal errors). - * change all `solana_program::program::invoke()` to `solana_program::program::invoke().map_err(Into::into)` and `solana_program::program::invoke_signed()` to `solana_program::program::invoke_signed().map_err(Into::into)` - -## [0.21.0] - 2022-02-07 - -### Fixes - -* ts: Fix the root type declaration of the `Wallet` / `NodeWallet` class ([#1363](https://github.com/project-serum/anchor/pull/1363)). -* ts: Improve type mapping of Account fields into Typescript with additional support for `Option` and `Vec` types ([#1393](https://github.com/project-serum/anchor/pull/1393)). - -### Features - -* lang: Add `seeds::program` constraint for specifying which program_id to use when deriving PDAs ([#1197](https://github.com/project-serum/anchor/pull/1197)). -* lang: `Context` now has a new `bumps: BTree` argument, mapping account name to bump seed "found" by the accounts context. This allows one to access bump seeds without having to pass them in from the client or recalculate them in the handler ([#1367](https://github.com/project-serum/anchor/pull/1367)). -* lang, ts: Automatically infer PDA addresses ([#1331](https://github.com/project-serum/anchor/pull/1331)). -* ts: Remove error logging in the event parser when log websocket encounters a program error ([#1313](https://github.com/project-serum/anchor/pull/1313)). -* ts: Add new `methods` namespace to the program client, introducing a more ergonomic builder API ([#1324](https://github.com/project-serum/anchor/pull/1324)). -* ts: Add registry utility for fetching the latest verified build ([#1371](https://github.com/project-serum/anchor/pull/1371)). -* cli: Expose the solana-test-validator --account flag in Anchor.toml via [[test.validator.account]] ([#1366](https://github.com/project-serum/anchor/pull/1366)). -* cli: Add avm, a tool for managing anchor-cli versions ([#1385](https://github.com/project-serum/anchor/pull/1385)). - -### Breaking - -* lang: Put `init_if_needed` behind a feature flag to decrease wrong usage ([#1258](https://github.com/project-serum/anchor/pull/1258)). -* lang: rename `loader_account` module to `account_loader` module ([#1279](https://github.com/project-serum/anchor/pull/1279)) -* lang: The `Accounts` trait's `try_accounts` method now has an additional `bumps: &mut BTreeMap` argument, which accumulates bump seeds ([#1367](https://github.com/project-serum/anchor/pull/1367)). -* lang: Providing `bump = ` targets with `init` will now error. On `init` only, it is required to use `bump` without a target and access the seed inside function handlers via `ctx.bumps.get("` target to minimize compute units used ([#1380](https://github.com/project-serum/anchor/pull/1380)). -* ts: `Coder` is now an interface and the existing class has been renamed to `BorshCoder`. This change allows the generation of Anchor clients for non anchor programs ([#1259](https://github.com/project-serum/anchor/pull/1259/files)). -* cli: [[test.clone]] key in Anchor.toml is renamed to [[test.validator.clone]] ([#1366](https://github.com/project-serum/anchor/pull/1366)). - - -## [0.20.1] - 2022-01-09 - -### Fixes - -* lang: Improved error msgs when required programs are missing when using the `init` constraint([#1257](https://github.com/project-serum/anchor/pull/1257)) - -### Features - -* lang: Allow repr overrides for zero copy accounts ([#1273](https://github.com/project-serum/anchor/pull/1273)). - -## [0.20.0] - 2022-01-06 - -### Fixes - -* lang: `init_if_needed` now checks rent exemption when init is not needed ([#1250](https://github.com/project-serum/anchor/pull/1250)). -* lang: Add missing owner check when `associated_token::authority` is used ([#1240](https://github.com/project-serum/anchor/pull/1240)). -* ts: Add type declarations for conditional `workspace` and `Wallet` exports ([#1137](https://github.com/project-serum/anchor/pull/1137)). -* ts: Change commitment message `recent` to `processed` and `max` to `finalized` ([#1128](https://github.com/project-serum/anchor/pull/1128)) -* ts: fix `translateAddress` which currently leads to failing browser code. Now uses `PublicKey` constructor instead of prototype chain constructor name checking which doesn't work in the presence of code minifying/mangling([#1138](https://github.com/project-serum/anchor/pull/1138)) -* lang: add missing check that verifies that account is ATA when using `init_if_needed` and init is not needed([#1221](https://github.com/project-serum/anchor/pull/1221)) - -### Features - -* lang: Add `programdata_address: Option` field to `Program` account. Will be populated if account is a program owned by the upgradable bpf loader ([#1125](https://github.com/project-serum/anchor/pull/1125)) -* lang,ts,ci,cli,docs: update solana toolchain to version 1.8.5([#1133](https://github.com/project-serum/anchor/pull/1133)). -* lang: Account wrappers for non-Anchor programs no longer have to implement the `serialize` function because it has a default impl now. Similarly, they no longer have to implement `try_deserialize` which now delegates to `try_deserialize_unchecked` by default([#1156](https://github.com/project-serum/anchor/pull/1156)). -* lang: Add `set_inner` method to `Account<'a, T>` to enable easy updates ([#1177](https://github.com/project-serum/anchor/pull/1177)). -* lang: Handle arrays with const as length ([#968](https://github.com/project-serum/anchor/pull/968)). -* ts: Add optional commitment argument to `fetch` and `fetchMultiple` ([#1171](https://github.com/project-serum/anchor/pull/1171)). -* lang: Implement `AsRef` for `Account<'a, T>`([#1173](https://github.com/project-serum/anchor/pull/1173)) -* cli: Add `anchor expand` command which wraps around `cargo expand` ([#1160](https://github.com/project-serum/anchor/pull/1160)) - -### Breaking - -* client: Client::new and Client::new_with_options now accept `Rc` instead of `Keypair` ([#975](https://github.com/project-serum/anchor/pull/975)). -* lang, ts: Change error enum name and message for 'wrong program ownership' account validation ([#1154](https://github.com/project-serum/anchor/pull/1154)). -* lang: Change from `#[repr(packed)]` to `#[repr(C)]` for zero copy accounts ([#1106](https://github.com/project-serum/anchor/pull/1106)). -* lang: Account types can now be found either in the `prelude` module or the `accounts` module but not longer directly under the root. -Deprecated account types are no longer imported by the prelude ([#1208](https://github.com/project-serum/anchor/pull/1208)). - -## [0.19.0] - 2021-12-08 - -### Fixes - -* lang: Add `deprecated` attribute to `ProgramAccount` ([#1014](https://github.com/project-serum/anchor/pull/1014)). -* cli: Add version number from programs `Cargo.toml` into extracted IDL ([#1061](https://github.com/project-serum/anchor/pull/1061)). -* lang: Add `deprecated` attribute to `Loader`([#1078](https://github.com/project-serum/anchor/pull/1078)). -* lang: the `init_if_needed` attribute now checks that given attributes (e.g. space, owner, token::authority etc.) are validated even when init is not needed ([#1096](https://github.com/project-serum/anchor/pull/1096)). - -### Features - -* lang: Add `ErrorCode::AccountNotInitialized` error to separate the situation when the account has the wrong owner from when it does not exist (#[1024](https://github.com/project-serum/anchor/pull/1024)). -* lang: Called instructions now log their name by default. This can be turned off with the `no-log-ix-name` flag ([#1057](https://github.com/project-serum/anchor/pull/1057)). -* lang: `ProgramData` and `UpgradableLoaderState` can now be passed into `Account` as generics. see [UpgradeableLoaderState](https://docs.rs/solana-program/latest/solana_program/bpf_loader_upgradeable/enum.UpgradeableLoaderState.html). `UpgradableLoaderState` can also be matched on to get `ProgramData`, but when `ProgramData` is used instead, anchor does the serialization and checking that it is actually program data for you ([#1095](https://github.com/project-serum/anchor/pull/1095)). -* ts: Add better error msgs in the ts client if something wrong (i.e. not a pubkey or a string) is passed in as an account in an instruction accounts object ([#1098](https://github.com/project-serum/anchor/pull/1098)). -* ts: Add inputs `postInstructions` and `preInstructions` as a replacement for (the now deprecated) `instructions` ([#1007](https://github.com/project-serum/anchor/pull/1007)). -* ts: Add `getAccountInfo` helper method to account namespace/client ([#1084](https://github.com/project-serum/anchor/pull/1084)). - -### Breaking - -* lang, ts: Error codes have been mapped to new numbers to allow for more errors per namespace ([#1096](https://github.com/project-serum/anchor/pull/1096)). - -## [0.18.2] - 2021-11-14 - -* cli: Replace global JavaScript dependency installs with local. - -### Features - -* lang: Add `SystemAccount<'info>` account type for generic wallet addresses or accounts owned by the system program ([#954](https://github.com/project-serum/anchor/pull/954)) - -### Fixes - -* cli: fix dns in NODE_OPTIONS ([#928](https://github.com/project-serum/anchor/pull/928)). -* cli: output TypeScript IDL in `idl parse` subcommand ([#941](https://github.com/project-serum/anchor/pull/941)). -* cli: Add fields `os` and `cpu` to npm package `@project-serum/anchor-cli` ([#976](https://github.com/project-serum/anchor/pull/976)). -* cli: Allow specify output directory for TypeScript IDL ([#940](https://github.com/project-serum/anchor/pull/940)). - -### Breaking - -* spl: Move permissioned markets into dex repository ([#962](https://github.com/project-serum/anchor/pull/962)). - -## [0.18.0] - 2021-10-24 - -### Features - -* cli: Add support for configuration options for `solana-test-validator` in Anchor.toml ([#834](https://github.com/project-serum/anchor/pull/834)). -* cli: `target/types` directory now created on build to store a TypeScript types file for each program's IDL ([#795](https://github.com/project-serum/anchor/pull/795)). -* ts: `Program` can now be typed with an IDL type ([#795](https://github.com/project-serum/anchor/pull/795)). -* lang: Add `mint::freeze_authority` keyword for mint initialization within `#[derive(Accounts)]` ([#835](https://github.com/project-serum/anchor/pull/835)). -* lang: Add `AccountLoader` type for `zero_copy` accounts with support for CPI ([#792](https://github.com/project-serum/anchor/pull/792)). -* lang: Add `#[account(init_if_needed)]` keyword for allowing one to invoke the same instruction even if the account was created already ([#906](https://github.com/project-serum/anchor/pull/906)). -* lang: Add custom errors support for raw constraints ([#905](https://github.com/project-serum/anchor/pull/905)). -* lang, cli, spl: Update solana toolchain to v1.8.0 ([#886](https://github.com/project-serum/anchor/pull/886)). -* lang: Add custom errors support for `signer`, `mut`, `has_one`, `owner`, raw constraints and `address` ([#905](https://github.com/project-serum/anchor/pull/905), [#913](https://github.com/project-serum/anchor/pull/913)). - -### Breaking - -* lang: Accounts marked with the `#[account(signer)]` constraint now enforce signer when the `"cpi"` feature is enabled ([#849](https://github.com/project-serum/anchor/pull/849)). - -## [0.17.0] - 2021-10-03 - -### Features - -* cli: Add `localnet` command for starting a local `solana-test-validator` with the workspace deployed ([#820](https://github.com/project-serum/anchor/pull/820)). - -### Breaking - -* `CpiContext` accounts must now be used with the accounts struct generated in the `crate::cpi::accounts::*` module. These structs correspond to the accounts context for each instruction, except that each field is of type `AccountInfo` ([#824](https://github.com/project-serum/anchor/pull/824)). - -## [0.16.2] - 2021-09-27 - -### Features - -* lang: Add `--detach` flag to `anchor test` ([#770](https://github.com/project-serum/anchor/pull/770)). -* lang: Add `associated_token` keyword for initializing associated token accounts within `#[derive(Accounts)]` ([#790](https://github.com/project-serum/anchor/pull/790)). -* cli: Allow passing through cargo flags for build command ([#719](https://github.com/project-serum/anchor/pull/719)). -* cli: Allow passing through cargo flags for test, verify, and publish commands ([#804](https://github.com/project-serum/anchor/pull/804)). - -### Fixes - -* lang: Generated `AccountMeta`s for Rust clients now properly set the `isSigner` field ([#762](https://github.com/project-serum/anchor/pull/762)). - -## [0.16.1] - 2021-09-17 - -### Fixes - -* lang: `Signer` type now sets isSigner to true in the IDL ([#750](https://github.com/project-serum/anchor/pull/750)). - -## [0.16.0] - 2021-09-16 - -### Features - -* lang: `Program` type introduced for executable accounts ([#705](https://github.com/project-serum/anchor/pull/705)). -* lang: `Signer` type introduced for signing accounts where data is not used ([#705](https://github.com/project-serum/anchor/pull/705)). -* lang: `UncheckedAccount` type introduced as a preferred alias for `AccountInfo` ([#745](https://github.com/project-serum/anchor/pull/745)). - -### Breaking Changes - -* lang: `#[account(owner = )]` now requires a `Pubkey` instead of an account ([#691](https://github.com/project-serum/anchor/pull/691)). - -## [0.15.0] - 2021-09-07 - -### Features - -* lang: Add new `Account` type to replace `ProgramAccount` and `CpiAccount`, both of which are deprecated ([#686](https://github.com/project-serum/anchor/pull/686)). -* lang: `Box` can be used with `Account` types to reduce stack usage ([#686](https://github.com/project-serum/anchor/pull/686)). -* lang: Add `Owner` trait, which is automatically implemented by all `#[account]` structs ([#686](https://github.com/project-serum/anchor/pull/686)). -* lang: Check that ProgramAccount writable before mut borrow (`anchor-debug` only) ([#681](https://github.com/project-serum/anchor/pull/681)). - -### Breaking Changes - -* lang: All programs must now define their program id in source via `declare_id!` ([#686](https://github.com/project-serum/anchor/pull/686)). - -## [0.14.0] - 2021-09-02 - -### Features - -* lang: Ignore `Unnamed` structs instead of panic ([#605](https://github.com/project-serum/anchor/pull/605)). -* lang: Add constraints for initializing mint accounts as pdas, `#[account(init, seeds = [...], mint::decimals = , mint::authority = )]` ([#562](https://github.com/project-serum/anchor/pull/562)). -* lang: Add `AsRef` for `AccountInfo` wrappers ([#652](https://github.com/project-serum/anchor/pull/652)). -* lang: Optimize `trait Key` by removing `AccountInfo` cloning ([#652](https://github.com/project-serum/anchor/pull/652)). -* cli, client, lang: Update solana toolchain to v1.7.11 ([#653](https://github.com/project-serum/anchor/pull/653)). - -### Breaking Changes - -* lang: Change `#[account(init, seeds = [...], token = , authority = )]` to `#[account(init, token::mint = token::authority = )]` ([#562](https://github.com/project-serum/anchor/pull/562)). -* lang: `#[associated]` and `#[account(associated = , with = )]` are both removed ([#612](https://github.com/project-serum/anchor/pull/612)). -* cli: Removed `anchor launch` command ([#634](https://github.com/project-serum/anchor/pull/634)). -* lang: `#[account(init)]` now creates the account inside the same instruction to be consistent with initializing PDAs. To maintain the old behavior of `init`, replace it with `#[account(zero)]` ([#641](https://github.com/project-serum/anchor/pull/641)). -* lang: `bump` must be provided when using the `seeds` constraint. This has been added as an extra safety constraint to ensure that whenever a PDA is initialized via a constraint the bump used is the one created by `Pubkey::find_program_address` ([#641](https://github.com/project-serum/anchor/pull/641)). -* lang: `try_from_init` has been removed from `Loader`, `ProgramAccount`, and `CpiAccount` and replaced with `try_from_unchecked` ([#641](https://github.com/project-serum/anchor/pull/641)). -* lang: Remove `AccountsInit` trait ([#641](https://github.com/project-serum/anchor/pull/641)). -* lang: `try_from` methods for `ProgramAccount`, `Loader`, and `ProgramState` now take in an additional `program_id: &Pubkey` parameter ([#660](https://github.com/project-serum/anchor/pull/660)). - -## [0.13.2] - 2021-08-11 - -### Fixes - -* cli: Fix `anchor init` command "Workspace not found" regression ([#598](https://github.com/project-serum/anchor/pull/598)). - -## [0.13.1] - 2021-08-10 - -### Features - -* cli: Programs embedded into genesis during tests will produce program logs ([#594](https://github.com/project-serum/anchor/pull/594)). - -### Fixes - -* cli: Allows Cargo.lock to exist in workspace subdirectories when publishing ([#593](https://github.com/project-serum/anchor/pull/593)). - -## [0.13.0] - 2021-08-08 - -### Features - -* cli: Adds a `[registry]` section in the Anchor toml ([#570](https://github.com/project-serum/anchor/pull/570)). -* cli: Adds the `anchor login ` command ([#570](https://github.com/project-serum/anchor/pull/570)). -* cli: Adds the `anchor publish ` command ([#570](https://github.com/project-serum/anchor/pull/570)). -* cli: Adds a root level `anchor_version` field to the Anchor.toml for specifying the anchor docker image to use for verifiable builds ([#570](https://github.com/project-serum/anchor/pull/570)). -* cli: Adds a root level `solana_version` field to the Anchor.toml for specifying the solana toolchain to use for verifiable builds ([#570](https://github.com/project-serum/anchor/pull/570)). -* lang: Dynamically fetch rent sysvar for when using `init` ([#587](https://github.com/project-serum/anchor/pull/587)). - -### Breaking - -* cli: `[clusters.]` Anchor.toml section has been renamed to `[programs.]` ([#570](https://github.com/project-serum/anchor/pull/570)). -* cli: `[workspace]` member and exclude arrays must now be filepaths relative to the workpsace root ([#570](https://github.com/project-serum/anchor/pull/570)). - -## [0.12.0] - 2021-08-03 - -### Features - -* cli: Add keys `members` / `exclude` in config `programs` section ([#546](https://github.com/project-serum/anchor/pull/546)). -* cli: Allow program address configuration for test command through `clusters.localnet` ([#554](https://github.com/project-serum/anchor/pull/554)). -* lang: IDLs are now parsed from the entire crate ([#517](https://github.com/project-serum/anchor/pull/517)). -* spl: Dex permissioned markets proxy ([#519](https://github.com/project-serum/anchor/pull/519), [#543](https://github.com/project-serum/anchor/pull/543)). - -### Breaking Changes - -* ts: Use `hex` by default for decoding Instruction ([#547](https://github.com/project-serum/anchor/pull/547)). -* lang: `CpiAccount::reload` mutates the existing struct instead of returning a new one ([#526](https://github.com/project-serum/anchor/pull/526)). -* cli: Anchor.toml now requires an explicit `[scripts]` test command ([#550](https://github.com/project-serum/anchor/pull/550)). - -## [0.11.1] - 2021-07-09 - -### Features - -* lang: Adds `require` macro for specifying assertions that return error codes on failure ([#483](https://github.com/project-serum/anchor/pull/483)). -* lang: Allow one to specify arbitrary programs as the owner when creating PDA ([#483](https://github.com/project-serum/anchor/pull/483)). -* lang: A new `bump` keyword is added to the accounts constraints, which is used to add an optional bump seed to the end of a `seeds` array. When used in conjunction with *both* `init` and `seeds`, then the program executes `find_program_address` to assert that the given bump is the canonical bump ([#483](https://github.com/project-serum/anchor/pull/483)). - -### Fixes - -* lang: Preserve all instruction data for fallback functions ([#483](https://github.com/project-serum/anchor/pull/483)). -* ts: Event listener not firing when creating associated accounts ([#356](https://github.com/project-serum/anchor/issues/356)). - -## [0.11.0] - 2021-07-03 - -### Features - -* lang: Add fallback functions ([#457](https://github.com/project-serum/anchor/pull/457)). -* lang: Add feature flag for using the old state account discriminator. This is a temporary flag for those with programs built prior to v0.7.0 but want to use the latest Anchor version. Expect this to be removed in a future version ([#446](https://github.com/project-serum/anchor/pull/446)). -* lang: Add generic support to Accounts ([#496](https://github.com/project-serum/anchor/pull/496)). - -### Breaking Changes - -* cli: Remove `.spec` suffix on TypeScript tests files ([#441](https://github.com/project-serum/anchor/pull/441)). -* lang: Remove `belongs_to` constraint ([#459](https://github.com/project-serum/anchor/pull/459)). - -## [0.10.0] - 2021-06-27 - -### Features - -* lang: Add `#[account(address = )]` constraint for asserting the address of an account ([#400](https://github.com/project-serum/anchor/pull/400)). -* lang: Add `#[account(init, token = , authority = ...)]` constraint for initializing SPL token accounts as program derived addresses for the program. Can be used when initialized via `seeds` or `associated` ([#400](https://github.com/project-serum/anchor/pull/400)). -* lang: Add `associated_seeds!` macro for generating signer seeds for CPIs signed by an `#[account(associated = )]` account ([#400](https://github.com/project-serum/anchor/pull/400)). -* cli: Add `[scripts]` section to the Anchor.toml for specifying workspace scripts that can be run via `anchor run diff --git a/docs/src/.vuepress/components/OtherComponent.vue b/docs/src/.vuepress/components/OtherComponent.vue deleted file mode 100755 index 1d97c7ca..00000000 --- a/docs/src/.vuepress/components/OtherComponent.vue +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/docs/src/.vuepress/components/demo-component.vue b/docs/src/.vuepress/components/demo-component.vue deleted file mode 100755 index 7d49de79..00000000 --- a/docs/src/.vuepress/components/demo-component.vue +++ /dev/null @@ -1,15 +0,0 @@ - - - diff --git a/docs/src/.vuepress/config.js b/docs/src/.vuepress/config.js deleted file mode 100755 index 6c921d62..00000000 --- a/docs/src/.vuepress/config.js +++ /dev/null @@ -1,104 +0,0 @@ -const { description } = require("../../package"); - -module.exports = { - base: "/anchor/", - /** - * Ref:https://v1.vuepress.vuejs.org/config/#title - */ - title: "⚓ Anchor", - /** - * Ref:https://v1.vuepress.vuejs.org/config/#description - */ - description: description, - - /** - * Extra tags to be injected to the page HTML `` - * - * ref:https://v1.vuepress.vuejs.org/config/#head - */ - head: [ - ["link", { rel: "icon", href: "data:image/svg+xml,"}], - ["meta", { name: "theme-color", content: "#3eaf7c" }], - ["meta", { name: "apple-mobile-web-app-capable", content: "yes" }], - [ - "meta", - { name: "apple-mobile-web-app-status-bar-style", content: "black" }, - ], - ], - - theme: "default-prefers-color-scheme", - - /** - * Theme configuration, here is the default theme configuration for VuePress. - * - * ref:https://v1.vuepress.vuejs.org/theme/default-theme-config.html - */ - themeConfig: { - repo: "", - editLinks: false, - docsDir: "", - editLinkText: "", - lastUpdated: false, - sidebarDepth: 2, - sidebar: [ - { - collapsable: false, - title: "Getting Started", - children: [ - "/getting-started/introduction", - "/getting-started/installation", - ], - }, - { - collapsable: false, - title: "Teams", - children: [ - "/getting-started/projects", - ], - }, - { - collapsable: false, - title: "Tutorials", - children: [ - "/tutorials/tutorial-0", - "/tutorials/tutorial-1", - "/tutorials/tutorial-2", - "/tutorials/tutorial-3", - "/tutorials/tutorial-4", - ], - }, - { - collapsable: false, - title: "CLI", - children: [ - "/cli/commands", - ], - }, - { - collapsable: false, - title: "Source Verification", - children: [ - "/getting-started/verification", - "/getting-started/publishing", - ], - }, - ], - - nav: [ - { text: "The Anchor Book", link: "https://book.anchor-lang.com" }, - { text: "Rust", link: "https://docs.rs/anchor-lang/latest/anchor_lang/" }, - { text: "TypeScript", link: "https://project-serum.github.io/anchor/ts/index.html" }, - { text: "GitHub", link: "https://github.com/project-serum/anchor" } - ], - }, - - /** - * Apply plugins,ref:https://v1.vuepress.vuejs.org/zh/plugin/ - */ - plugins: [ - "dehydrate", - "@vuepress/plugin-back-to-top", - "@vuepress/plugin-medium-zoom", - "@xiaopanda/vuepress-plugin-code-copy", - ], -}; diff --git a/docs/src/.vuepress/enhanceApp.js b/docs/src/.vuepress/enhanceApp.js deleted file mode 100755 index d3fefb51..00000000 --- a/docs/src/.vuepress/enhanceApp.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Client app enhancement file. - * - * https://v1.vuepress.vuejs.org/guide/basic-config.html#app-level-enhancements - */ - -export default ({ - Vue, // the version of Vue being used in the VuePress app - options, // the options for the root Vue instance - router, // the router instance for the app - siteData, // site metadata -}) => { - // ...apply enhancements for the site. - router.addRoutes([{ path: "/", redirect: "/getting-started/introduction" }]); -}; diff --git a/docs/src/.vuepress/styles/index.styl b/docs/src/.vuepress/styles/index.styl deleted file mode 100755 index 420feb93..00000000 --- a/docs/src/.vuepress/styles/index.styl +++ /dev/null @@ -1,8 +0,0 @@ -/** - * Custom Styles here. - * - * ref:https://v1.vuepress.vuejs.org/config/#index-styl - */ - -.home .hero img - max-width 450px!important diff --git a/docs/src/.vuepress/styles/palette.styl b/docs/src/.vuepress/styles/palette.styl deleted file mode 100755 index 6490cb35..00000000 --- a/docs/src/.vuepress/styles/palette.styl +++ /dev/null @@ -1,10 +0,0 @@ -/** - * Custom palette here. - * - * ref:https://v1.vuepress.vuejs.org/zh/config/#palette-styl - */ - -$accentColor = #3eaf7c -$textColor = #2c3e50 -$borderColor = #eaecef -$codeBgColor = #282c34 diff --git a/docs/src/cli/commands.md b/docs/src/cli/commands.md deleted file mode 100644 index ee5a18ee..00000000 --- a/docs/src/cli/commands.md +++ /dev/null @@ -1,245 +0,0 @@ -# Commands - -A CLI is provided to support building and managing an Anchor workspace. -For a comprehensive list of commands and options, run `anchor -h` on any -of the following subcommands. - -``` -anchor-cli - -USAGE: - anchor - -FLAGS: - -h, --help Prints help information - -V, --version Prints version information - -SUBCOMMANDS: - build Builds the workspace - cluster Cluster commands - deploy Deploys each program in the workspace - expand Expands the macros of a program or the workspace - help Prints this message or the help of the given subcommand(s) - idl Commands for interacting with interface definitions - init Initializes a workspace - migrate Runs the deploy migration script - new Creates a new program - test Runs integration tests against a localnetwork - upgrade Upgrades a single program. The configured wallet must be the upgrade authority - verify Verifies the on-chain bytecode matches the locally compiled artifact. Run this - command inside a program subdirectory, i.e., in the dir containing the program's - Cargo.toml -``` - - -## Build - -``` -anchor build -``` - -Builds programs in the workspace targeting Solana's BPF runtime and emitting IDLs in the `target/idl` directory. - -``` -anchor build --verifiable -``` - -Runs the build inside a docker image so that the output binary is deterministic (assuming a Cargo.lock file is used). This command must be run from within a single crate subdirectory within the workspace. For example, `programs//`. - -## Cluster - -### Cluster list - -``` -anchor cluster list -``` - -This lists cluster endpoints: - -``` -Cluster Endpoints: - -* Mainnet - https://solana-api.projectserum.com -* Mainnet - https://api.mainnet-beta.solana.com -* Devnet - https://api.devnet.solana.com -* Testnet - https://api.testnet.solana.com -``` - -## Deploy - -``` -anchor deploy -``` - -Deploys all programs in the workspace to the configured cluster. - -::: tip Note -This is different from the `solana program deploy` command, because everytime it's run -it will generate a *new* program address. -::: - -## Expand - -``` -anchor expand -``` - -If run inside a program folder, expands the macros of the program. - -If run in the workspace but outside a program folder, expands the macros of the workspace. - -If run with the `--program-name` option, expand only the given program. - -## Idl - -The `idl` subcommand provides commands for interacting with interface definition files. -It's recommended to use these commands to store an IDL on chain, at a deterministic -address, as a function of nothing but the the program's ID. This -allows us to generate clients for a program using nothing but the program ID. - -### Idl Init - -``` -anchor idl init -f -``` - -Creates an idl account, writing the given `` file into a program owned account. By default, the size of the account is double the size of the IDL, -allowing room for growth in case the idl needs to be upgraded in the future. - -### Idl Fetch - -``` -anchor idl fetch -o -``` - -Fetches an IDL from the configured blockchain. For example, make sure -your `Anchor.toml` is pointing to the `mainnet` cluster and run - -``` -anchor idl fetch GrAkKfEpTKQuVHG2Y97Y2FF4i7y7Q5AHLK94JBy7Y5yv -``` - -### Idl Authority - -``` -anchor idl authority -``` - -Outputs the IDL account's authority. This is the wallet that has the ability to -update the IDL. - -### Idl Erase Authority - -``` -anchor idl erase-authority -p -``` - -Erases the IDL account's authority so that upgrades can no longer occur. The -configured wallet must be the current authority. - -### Idl Upgrade - -``` -anchor idl upgrade -f -``` - -Upgrades the IDL file on chain to the new `target/idl/program.json` idl. -The configured wallet must be the current authority. - -``` -anchor idl set-authority -n -p -``` - -Sets a new authority on the IDL account. Both the `new-authority` and `program-id` -must be encoded in base 58. - -## Init - -``` -anchor init -``` - -Initializes a project workspace with the following structure. - -* `Anchor.toml`: Anchor configuration file. -* `Cargo.toml`: Rust workspace configuration file. -* `package.json`: JavaScript dependencies file. -* `programs/`: Directory for Solana program crates. -* `app/`: Directory for your application frontend. -* `tests/`: Directory for JavaScript integration tests. -* `migrations/deploy.js`: Deploy script. - -## Migrate - -``` -anchor migrate -``` - -Runs the deploy script located at `migrations/deploy.js`, injecting a provider configured -from the workspace's `Anchor.toml`. For example, - -```javascript -// File: migrations/deploys.js - -const anchor = require("@project-serum/anchor"); - -module.exports = async function (provider) { - anchor.setProvider(provider); - - // Add your deploy script here. -} -``` - -Migrations are a new feature -and only support this simple deploy script at the moment. - -## New - -``` -anchor new -``` - -Creates a new program in the workspace's `programs/` directory initialized with boilerplate. - -## Test - -``` -anchor test -``` - -Run an integration test suit against the configured cluster, deploying new versions -of all workspace programs before running them. - -If the configured network is a localnet, then automatically starts the localnetwork and runs -the test. - -::: tip Note -Be sure to shutdown any other local validators, otherwise `anchor test` will fail to run. - -If you'd prefer to run the program against your local validator use `anchor test --skip-local-validator`. -::: - -When running tests we stream program logs to `.anchor/program-logs/
..log` - -::: tip Note -The Anchor workflow [recommends](https://www.parity.io/paritys-checklist-for-secure-smart-contract-development/) -to test your program using integration tests in a language other -than Rust to make sure that bugs related to syntax misunderstandings -are coverable with tests and not just replicated in tests. -::: - -## Upgrade - -``` -anchor upgrade --program-id -``` - -Uses Solana's upgradeable BPF loader to upgrade the on chain program code. - -## Verify - -``` -anchor verify -``` - -Verifies the on-chain bytecode matches the locally compiled artifact. diff --git a/docs/src/getting-started/installation.md b/docs/src/getting-started/installation.md deleted file mode 100644 index bc7f8d0c..00000000 --- a/docs/src/getting-started/installation.md +++ /dev/null @@ -1,74 +0,0 @@ -# Installing Dependencies - -To get started, make sure to setup all the prerequisite tools on your local machine -(an installer has not yet been developed). - -## Install Rust - -For an introduction to Rust, see the excellent Rust [book](https://doc.rust-lang.org/book/). - -```bash -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -source $HOME/.cargo/env -rustup component add rustfmt -``` - -## Install Solana - -See the solana [docs](https://docs.solana.com/cli/install-solana-cli-tools) for installation instructions. On macOS and Linux, - -```bash -sh -c "$(curl -sSfL https://release.solana.com/v1.9.1/install)" -``` - -## Install Yarn - -[Yarn](https://yarnpkg.com/) is recommended for JavaScript package management. - -```bash -npm install -g yarn -``` - -## Install Anchor - -### Install using pre-build binary on x86_64 Linux - -Anchor binaries are available via an NPM package [`@project-serum/anchor-cli`](https://www.npmjs.com/package/@project-serum/anchor-cli). Only x86_64 Linux is supported currently, you must build from source for other OS'. - -```bash -npm i -g @project-serum/anchor-cli -``` - -### Build from source for other operating systems - -For now, we can use Cargo to install the CLI. - -```bash -cargo install --git https://github.com/project-serum/anchor --tag v0.24.2 anchor-cli --locked -``` - -On Linux systems you may need to install additional dependencies if `cargo install` fails. On Ubuntu, - -```bash -sudo apt-get update && sudo apt-get upgrade && sudo apt-get install -y pkg-config build-essential libudev-dev -``` - -Now verify the CLI is installed properly. - -```bash -anchor --version -``` - -## Start a Project - -To initialize a new project, simply run: - -```bash -anchor init -``` - -## Minimum version requirements - -| Build tool | Version | -|:------------|:---------------| -| Node.js | v11.0.0 | diff --git a/docs/src/getting-started/introduction.md b/docs/src/getting-started/introduction.md deleted file mode 100644 index ff0827e4..00000000 --- a/docs/src/getting-started/introduction.md +++ /dev/null @@ -1,18 +0,0 @@ -# Introduction - -
This documentation is being sunset in favor of The Anchor Book. At this point in time, either documentation may contain information that the other does not.
- -Anchor is a framework for Solana's [Sealevel](https://medium.com/solana-labs/sealevel-parallel-processing-thousands-of-smart-contracts-d814b378192) runtime providing several convenient developer tools. - -- Rust crates and eDSL for writing Solana programs -- [IDL](https://en.wikipedia.org/wiki/Interface_description_language) specification -- TypeScript package for generating clients from IDL -- CLI and workspace management for developing complete applications - -If you're familiar with developing in Ethereum's [Solidity](https://docs.soliditylang.org/en/v0.7.4/), [Truffle](https://www.trufflesuite.com/), [web3.js](https://github.com/ethereum/web3.js) or Parity's [Ink!](https://github.com/paritytech/ink), then the experience will be familiar. Although the DSL syntax and semantics are targeted at Solana, the high level flow of writing RPC request handlers, emitting an IDL, and generating clients from IDL is the same. - -Here, we'll walk through several tutorials demonstrating how to use Anchor. To skip the tutorials and jump straight to examples, go [here](https://github.com/project-serum/anchor/blob/master/examples). For an introduction to Solana, see the [docs](https://docs.solana.com/developing/programming-model/overview). - -::: tip NOTE -Anchor is in active development, so all APIs are subject to change. If you are one of the early developers to try it out and have feedback, please reach out by [filing an issue](https://github.com/project-serum/anchor/issues/new). This documentation is a work in progress and is expected to change dramatically as features continue to be built out. If you have any problems, consult the [source](https://github.com/project-serum/anchor) or feel free to ask questions on the [Discord](https://discord.gg/JgVgQ82erk). -::: diff --git a/docs/src/getting-started/projects.md b/docs/src/getting-started/projects.md deleted file mode 100644 index 7b087437..00000000 --- a/docs/src/getting-started/projects.md +++ /dev/null @@ -1,24 +0,0 @@ -# Projects - -Open a pull request to add your project to the [list](https://github.com/project-serum/anchor/blob/master/docs/src/getting-started/projects.md). - -* [Serum](https://github.com/project-serum) -* [Synthetify](https://github.com/Synthetify) -* [SolFarm](https://solfarm.io/) -* [Zeta Markets](https://zeta.markets/) -* [Saber](https://saber.so) -* [01](https://01protocol.com/) -* [Parrot Finance](https://parrot.fi/) -* [Marinade Finance](https://marinade.finance/) -* [Aldrin](https://dex.aldrin.com/) -* [Cyclos](https://cyclos.io/) -* [Solend](https://solend.fi) -* [Drift](https://www.drift.trade/) -* [Fabric](https://stake.fsynth.io/) -* [Jet Protocol](https://jetprotocol.io/) -* [Quarry](https://quarry.so/) -* [PsyOptions](https://psyoptions.io/) -* [sosol](https://sosol.app/) -* [Arrow Protocol](https://arrowprotocol.com/) -* [Hubble Protocol](https://hubbleprotocol.io/) - diff --git a/docs/src/getting-started/publishing.md b/docs/src/getting-started/publishing.md deleted file mode 100644 index 130908f0..00000000 --- a/docs/src/getting-started/publishing.md +++ /dev/null @@ -1,88 +0,0 @@ -# Publishing Source - -The Anchor Program Registry at [anchor.projectserum.com](https://anchor.projectserum.com) -hosts a catalog of verified programs on Solana both written with and without Anchor. It is recommended -that authors of smart contracts publish their source to promote best -practices for security and transparency. - -::: tip note -The Anchor Program Registry is currently in alpha testing. For access to publishing -please ask on [Discord](https://discord.gg/rg5ZZPmmTm). -::: - -## Getting Started - -The process for publishing is mostly identical to `crates.io`. - -* Signup for an account [here](https://anchor.projectserum.com/signup). -* Confirm your email by clicking the link sent to your address. -* Navigate to your Username -> Account Settings on the top navbar. -* Click "New Token" in the **API Access** section. -* Run `anchor login ` at the command line. - -And you're ready to interact with the registry. - -## Configuring a Build - -Whether your program is written in Anchor or not, all source being published must -have an `Anchor.toml` to define the build. - -An example `Anchor.toml` config looks as follows, - -```toml -anchor_version = "0.24.2" - -[workspace] -members = ["programs/multisig"] - -[provider] -cluster = "mainnet" -wallet = "~/.config/solana/id.json" - -[programs.mainnet] -multisig = "A9HAbnCwoD6f2NkZobKFf6buJoN9gUVVvX5PoUnDHS6u" - -[programs.localnet] -multisig = "A9HAbnCwoD6f2NkZobKFf6buJoN9gUVVvX5PoUnDHS6u" -``` - -Here there are four sections. - -1. `anchor_version` (optional) - sets the anchor docker image to use. By default, the builder will use the latest version of Anchor. -2. `[workspace]` (optional) - sets the paths--relative to the `Anchor.toml`-- - to all programs in the local - workspace, i.e., the path to the `Cargo.toml` manifest associated with each - program that can be compiled by the `anchor` CLI. For programs using the - standard Anchor workflow, this can be ommitted. For programs not written in Anchor - but still want to publish, this should be added. -3. `[provider]` - configures the wallet and cluster settings. Here, `mainnet` is used because the registry only supports `mainnet` binary verification at the moment. -3. `[programs.mainnet]` - configures each program in the workpace, providing - the `address` of the program to verify. - -::: tip -When defining program in `[programs.mainnet]`, make sure the name provided -matches the **lib** name for your program, which is defined -by your program's Cargo.toml. -::: - -### Examples - -#### Anchor Program - -An example of a toml file for an Anchor program can be found [here](https://anchor.projectserum.com/build/2). - -#### Non Anchor Program - -An example of a toml file for a non-anchor program can be found [here](https://anchor.projectserum.com/build/1). - -## Publishing - -To publish to the Anchor Program Registry, change directories to the `Anchor.toml` -defined root and run - -```bash -anchor publish -``` - -where `` is as defined in `[programs.mainnet]`, i.e., `multisig` -in the example above. diff --git a/docs/src/getting-started/verification.md b/docs/src/getting-started/verification.md deleted file mode 100644 index 67df7a3d..00000000 --- a/docs/src/getting-started/verification.md +++ /dev/null @@ -1,52 +0,0 @@ -# Verifiable Builds - -Building programs with the Solana CLI may embed machine specfic -code into the resulting binary. As a result, building the same program -on different machines may produce different executables. To get around this -problem, one can build inside a docker image with pinned dependencies to produce -a verifiable build. - -Anchor makes this easy by providing CLI commands to build and take care of -docker for you. To get started, first make sure you -[install](https://docs.docker.com/get-docker/) docker on your local machine. - -## Building - -To produce a verifiable build, run - -```bash -anchor build --verifiable -``` - -## Verifying - -To verify a build against a program deployed on mainnet, run - -```bash -anchor verify -p -``` - -where the `` is defined by your program's Cargo.toml. - -If the program has an IDL, it will also check the IDL deployed on chain matches. - -## Images - -A docker image for each version of Anchor is published on [Docker Hub](https://hub.docker.com/r/projectserum/build). They are tagged in the form `projectserum/build:`. For example, to get the image for Anchor `v0.24.2` one can run - -``` -docker pull projectserum/build:v0.24.2 -``` - -## Removing an Image - In the event you run a verifiable build from the CLI and exit prematurely, - it's possible the docker image may still be building in the background. - -To remove, run - -``` -docker rm -f anchor-program -``` - -where `anchor-program` is the name of the image created by default from within -the Anchor CLI. diff --git a/docs/src/index.md b/docs/src/index.md deleted file mode 100644 index 2ef7a6b9..00000000 --- a/docs/src/index.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -home: true -heroText: Anchor -tagline: A framework for building Solana programs -actionText: Get Started → -actionLink: /getting-started/introduction -features: -- title: Security - details: Anchor eliminates many footguns of raw Solana programs by default and allows you to add more security checks without disrupting your business logic. -- title: Code Generation - details: (De)Serialization, cross-program invocations, account initialization, and more. -- title: IDL & Client Generation - details: Anchor generates an IDL based on your program and automatically creates a typescript client with it. -- title: Verifiability - details: Anchor programs can be built verifiably, so users know that the on-chain program matches the code base. -- title: Workspace Management - details: The CLI helps you manage workspaces with multiple programs, e2e tests, and more. -- title: Compatibility - details: Anchor programs can interact with all non-anchor programs on Solana. - -footer: Apache License 2.0 ---- -
This documentation is being sunset in favor of The Anchor Book. At this point in time, either documentation may contain information that the other does not.
diff --git a/docs/src/tutorials/tutorial-0.md b/docs/src/tutorials/tutorial-0.md deleted file mode 100644 index 5ae291b8..00000000 --- a/docs/src/tutorials/tutorial-0.md +++ /dev/null @@ -1,189 +0,0 @@ -# A Minimal Example - -Here, we introduce Anchor's core syntax elements and project workflow. This tutorial assumes all -[prerequisites](../getting-started/installation.md) are installed. - -## Clone the Repo - -To get started, clone the repo. - -```bash -git clone https://github.com/project-serum/anchor -``` - -Next, checkout the tagged branch of the same version of the anchor cli you have installed. - -```bash -git checkout tags/ -``` - -Change directories to the [example](https://github.com/project-serum/anchor/tree/master/examples/tutorial/basic-0). - -```bash -cd anchor/examples/tutorial/basic-0 -``` - -And install any additional JavaScript dependencies: - -```bash -yarn install -``` - -## Starting a Localnet - -In a separate terminal, start a local network. If you're running solana -for the first time, generate a wallet. - -``` -solana-keygen new -``` - -Then run - -``` -solana-test-validator -``` - -Then, shut it down. - -The test validator will be used when testing Anchor programs. Make sure to turn off the validator before you begin testing Anchor programs. - -::: details -As you'll see later, starting a localnet manually like this is not necessary when testing with Anchor, -but is done for educational purposes in this tutorial. -::: - -## Defining a Program - -We define the minimum viable program as follows. - -<<< @/../examples/tutorial/basic-0/programs/basic-0/src/lib.rs - -* `#[program]` First, notice that a program is defined with the `#[program]` attribute, where each -inner method defines an RPC request handler, or, in Solana parlance, an "instruction" -handler. These handlers are the entrypoints to your program that clients may invoke, as -we will see soon. - -* `Context` The first parameter of _every_ RPC handler is the `Context` struct, which is a simple -container for the currently executing `program_id` generic over -`Accounts`--here, the `Initialize` struct. - -* `#[derive(Accounts)]` The `Accounts` derive macro marks a struct containing all the accounts that must be -specified for a given instruction. To understand Accounts on Solana, see the -[docs](https://docs.solana.com/developing/programming-model/accounts). -In subsequent tutorials, we'll demonstrate how an `Accounts` struct can be used to -specify constraints on accounts given to your program. Since this example doesn't touch any -accounts, we skip this (important) detail. - -## Building and Emitting an IDL - -After creating a program, you can use the `anchor` CLI to build and emit an IDL, from which clients -can be generated. - -```bash -anchor build -``` - -::: details -The `build` command is a convenience combining two steps. - -1) `cargo build-bpf` -2) `anchor idl parse -f program/src/lib.rs -o target/idl/basic_0.json`. -::: - -Once run, you should see your build artifacts, as usual, in your `target/` directory. Additionally, -a `target/idl/basic_0.json` file is created. Inspecting its contents you should see - -```json -{ - "version": "0.1.0", - "name": "basic_0", - "instructions": [ - { - "name": "initialize", - "accounts": [], - "args": [] - } - ] -} -``` - -From this file a client can be generated. Note that this file is created by parsing the `src/lib.rs` -file in your program's crate. - -::: tip -If you've developed on Ethereum, the IDL is analogous to the `abi.json`. -::: - -## Deploying - -Once built, we can deploy the program by running - -```bash -anchor deploy -``` - -Take note of the program's deployed address. We'll use it next. - -## Generating a Client - -Now that we've built a program, deployed it to a local cluster, and generated an IDL, -we can use the IDL to generate a client to speak to our on-chain program. For example, -see [client.js](https://github.com/project-serum/anchor/tree/master/examples/tutorial/basic-0/client.js). - -<<< @/../examples/tutorial/basic-0/client.js#main - -Notice how we dynamically created the `initialize` method under -the `rpc` namespace. - -Now, make sure to plugin your program's address into `` (a mild -annoyance that we'll address next). In order to run the client, you'll also need the path -to your wallet's keypair you generated when you ran `solana-keygen new`; you can find it -by running - -```bash -solana config get keypair -``` - -Once you've got it, run the client with the environment variable `ANCHOR_WALLET` set to -that path, e.g. - -```bash -ANCHOR_WALLET= node client.js -``` - -You just successfully created a client and executed a transaction on your localnet. - -## Workspaces - -So far we've seen the basics of how to create, deploy, and make RPCs to a program, but -deploying a program, copy and pasting the address, and explicitly reading -an IDL is all a bit tedious, and can easily get out of hand the more tests and the more -programs you have. For this reason, we introduce the concept of a workspace. - -Inspecting [tests/basic-0.js](https://github.com/project-serum/anchor/tree/master/examples/tutorial/basic-0/tests/basic-0.js), we see the above example can be reduced to - -<<< @/../examples/tutorial/basic-0/tests/basic-0.js#code - -The `workspace` namespace provides access to all programs in the local project and is -automatically updated to reflect the latest deployment, making it easy to change -your program, update your JavaScript, and run your tests in a fast feedback loop. - -::: tip NOTE -For now, the workspace feature is only available when running the `anchor test` command, -which will automatically `build`, `deploy`, and `test` all programs against a localnet -in one command. -::: - -Finally, we can run the test. Don't forget to kill the local validator started earlier. -We won't need to start one manually for any future tutorials. - -```bash -anchor test -``` - -## Next Steps - -We've introduced the basic syntax of writing programs in Anchor along with a productive -workflow for building and testing. However, programs aren't all that interesting without -interacting with persistent state. We'll cover that next. diff --git a/docs/src/tutorials/tutorial-1.md b/docs/src/tutorials/tutorial-1.md deleted file mode 100644 index bb694923..00000000 --- a/docs/src/tutorials/tutorial-1.md +++ /dev/null @@ -1,106 +0,0 @@ -# Arguments and Accounts - -This tutorial covers the basics of creating and mutating accounts using Anchor. -It's recommended to read [Tutorial 0](./tutorial-0.md) first, as this tutorial will -build on top of it. - -## Clone the Repo - -To get started, clone the repo. - -```bash -git clone https://github.com/project-serum/anchor -``` - -Change directories to the [example](https://github.com/project-serum/anchor/tree/master/examples/tutorial/basic-1). - -```bash -cd anchor/examples/tutorial/basic-1 -``` - -And install any additional JavaScript dependencies: - -```bash -yarn install -``` - -## Defining a Program - -We define our program as follows - -<<< @/../examples/tutorial/basic-1/programs/basic-1/src/lib.rs - -Some new syntax elements are introduced here. - -### `initialize` instruction - -First, let's start with the initialize instruction. Notice the `data` argument passed into the program. This argument and any other valid -Rust types can be passed to the instruction to define inputs to the program. - -Additionally, -notice how we take a mutable reference to `my_account` and assign the `data` to it. This leads us to -the `Initialize` struct, deriving `Accounts`. There are two things to notice about `Initialize`. - -1. The `my_account` field is of type `Account<'info, MyAccount>` and the deserialized data structure is `MyAccount`. -2. The `my_account` field is marked with the `init` attribute. This will create a new -account owned by the current program, zero initialized. When using `init`, one must also provide -`payer`, which will fund the account creation, `space`, which defines how large the account should be, -and the `system_program`, which is required by the runtime for creating the account. - -::: details -All accounts created with Anchor are laid out as follows: `8-byte-discriminator || borsh -serialized data`. The 8-byte-discriminator is created from the first 8 bytes of the -`Sha256` hash of the account's type--using the example above, `sha256("account:MyAccount")[..8]`. -The `account:` is a fixed prefix. - -Importantly, this allows a program to know for certain an account is indeed of a given type. -Without it, a program would be vulnerable to account injection attacks, where a malicious user -specifies an account of an unexpected type, causing the program to do unexpected things. - -On account creation, this 8-byte discriminator doesn't exist, since the account storage is -zeroed. The first time an Anchor program mutates an account, this discriminator is prepended -to the account storage array and all subsequent accesses to the account (not decorated with -`#[account(init)]`) will check for this discriminator. -::: - -### `update` instruction - -Similarly, the `Update` accounts struct is marked with the `#[account(mut)]` attribute. -Marking an account as `mut` persists any changes made upon exiting the program. - -Here we've covered the basics of how to interact with accounts. In a later tutorial, -we'll delve more deeply into deriving `Accounts`, but for now, just know -you must mark an account `init` when using it for the first time and `mut` -for persisting changes. - -## Creating and Initializing Accounts - -We can interact with the program as follows. - -<<< @/../examples/tutorial/basic-1/tests/basic-1.js#code-simplified - -The last element passed into the method is common amongst all dynamically generated -methods on the `rpc` namespace, containing several options for a transaction. Here, -we specify the `accounts` field, an object of all the addresses the transaction -needs to touch, and the `signers` array of all `Signer` objects needed to sign the -transaction. Because `myAccount` is being created, the Solana runtime requires it -to sign the transaction. - -::: details -If you've developed on Solana before, you might notice two things 1) the ordering of the accounts doesn't -matter and 2) the `isWritable` and `isSigner` -options are not specified on the account anywhere. In both cases, the framework takes care -of these details for you, by reading the IDL. -::: - -As before, we can run the example tests. - -``` -anchor test -``` - -## Next Steps - -We've covered all the basics of developing applications using Anchor. However, we've -left out one important aspect to ensure the security of our programs--validating input -and access control. We'll cover that next. diff --git a/docs/src/tutorials/tutorial-2.md b/docs/src/tutorials/tutorial-2.md deleted file mode 100644 index e337d2a1..00000000 --- a/docs/src/tutorials/tutorial-2.md +++ /dev/null @@ -1,77 +0,0 @@ -# Account Constraints and Access Control - -This tutorial covers how to specify constraints and access control on accounts, a problem -somewhat unique to the parallel nature of Solana. - -On Solana, a transaction must specify all accounts required for execution. And because an untrusted client specifies those accounts, a program must responsibly validate all such accounts are what the client claims they are--in addition to any instruction specific access control the program needs to do. - -For example, you could imagine easily writing a faulty token program that forgets to check if the **signer** of a transaction claiming to be the **owner** of a Token `Account` actually matches the **owner** on that account. Furthermore, imagine what might happen if the program expects a `Mint` account but a malicious user gives a token `Account`. - -To address these problems, Anchor provides several types, traits, and macros. It's easiest to understand by seeing how they're used in an example, but a couple include - -- [Accounts](https://docs.rs/anchor-lang/latest/anchor_lang/derive.Accounts.html): derive macro implementing the `Accounts` [trait](https://docs.rs/anchor-lang/latest/anchor_lang/trait.Accounts.html), allowing a struct to transform - from the untrusted `&[AccountInfo]` slice given to a Solana program into a validated struct - of deserialized account types. -- [#[account]](https://docs.rs/anchor-lang/latest/anchor_lang/attr.account.html): attribute macro implementing [AccountSerialize](https://docs.rs/anchor-lang/latest/anchor_lang/trait.AccountSerialize.html) and [AccountDeserialize](https://docs.rs/anchor-lang/latest/anchor_lang/trait.AnchorDeserialize.html), automatically prepending a unique 8 byte discriminator to the account array. The discriminator is defined by the first 8 bytes of the `Sha256` hash of the account's Rust identifier--i.e., the struct type name--and ensures no account can be substituted for another. -- [Account](https://docs.rs/anchor-lang/latest/anchor_lang/accounts/account/struct.Account.html): a wrapper type for a deserialized account implementing `AccountDeserialize`. Using this type within an `Accounts` struct will ensure the account is **owned** by the address defined by `declare_id!` where the inner account was defined. - -With the above, we can define preconditions for any instruction handler expecting a certain set of -accounts, allowing us to more easily reason about the security of our programs. - -## Clone the Repo - -To get started, clone the repo. - -```bash -git clone https://github.com/project-serum/anchor -``` - -Change directories to the [example](https://github.com/project-serum/anchor/tree/master/examples/tutorial/basic-2). - -```bash -cd anchor/examples/tutorial/basic-2 -``` - -And install any additional JavaScript dependencies: - -```bash -yarn install -``` - -## Defining a Program - -Here we have a simple **Counter** program, where anyone can create a counter, but only the assigned -**authority** can increment it. - -<<< @/../examples/tutorial/basic-2/programs/basic-2/src/lib.rs - -If you've gone through the previous tutorials the `create` instruction should be straightforward. -Let's focus on the `increment` instruction, specifically the `Increment` struct deriving -`Accounts`. - -```rust -#[derive(Accounts)] -pub struct Increment<'info> { - #[account(mut, has_one = authority)] - pub counter: Account<'info, Counter>, - pub authority: Signer<'info>, -} -``` - -Here, a couple `#[account(..)]` attributes are used. - -- `mut`: tells the program to persist all changes to the account. -- `has_one`: enforces the constraint that `Increment.counter.authority == Increment.authority.key`. - -Another new concept here is the `Signer` type. This enforces the constraint that the `authority` -account **signed** the transaction. However, anchor doesn't fetch the data on that account. - -If any of these constraints do not hold, then the `increment` instruction will never be executed. -This allows us to completely separate account validation from our program's business logic, allowing us -to reason about each concern more easily. For more, see the full [list](https://docs.rs/anchor-lang/latest/anchor_lang/derive.Accounts.html) of account constraints. - -## Next Steps - -We've covered the basics for writing a single program using Anchor on Solana. But the power of -blockchains come not from a single program, but from combining multiple _composable_ programs -(buzzword...check). Next, we'll see how to call one program from another. diff --git a/docs/src/tutorials/tutorial-3.md b/docs/src/tutorials/tutorial-3.md deleted file mode 100644 index 73cf05bd..00000000 --- a/docs/src/tutorials/tutorial-3.md +++ /dev/null @@ -1,84 +0,0 @@ -# Cross Program Invocations (CPI) - -This tutorial covers how to call one program from another, a process known as -*cross-program-invocation* (CPI). - -## Clone the Repo - -To get started, clone the repo. - -```bash -git clone https://github.com/project-serum/anchor -``` - -Change directories to the [example](https://github.com/project-serum/anchor/tree/master/examples/tutorial/basic-3). - -```bash -cd anchor/examples/tutorial/basic-3 -``` - -And install any additional JavaScript dependencies: - -```bash -yarn install -``` - -## Defining a Puppet Program - -We start with the program that will be called by another program, the puppet. - -<<< @/../examples/tutorial/basic-3/programs/puppet/src/lib.rs - -If you've followed along the other tutorials, this should be straight forward. We have -a program with two instructions, `initialize`, which does nothing other than the -initialization of the account (remember, the program *transparently* prepends a unique 8 -byte discriminator the first time an account is used), and `set_data`, which takes a previously -initialized account, and sets its data field. - -Now, suppose we wanted to call `set_data` from another program. - -## Defining a Puppet Master Program - -We define a new `puppet-master` crate, which successfully executes the Puppet program's `set_data` -instruction via CPI. - -<<< @/../examples/tutorial/basic-3/programs/puppet-master/src/lib.rs#core - -Things to notice - -* We create a `CpiContext` object with the target instruction's accounts and program, - here `SetData` and `puppet_program`. -* To invoke an instruction on another program, just use the `cpi` module on the crate, here, `puppet::cpi::set_data`. -* Our `Accounts` struct contains the puppet account we are calling into via CPI. Accounts used for CPI are not specifically denoted - as such with the `CpiAccount` label since v0.15. Accounts used for CPI are not fundamentally different from `Program` or `Signer` - accounts except for their role and ownership in the specific context in which they are used. - -::: tip -When using another Anchor program for CPI, make sure to specify the `cpi` feature in your `Cargo.toml`. -If you look at the `Cargo.toml` for this example, you'll see -`puppet = { path = "../puppet", features = ["cpi"] }`. -::: - -## Signer Seeds - -Often it's useful for a program to sign instructions. For example, if a program controls a token -account and wants to send tokens to another account, it must sign. In Solana, this is done by specifying -"signer seeds" on CPI. To do this using the example above, simply change -`CpiContext::new(cpi_accounts, cpi_program)` to -`CpiContext::new_with_signer(cpi_accounts, cpi_program, signer_seeds)`. - -For more background on signing with program derived addresses, see the official Solana [documentation](https://docs.solana.com/developing/programming-model/calling-between-programs#program-signed-accounts). - -## Return values - -Solana currently has no way to return values from CPI, alas. However, you can approximate this -by having the callee write return values to an account and the caller read that account to -retrieve the return value. In future work, Anchor should do this transparently. - -## Conclusion - -Now that you can have your programs call other programs, you should be able to access all the work being done by other developers in your own applications! - -## Next Steps - -We just covered Cross Program Invocation and showed how anchor can handle talking to multiple different programs in the solana ecosystem. In the next step, we will teach you how to handle errors and in Anchor. \ No newline at end of file diff --git a/docs/src/tutorials/tutorial-4.md b/docs/src/tutorials/tutorial-4.md deleted file mode 100644 index 2e68b6bf..00000000 --- a/docs/src/tutorials/tutorial-4.md +++ /dev/null @@ -1,56 +0,0 @@ -# Errors - -If you've ever programmed on a blockchain, you've probably been frustrated by -either non existent or opaque error codes. Anchor attempts to address this by -providing the `#[error_code]` attribute, which can be used to create typed Errors with -descriptive messages that automatically propagate to the client. - -## Defining a Program - -For example, - -```rust -use anchor_lang::prelude::*; - -#[program] -mod errors { - use super::*; - pub fn hello(_ctx: Context) -> Result<()> { - Err(error!(ErrorCode::Hello)) - } -} - -#[derive(Accounts)] -pub struct Hello {} - -#[error_code] -pub enum ErrorCode { - #[msg("This is an error message clients will automatically display")] - Hello, -} -``` - -Observe the [#[error_code]](https://docs.rs/anchor-lang/latest/anchor_lang/attr.error_code.html) attribute on the `ErrorCode` enum. -This macro generates internal anchor code that helps anchor turn the error code into an error and display it properly. - -To create an error, use the [`error!`](https://docs.rs/anchor-lang/latest/anchor_lang/prelude/macro.error.html) macro together with an error code. This macro creates an [`AnchorError`](https://docs.rs/anchor-lang/latest/anchor_lang/error/struct.AnchorError.html) that includes helpful information like the file and line the error was created in. - -To make writing errors even easier, anchor also provides the [`err!`](https://docs.rs/anchor-lang/latest/anchor_lang/prelude/macro.err.html) and the [`require!`](https://docs.rs/anchor-lang/latest/anchor_lang/prelude/macro.require.html) macros. - -## Using the Client - -When using the client, we get the error message. - -```javascript -try { - const tx = await program.rpc.hello(); - assert.ok(false); -} catch (err) { - const errMsg = "This is an error message clients will automatically display"; - assert.equal(err.toString(), errMsg); -} -``` - -It's that easy. :) - -To run the full example, go [here](https://github.com/project-serum/anchor/tree/master/examples/tutorial/basic-4). diff --git a/examples/tutorial/basic-0/Anchor.toml b/examples/tutorial/basic-0/Anchor.toml deleted file mode 100644 index 7e339cf9..00000000 --- a/examples/tutorial/basic-0/Anchor.toml +++ /dev/null @@ -1,9 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -basic_0 = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[scripts] -test = "yarn run mocha -t 1000000 tests/" diff --git a/examples/tutorial/basic-0/Cargo.toml b/examples/tutorial/basic-0/Cargo.toml deleted file mode 100644 index 3fc18468..00000000 --- a/examples/tutorial/basic-0/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/examples/tutorial/basic-0/client.js b/examples/tutorial/basic-0/client.js deleted file mode 100644 index 77a7f08b..00000000 --- a/examples/tutorial/basic-0/client.js +++ /dev/null @@ -1,29 +0,0 @@ -// client.js is used to introduce the reader to generating clients from IDLs. -// It is not expected users directly test with this example. For a more -// ergonomic example, see `tests/basic-0.js` in this workspace. - -const anchor = require("@project-serum/anchor"); - -// Configure the local cluster. -anchor.setProvider(anchor.AnchorProvider.local()); - -async function main() { - // #region main - // Read the generated IDL. - const idl = JSON.parse( - require("fs").readFileSync("./target/idl/basic_0.json", "utf8") - ); - - // Address of the deployed program. - const programId = new anchor.web3.PublicKey(""); - - // Generate the program client from IDL. - const program = new anchor.Program(idl, programId); - - // Execute the RPC. - await program.rpc.initialize(); - // #endregion main -} - -console.log("Running client."); -main().then(() => console.log("Success")); diff --git a/examples/tutorial/basic-0/package.json b/examples/tutorial/basic-0/package.json deleted file mode 100644 index f44bf274..00000000 --- a/examples/tutorial/basic-0/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "basic-0", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test --skip-lint" - } -} diff --git a/examples/tutorial/basic-0/programs/basic-0/Cargo.toml b/examples/tutorial/basic-0/programs/basic-0/Cargo.toml deleted file mode 100644 index ba1e97ab..00000000 --- a/examples/tutorial/basic-0/programs/basic-0/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "basic-0" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "basic_0" - -[features] -no-entrypoint = [] -cpi = ["no-entrypoint"] - -[dependencies] -anchor-lang = { path = "../../../../../lang" } diff --git a/examples/tutorial/basic-0/programs/basic-0/Xargo.toml b/examples/tutorial/basic-0/programs/basic-0/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/examples/tutorial/basic-0/programs/basic-0/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/examples/tutorial/basic-0/programs/basic-0/src/lib.rs b/examples/tutorial/basic-0/programs/basic-0/src/lib.rs deleted file mode 100644 index 799fa9e1..00000000 --- a/examples/tutorial/basic-0/programs/basic-0/src/lib.rs +++ /dev/null @@ -1,14 +0,0 @@ -use anchor_lang::prelude::*; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -mod basic_0 { - use super::*; - pub fn initialize(_ctx: Context) -> Result<()> { - Ok(()) - } -} - -#[derive(Accounts)] -pub struct Initialize {} diff --git a/examples/tutorial/basic-0/tests/basic-0.js b/examples/tutorial/basic-0/tests/basic-0.js deleted file mode 100644 index e9be9609..00000000 --- a/examples/tutorial/basic-0/tests/basic-0.js +++ /dev/null @@ -1,16 +0,0 @@ -const anchor = require("@project-serum/anchor"); - -describe("basic-0", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.local()); - - it("Uses the workspace to invoke the initialize instruction", async () => { - // #region code - // Read the deployed program from the workspace. - const program = anchor.workspace.Basic0; - - // Execute the RPC. - await program.rpc.initialize(); - // #endregion code - }); -}); diff --git a/examples/tutorial/basic-1/Anchor.toml b/examples/tutorial/basic-1/Anchor.toml deleted file mode 100644 index c2a1399e..00000000 --- a/examples/tutorial/basic-1/Anchor.toml +++ /dev/null @@ -1,9 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -basic_1 = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[scripts] -test = "yarn run mocha -t 1000000 tests/" diff --git a/examples/tutorial/basic-1/Cargo.toml b/examples/tutorial/basic-1/Cargo.toml deleted file mode 100644 index 3fc18468..00000000 --- a/examples/tutorial/basic-1/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/examples/tutorial/basic-1/package.json b/examples/tutorial/basic-1/package.json deleted file mode 100644 index 13bcd228..00000000 --- a/examples/tutorial/basic-1/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "basic-1", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test --skip-lint" - } -} diff --git a/examples/tutorial/basic-1/programs/basic-1/Cargo.toml b/examples/tutorial/basic-1/programs/basic-1/Cargo.toml deleted file mode 100644 index ab9b05c8..00000000 --- a/examples/tutorial/basic-1/programs/basic-1/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "basic-1" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "basic_1" - -[features] -no-entrypoint = [] -cpi = ["no-entrypoint"] - -[dependencies] -anchor-lang = { path = "../../../../../lang" } diff --git a/examples/tutorial/basic-1/programs/basic-1/Xargo.toml b/examples/tutorial/basic-1/programs/basic-1/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/examples/tutorial/basic-1/programs/basic-1/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/examples/tutorial/basic-1/programs/basic-1/src/lib.rs b/examples/tutorial/basic-1/programs/basic-1/src/lib.rs deleted file mode 100644 index b2d346ea..00000000 --- a/examples/tutorial/basic-1/programs/basic-1/src/lib.rs +++ /dev/null @@ -1,40 +0,0 @@ -use anchor_lang::prelude::*; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -mod basic_1 { - use super::*; - - pub fn initialize(ctx: Context, data: u64) -> Result<()> { - let my_account = &mut ctx.accounts.my_account; - my_account.data = data; - Ok(()) - } - - pub fn update(ctx: Context, data: u64) -> Result<()> { - let my_account = &mut ctx.accounts.my_account; - my_account.data = data; - Ok(()) - } -} - -#[derive(Accounts)] -pub struct Initialize<'info> { - #[account(init, payer = user, space = 8 + 8)] - pub my_account: Account<'info, MyAccount>, - #[account(mut)] - pub user: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct Update<'info> { - #[account(mut)] - pub my_account: Account<'info, MyAccount>, -} - -#[account] -pub struct MyAccount { - pub data: u64, -} diff --git a/examples/tutorial/basic-1/tests/basic-1.js b/examples/tutorial/basic-1/tests/basic-1.js deleted file mode 100644 index 98e7ef5a..00000000 --- a/examples/tutorial/basic-1/tests/basic-1.js +++ /dev/null @@ -1,65 +0,0 @@ -const assert = require("assert"); -const anchor = require("@project-serum/anchor"); -const { SystemProgram } = anchor.web3; - -describe("basic-1", () => { - // Use a local provider. - const provider = anchor.AnchorProvider.local(); - - // Configure the client to use the local cluster. - anchor.setProvider(provider); - - it("Creates and initializes an account in a single atomic transaction (simplified)", async () => { - // #region code-simplified - // The program to execute. - const program = anchor.workspace.Basic1; - - // The Account to create. - const myAccount = anchor.web3.Keypair.generate(); - - // Create the new account and initialize it with the program. - // #region code-simplified - await program.rpc.initialize(new anchor.BN(1234), { - accounts: { - myAccount: myAccount.publicKey, - user: provider.wallet.publicKey, - systemProgram: SystemProgram.programId, - }, - signers: [myAccount], - }); - // #endregion code-simplified - - // Fetch the newly created account from the cluster. - const account = await program.account.myAccount.fetch(myAccount.publicKey); - - // Check it's state was initialized. - assert.ok(account.data.eq(new anchor.BN(1234))); - - // Store the account for the next test. - _myAccount = myAccount; - }); - - it("Updates a previously created account", async () => { - const myAccount = _myAccount; - - // #region update-test - - // The program to execute. - const program = anchor.workspace.Basic1; - - // Invoke the update rpc. - await program.rpc.update(new anchor.BN(4321), { - accounts: { - myAccount: myAccount.publicKey, - }, - }); - - // Fetch the newly updated account. - const account = await program.account.myAccount.fetch(myAccount.publicKey); - - // Check it's state was mutated. - assert.ok(account.data.eq(new anchor.BN(4321))); - - // #endregion update-test - }); -}); diff --git a/examples/tutorial/basic-2/Anchor.toml b/examples/tutorial/basic-2/Anchor.toml deleted file mode 100644 index 2530e958..00000000 --- a/examples/tutorial/basic-2/Anchor.toml +++ /dev/null @@ -1,9 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -basic_2 = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[scripts] -test = "yarn run mocha -t 1000000 tests/" diff --git a/examples/tutorial/basic-2/Cargo.toml b/examples/tutorial/basic-2/Cargo.toml deleted file mode 100644 index 3fc18468..00000000 --- a/examples/tutorial/basic-2/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/examples/tutorial/basic-2/package.json b/examples/tutorial/basic-2/package.json deleted file mode 100644 index 8e4501af..00000000 --- a/examples/tutorial/basic-2/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "basic-2", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test --skip-lint" - } -} diff --git a/examples/tutorial/basic-2/programs/basic-2/Cargo.toml b/examples/tutorial/basic-2/programs/basic-2/Cargo.toml deleted file mode 100644 index 59426513..00000000 --- a/examples/tutorial/basic-2/programs/basic-2/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "basic-2" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "basic_2" - -[features] -no-entrypoint = [] -cpi = ["no-entrypoint"] - -[dependencies] -anchor-lang = { path = "../../../../../lang" } diff --git a/examples/tutorial/basic-2/programs/basic-2/Xargo.toml b/examples/tutorial/basic-2/programs/basic-2/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/examples/tutorial/basic-2/programs/basic-2/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/examples/tutorial/basic-2/programs/basic-2/src/lib.rs b/examples/tutorial/basic-2/programs/basic-2/src/lib.rs deleted file mode 100644 index 6b9c66b3..00000000 --- a/examples/tutorial/basic-2/programs/basic-2/src/lib.rs +++ /dev/null @@ -1,43 +0,0 @@ -use anchor_lang::prelude::*; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -mod basic_2 { - use super::*; - - pub fn create(ctx: Context, authority: Pubkey) -> Result<()> { - let counter = &mut ctx.accounts.counter; - counter.authority = authority; - counter.count = 0; - Ok(()) - } - - pub fn increment(ctx: Context) -> Result<()> { - let counter = &mut ctx.accounts.counter; - counter.count += 1; - Ok(()) - } -} - -#[derive(Accounts)] -pub struct Create<'info> { - #[account(init, payer = user, space = 8 + 40)] - pub counter: Account<'info, Counter>, - #[account(mut)] - pub user: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct Increment<'info> { - #[account(mut, has_one = authority)] - pub counter: Account<'info, Counter>, - pub authority: Signer<'info>, -} - -#[account] -pub struct Counter { - pub authority: Pubkey, - pub count: u64, -} diff --git a/examples/tutorial/basic-2/tests/basic-2.js b/examples/tutorial/basic-2/tests/basic-2.js deleted file mode 100644 index 8f638ee8..00000000 --- a/examples/tutorial/basic-2/tests/basic-2.js +++ /dev/null @@ -1,48 +0,0 @@ -const assert = require("assert"); -const anchor = require("@project-serum/anchor"); -const { SystemProgram } = anchor.web3; - -describe("basic-2", () => { - const provider = anchor.AnchorProvider.local(); - - // Configure the client to use the local cluster. - anchor.setProvider(provider); - - // Counter for the tests. - const counter = anchor.web3.Keypair.generate(); - - // Program for the tests. - const program = anchor.workspace.Basic2; - - it("Creates a counter", async () => { - await program.rpc.create(provider.wallet.publicKey, { - accounts: { - counter: counter.publicKey, - user: provider.wallet.publicKey, - systemProgram: SystemProgram.programId, - }, - signers: [counter], - }); - - let counterAccount = await program.account.counter.fetch(counter.publicKey); - - assert.ok(counterAccount.authority.equals(provider.wallet.publicKey)); - assert.ok(counterAccount.count.toNumber() === 0); - }); - - it("Updates a counter", async () => { - await program.rpc.increment({ - accounts: { - counter: counter.publicKey, - authority: provider.wallet.publicKey, - }, - }); - - const counterAccount = await program.account.counter.fetch( - counter.publicKey - ); - - assert.ok(counterAccount.authority.equals(provider.wallet.publicKey)); - assert.ok(counterAccount.count.toNumber() == 1); - }); -}); diff --git a/examples/tutorial/basic-3/Anchor.toml b/examples/tutorial/basic-3/Anchor.toml deleted file mode 100644 index d3377156..00000000 --- a/examples/tutorial/basic-3/Anchor.toml +++ /dev/null @@ -1,10 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -puppet = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" -puppet_master = "HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L" - -[scripts] -test = "yarn run mocha -t 1000000 tests/" diff --git a/examples/tutorial/basic-3/Cargo.toml b/examples/tutorial/basic-3/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/examples/tutorial/basic-3/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/examples/tutorial/basic-3/package.json b/examples/tutorial/basic-3/package.json deleted file mode 100644 index 4e4a9fc1..00000000 --- a/examples/tutorial/basic-3/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "basic-3", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test --skip-lint" - } -} diff --git a/examples/tutorial/basic-3/programs/puppet-master/Cargo.toml b/examples/tutorial/basic-3/programs/puppet-master/Cargo.toml deleted file mode 100644 index 833bb70f..00000000 --- a/examples/tutorial/basic-3/programs/puppet-master/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "puppet-master" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "puppet_master" - -[features] -no-entrypoint = [] -cpi = ["no-entrypoint"] - -[dependencies] -anchor-lang = { path = "../../../../../lang" } -puppet = { path = "../puppet", features = ["cpi"] } diff --git a/examples/tutorial/basic-3/programs/puppet-master/Xargo.toml b/examples/tutorial/basic-3/programs/puppet-master/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/examples/tutorial/basic-3/programs/puppet-master/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/examples/tutorial/basic-3/programs/puppet-master/src/lib.rs b/examples/tutorial/basic-3/programs/puppet-master/src/lib.rs deleted file mode 100644 index 29d32bac..00000000 --- a/examples/tutorial/basic-3/programs/puppet-master/src/lib.rs +++ /dev/null @@ -1,28 +0,0 @@ -// #region core -use anchor_lang::prelude::*; -use puppet::cpi::accounts::SetData; -use puppet::program::Puppet; -use puppet::{self, Data}; - -declare_id!("HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L"); - -#[program] -mod puppet_master { - use super::*; - pub fn pull_strings(ctx: Context, data: u64) -> anchor_lang::Result<()> { - let cpi_program = ctx.accounts.puppet_program.to_account_info(); - let cpi_accounts = SetData { - puppet: ctx.accounts.puppet.to_account_info(), - }; - let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); - puppet::cpi::set_data(cpi_ctx, data) - } -} - -#[derive(Accounts)] -pub struct PullStrings<'info> { - #[account(mut)] - pub puppet: Account<'info, Data>, - pub puppet_program: Program<'info, Puppet>, -} -// #endregion core diff --git a/examples/tutorial/basic-3/programs/puppet/Cargo.toml b/examples/tutorial/basic-3/programs/puppet/Cargo.toml deleted file mode 100644 index 998dbbd2..00000000 --- a/examples/tutorial/basic-3/programs/puppet/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "puppet" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "puppet" - -[features] -no-entrypoint = [] -cpi = ["no-entrypoint"] - -[dependencies] -anchor-lang = { path = "../../../../../lang" } diff --git a/examples/tutorial/basic-3/programs/puppet/Xargo.toml b/examples/tutorial/basic-3/programs/puppet/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/examples/tutorial/basic-3/programs/puppet/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/examples/tutorial/basic-3/programs/puppet/src/lib.rs b/examples/tutorial/basic-3/programs/puppet/src/lib.rs deleted file mode 100644 index 11646bf3..00000000 --- a/examples/tutorial/basic-3/programs/puppet/src/lib.rs +++ /dev/null @@ -1,37 +0,0 @@ -use anchor_lang::prelude::*; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -pub mod puppet { - use super::*; - pub fn initialize(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn set_data(ctx: Context, data: u64) -> Result<()> { - let puppet = &mut ctx.accounts.puppet; - puppet.data = data; - Ok(()) - } -} - -#[derive(Accounts)] -pub struct Initialize<'info> { - #[account(init, payer = user, space = 8 + 8)] - pub puppet: Account<'info, Data>, - #[account(mut)] - pub user: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct SetData<'info> { - #[account(mut)] - pub puppet: Account<'info, Data>, -} - -#[account] -pub struct Data { - pub data: u64, -} diff --git a/examples/tutorial/basic-3/tests/basic-3.js b/examples/tutorial/basic-3/tests/basic-3.js deleted file mode 100644 index ad5a75d3..00000000 --- a/examples/tutorial/basic-3/tests/basic-3.js +++ /dev/null @@ -1,38 +0,0 @@ -const assert = require("assert"); -const anchor = require("@project-serum/anchor"); -const { SystemProgram } = anchor.web3; - -describe("basic-3", () => { - const provider = anchor.AnchorProvider.local(); - - // Configure the client to use the local cluster. - anchor.setProvider(provider); - - it("Performs CPI from puppet master to puppet", async () => { - const puppetMaster = anchor.workspace.PuppetMaster; - const puppet = anchor.workspace.Puppet; - - // Initialize a new puppet account. - const newPuppetAccount = anchor.web3.Keypair.generate(); - const tx = await puppet.rpc.initialize({ - accounts: { - puppet: newPuppetAccount.publicKey, - user: provider.wallet.publicKey, - systemProgram: SystemProgram.programId, - }, - signers: [newPuppetAccount], - }); - - // Invoke the puppet master to perform a CPI to the puppet. - await puppetMaster.rpc.pullStrings(new anchor.BN(111), { - accounts: { - puppet: newPuppetAccount.publicKey, - puppetProgram: puppet.programId, - }, - }); - - // Check the state updated. - puppetAccount = await puppet.account.data.fetch(newPuppetAccount.publicKey); - assert.ok(puppetAccount.data.eq(new anchor.BN(111))); - }); -}); diff --git a/examples/tutorial/basic-4/Anchor.toml b/examples/tutorial/basic-4/Anchor.toml deleted file mode 100644 index f60d23bb..00000000 --- a/examples/tutorial/basic-4/Anchor.toml +++ /dev/null @@ -1,9 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -basic_4 = "CwrqeMj2U8tFr1Rhkgwc84tpAsqbt9pTt2a4taoTADPr" - -[scripts] -test = "yarn run mocha -t 1000000 tests/" diff --git a/examples/tutorial/basic-4/Cargo.toml b/examples/tutorial/basic-4/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/examples/tutorial/basic-4/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/examples/tutorial/basic-4/package.json b/examples/tutorial/basic-4/package.json deleted file mode 100644 index 20b9006e..00000000 --- a/examples/tutorial/basic-4/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "basic-4", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test --skip-lint" - } -} diff --git a/examples/tutorial/basic-4/programs/basic-4/Cargo.toml b/examples/tutorial/basic-4/programs/basic-4/Cargo.toml deleted file mode 100644 index 90a71f7f..00000000 --- a/examples/tutorial/basic-4/programs/basic-4/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "basic-4" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "basic_4" - -[features] -no-entrypoint = [] -cpi = ["no-entrypoint"] - -[dependencies] -anchor-lang = { path = "../../../../../lang" } diff --git a/examples/tutorial/basic-4/programs/basic-4/Xargo.toml b/examples/tutorial/basic-4/programs/basic-4/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/examples/tutorial/basic-4/programs/basic-4/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/examples/tutorial/basic-4/programs/basic-4/src/lib.rs b/examples/tutorial/basic-4/programs/basic-4/src/lib.rs deleted file mode 100644 index 79d5d97e..00000000 --- a/examples/tutorial/basic-4/programs/basic-4/src/lib.rs +++ /dev/null @@ -1,44 +0,0 @@ -// #region code -use anchor_lang::prelude::*; - -declare_id!("CwrqeMj2U8tFr1Rhkgwc84tpAsqbt9pTt2a4taoTADPr"); - -#[program] -pub mod basic_4 { - use super::*; - - #[state] - pub struct Counter { - pub authority: Pubkey, - pub count: u64, - } - - impl Counter { - pub fn new(ctx: Context) -> anchor_lang::Result { - Ok(Self { - authority: *ctx.accounts.authority.key, - count: 0, - }) - } - - pub fn increment(&mut self, ctx: Context) -> anchor_lang::Result<()> { - if &self.authority != ctx.accounts.authority.key { - return Err(error!(ErrorCode::Unauthorized)); - } - self.count += 1; - Ok(()) - } - } -} - -#[derive(Accounts)] -pub struct Auth<'info> { - authority: Signer<'info>, -} -// #endregion code - -#[error_code] -pub enum ErrorCode { - #[msg("You are not authorized to perform this action.")] - Unauthorized, -} diff --git a/examples/tutorial/basic-4/tests/basic-4.js b/examples/tutorial/basic-4/tests/basic-4.js deleted file mode 100644 index fddc7514..00000000 --- a/examples/tutorial/basic-4/tests/basic-4.js +++ /dev/null @@ -1,41 +0,0 @@ -const assert = require("assert"); -const anchor = require("@project-serum/anchor"); - -describe("basic-4", () => { - const provider = anchor.AnchorProvider.local(); - - // Configure the client to use the local cluster. - anchor.setProvider(provider); - - const program = anchor.workspace.Basic4; - - it("Is runs the constructor", async () => { - // #region ctor - // Initialize the program's state struct. - await program.state.rpc.new({ - accounts: { - authority: provider.wallet.publicKey, - }, - }); - // #endregion ctor - - // Fetch the state struct from the network. - // #region accessor - const state = await program.state.fetch(); - // #endregion accessor - - assert.ok(state.count.eq(new anchor.BN(0))); - }); - - it("Executes a method on the program", async () => { - // #region instruction - await program.state.rpc.increment({ - accounts: { - authority: provider.wallet.publicKey, - }, - }); - // #endregion instruction - const state = await program.state.fetch(); - assert.ok(state.count.eq(new anchor.BN(1))); - }); -}); diff --git a/examples/tutorial/package.json b/examples/tutorial/package.json deleted file mode 100644 index 6760dfaf..00000000 --- a/examples/tutorial/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "anchor-examples", - "private": true, - "scripts": { - "lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w", - "lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check" - }, - "workspaces": [ - "basic-0", - "basic-1", - "basic-2", - "basic-3", - "basic-4" - ], - "dependencies": { - "@project-serum/anchor": "file:../../ts" - }, - "devDependencies": { - "mocha": "^9.1.3", - "prettier": "^2.5.1" - } -} diff --git a/examples/tutorial/yarn.lock b/examples/tutorial/yarn.lock deleted file mode 100644 index 478e9d95..00000000 --- a/examples/tutorial/yarn.lock +++ /dev/null @@ -1,1071 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5": - version "7.16.3" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5" - integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ== - dependencies: - regenerator-runtime "^0.13.4" - -"@ethersproject/bytes@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.5.0.tgz#cb11c526de657e7b45d2e0f0246fb3b9d29a601c" - integrity sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog== - dependencies: - "@ethersproject/logger" "^5.5.0" - -"@ethersproject/logger@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.5.0.tgz#0c2caebeff98e10aefa5aef27d7441c7fd18cf5d" - integrity sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg== - -"@ethersproject/sha2@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.5.0.tgz#a40a054c61f98fd9eee99af2c3cc6ff57ec24db7" - integrity sha512-B5UBoglbCiHamRVPLA110J+2uqsifpZaTmid2/7W5rbtYVz6gus6/hSDieIU/6gaKIDcOj12WnOdiymEUHIAOA== - dependencies: - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - hash.js "1.1.7" - -"@project-serum/anchor@file:../../ts": - version "0.24.0" - dependencies: - "@project-serum/borsh" "^0.2.5" - "@solana/web3.js" "^1.36.0" - base64-js "^1.5.1" - bn.js "^5.1.2" - bs58 "^4.0.1" - buffer-layout "^1.2.2" - camelcase "^5.3.1" - cross-fetch "^3.1.5" - crypto-hash "^1.3.0" - eventemitter3 "^4.0.7" - js-sha256 "^0.9.0" - pako "^2.0.3" - snake-case "^3.0.4" - toml "^3.0.0" - -"@project-serum/borsh@^0.2.5": - version "0.2.5" - resolved "https://registry.yarnpkg.com/@project-serum/borsh/-/borsh-0.2.5.tgz#6059287aa624ecebbfc0edd35e4c28ff987d8663" - integrity sha512-UmeUkUoKdQ7rhx6Leve1SssMR/Ghv8qrEiyywyxSWg7ooV7StdpPBhciiy5eB3T0qU1BXvdRNC8TdrkxK7WC5Q== - dependencies: - bn.js "^5.1.2" - buffer-layout "^1.2.0" - -"@solana/buffer-layout@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-3.0.0.tgz#b9353caeb9a1589cb77a1b145bcb1a9a93114326" - integrity sha512-MVdgAKKL39tEs0l8je0hKaXLQFb7Rdfb0Xg2LjFZd8Lfdazkg6xiS98uAZrEKvaoF3i4M95ei9RydkGIDMeo3w== - dependencies: - buffer "~6.0.3" - -"@solana/web3.js@^1.36.0": - version "1.36.0" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.36.0.tgz#79d7d5217b49b80139f4de68953adc5b9a9a264f" - integrity sha512-RNT1451iRR7TyW7EJKMCrH/0OXawIe4zVm0DWQASwXlR/u1jmW6FrmH0lujIh7cGTlfOVbH+2ZU9AVUPLBFzwA== - dependencies: - "@babel/runtime" "^7.12.5" - "@ethersproject/sha2" "^5.5.0" - "@solana/buffer-layout" "^3.0.0" - bn.js "^5.0.0" - borsh "^0.4.0" - bs58 "^4.0.1" - buffer "6.0.1" - cross-fetch "^3.1.4" - jayson "^3.4.4" - js-sha3 "^0.8.0" - rpc-websockets "^7.4.2" - secp256k1 "^4.0.2" - superstruct "^0.14.2" - tweetnacl "^1.0.0" - -"@types/bn.js@^4.11.5": - version "4.11.6" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" - integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== - dependencies: - "@types/node" "*" - -"@types/connect@^3.4.33": - version "3.4.35" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" - integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== - dependencies: - "@types/node" "*" - -"@types/express-serve-static-core@^4.17.9": - version "4.17.25" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.25.tgz#e42f7046adc65ece2eb6059b77aecfbe9e9f82e0" - integrity sha512-OUJIVfRMFijZukGGwTpKNFprqCCXk5WjNGvUgB/CxxBR40QWSjsNK86+yvGKlCOGc7sbwfHLaXhkG+NsytwBaQ== - dependencies: - "@types/node" "*" - "@types/qs" "*" - "@types/range-parser" "*" - -"@types/lodash@^4.14.159": - version "4.14.176" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.176.tgz#641150fc1cda36fbfa329de603bbb175d7ee20c0" - integrity sha512-xZmuPTa3rlZoIbtDUyJKZQimJV3bxCmzMIO2c9Pz9afyDro6kr7R79GwcB6mRhuoPmV2p1Vb66WOJH7F886WKQ== - -"@types/node@*": - version "16.11.7" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.7.tgz#36820945061326978c42a01e56b61cd223dfdc42" - integrity sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw== - -"@types/node@^12.12.54": - version "12.20.37" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.37.tgz#abb38afa9d6e8a2f627a8cb52290b3c80fbe61ed" - integrity sha512-i1KGxqcvJaLQali+WuypQnXwcplhtNtjs66eNsZpp2P2FL/trJJxx/VWsM0YCL2iMoIJrbXje48lvIQAQ4p2ZA== - -"@types/qs@*": - version "6.9.7" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" - integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== - -"@types/range-parser@*": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" - integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== - -"@types/ws@^7.4.4": - version "7.4.7" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" - integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww== - dependencies: - "@types/node" "*" - -"@ungap/promise-all-settled@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" - integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== - -JSONStream@^1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" - integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== - dependencies: - jsonparse "^1.2.0" - through ">=2.2.7 <3" - -ansi-colors@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -anymatch@~3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -base-x@^3.0.2: - version "3.0.9" - resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" - integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== - dependencies: - safe-buffer "^5.0.1" - -base64-js@^1.3.1, base64-js@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== - -bn.js@^4.11.9: - version "4.12.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" - integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== - -bn.js@^5.0.0, bn.js@^5.1.2: - version "5.2.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" - integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== - -borsh@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.4.0.tgz#9dd6defe741627f1315eac2a73df61421f6ddb9f" - integrity sha512-aX6qtLya3K0AkT66CmYWCCDr77qsE9arV05OmdFpmat9qu8Pg9J5tBUPDztAW5fNh/d/MyVG/OYziP52Ndzx1g== - dependencies: - "@types/bn.js" "^4.11.5" - bn.js "^5.0.0" - bs58 "^4.0.0" - text-encoding-utf-8 "^1.0.2" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -brorand@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" - integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= - -browser-stdout@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" - integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== - -bs58@^4.0.0, bs58@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" - integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo= - dependencies: - base-x "^3.0.2" - -buffer-layout@^1.2.0, buffer-layout@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/buffer-layout/-/buffer-layout-1.2.2.tgz#b9814e7c7235783085f9ca4966a0cfff112259d5" - integrity sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA== - -buffer@6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.1.tgz#3cbea8c1463e5a0779e30b66d4c88c6ffa182ac2" - integrity sha512-rVAXBwEcEoYtxnHSO5iWyhzV/O1WMtkUYWlfdLS7FjU4PnSJJHEfHXi/uHPI5EwltmOA794gN3bm3/pzuctWjQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - -buffer@~6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - -bufferutil@^4.0.1: - version "4.0.5" - resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.5.tgz#da9ea8166911cc276bf677b8aed2d02d31f59028" - integrity sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A== - dependencies: - node-gyp-build "^4.3.0" - -camelcase@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -camelcase@^6.0.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" - integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== - -chalk@^4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chokidar@3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" - integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - -circular-json@^0.5.9: - version "0.5.9" - resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.9.tgz#932763ae88f4f7dead7a0d09c8a51a4743a53b1d" - integrity sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ== - -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -commander@^2.20.3: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -cross-fetch@^3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39" - integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ== - dependencies: - node-fetch "2.6.1" - -cross-fetch@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" - integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== - dependencies: - node-fetch "2.6.7" - -crypto-hash@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-1.3.0.tgz#b402cb08f4529e9f4f09346c3e275942f845e247" - integrity sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg== - -debug@4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" - integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== - dependencies: - ms "2.1.2" - -decamelize@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" - integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== - -delay@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" - integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== - -diff@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" - integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== - -dot-case@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" - integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== - dependencies: - no-case "^3.0.4" - tslib "^2.0.3" - -elliptic@^6.5.2: - version "6.5.4" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== - dependencies: - bn.js "^4.11.9" - brorand "^1.1.0" - hash.js "^1.0.0" - hmac-drbg "^1.0.1" - inherits "^2.0.4" - minimalistic-assert "^1.0.1" - minimalistic-crypto-utils "^1.0.1" - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -es6-promise@^4.0.3: - version "4.2.8" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" - integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== - -es6-promisify@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" - integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= - dependencies: - es6-promise "^4.0.3" - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-string-regexp@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -eventemitter3@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" - integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== - -eyes@^0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" - integrity sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A= - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -find-up@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -flat@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" - integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob@7.1.7: - version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" - integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== - dependencies: - 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" - -growl@1.10.5: - version "1.10.5" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" - integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - -he@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - -hmac-drbg@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" - integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - -ieee754@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@^2.0.3, inherits@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-plain-obj@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" - integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== - -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - -isomorphic-ws@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" - integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== - -jayson@^3.4.4: - version "3.6.5" - resolved "https://registry.yarnpkg.com/jayson/-/jayson-3.6.5.tgz#e560bcad4daf098c7391f46ba8efc9d6f34a4102" - integrity sha512-wmOjX+eQcnCDyPF4KORomaIj9wj3h0B5VEbeD0+2VHfTfErB+h1zpR7oBkgCZp36AFjp3+a4CLz6U72BYpFHAw== - dependencies: - "@types/connect" "^3.4.33" - "@types/express-serve-static-core" "^4.17.9" - "@types/lodash" "^4.14.159" - "@types/node" "^12.12.54" - "@types/ws" "^7.4.4" - JSONStream "^1.3.5" - commander "^2.20.3" - delay "^5.0.0" - es6-promisify "^5.0.0" - eyes "^0.1.8" - isomorphic-ws "^4.0.1" - json-stringify-safe "^5.0.1" - lodash "^4.17.20" - uuid "^3.4.0" - ws "^7.4.5" - -js-sha256@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" - integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== - -js-sha3@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" - integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== - -js-yaml@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - -json-stringify-safe@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -jsonparse@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" - integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - -lodash@^4.17.20: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -log-symbols@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - -lower-case@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" - integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== - dependencies: - tslib "^2.0.3" - -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" - integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= - -minimatch@3.0.4, minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -mocha@^9.1.3: - version "9.1.3" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.1.3.tgz#8a623be6b323810493d8c8f6f7667440fa469fdb" - integrity sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw== - dependencies: - "@ungap/promise-all-settled" "1.1.2" - ansi-colors "4.1.1" - browser-stdout "1.3.1" - chokidar "3.5.2" - debug "4.3.2" - diff "5.0.0" - escape-string-regexp "4.0.0" - find-up "5.0.0" - glob "7.1.7" - growl "1.10.5" - he "1.2.0" - js-yaml "4.1.0" - log-symbols "4.1.0" - minimatch "3.0.4" - ms "2.1.3" - nanoid "3.1.25" - serialize-javascript "6.0.0" - strip-json-comments "3.1.1" - supports-color "8.1.1" - which "2.0.2" - workerpool "6.1.5" - yargs "16.2.0" - yargs-parser "20.2.4" - yargs-unparser "2.0.0" - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -nanoid@3.1.25: - version "3.1.25" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152" - integrity sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q== - -no-case@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" - integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== - dependencies: - lower-case "^2.0.2" - tslib "^2.0.3" - -node-addon-api@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" - integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== - -node-fetch@2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== - -node-fetch@2.6.7: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - -node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" - integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - -pako@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pako/-/pako-2.0.4.tgz#6cebc4bbb0b6c73b0d5b8d7e8476e2b2fbea576d" - integrity sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg== - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -picomatch@^2.0.4, picomatch@^2.2.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" - integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== - -prettier@^2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" - integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== - -randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - -regenerator-runtime@^0.13.4: - version "0.13.9" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" - integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= - -rpc-websockets@^7.4.2: - version "7.4.16" - resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.4.16.tgz#eb701cdef577d4357ba5f526d50e25f370396fac" - integrity sha512-0b7OVhutzwRIaYAtJo5tqtaQTWKfwAsKnaThOSOy+VkhVdleNUgb8eZnWSdWITRZZEigV5uPEIDr5KZe4DBrdQ== - dependencies: - "@babel/runtime" "^7.11.2" - circular-json "^0.5.9" - eventemitter3 "^4.0.7" - uuid "^8.3.0" - ws "^7.4.5" - optionalDependencies: - bufferutil "^4.0.1" - utf-8-validate "^5.0.2" - -safe-buffer@^5.0.1, safe-buffer@^5.1.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -secp256k1@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.2.tgz#15dd57d0f0b9fdb54ac1fa1694f40e5e9a54f4a1" - integrity sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg== - dependencies: - elliptic "^6.5.2" - node-addon-api "^2.0.0" - node-gyp-build "^4.2.0" - -serialize-javascript@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" - integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== - dependencies: - randombytes "^2.1.0" - -snake-case@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" - integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg== - dependencies: - dot-case "^3.0.4" - tslib "^2.0.3" - -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-json-comments@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -superstruct@^0.14.2: - version "0.14.2" - resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.14.2.tgz#0dbcdf3d83676588828f1cf5ed35cda02f59025b" - integrity sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ== - -supports-color@8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -text-encoding-utf-8@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13" - integrity sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg== - -"through@>=2.2.7 <3": - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -toml@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee" - integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= - -tslib@^2.0.3: - version "2.3.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" - integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== - -tweetnacl@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" - integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== - -utf-8-validate@^5.0.2: - version "5.0.7" - resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.7.tgz#c15a19a6af1f7ad9ec7ddc425747ca28c3644922" - integrity sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q== - dependencies: - node-gyp-build "^4.3.0" - -uuid@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -uuid@^8.3.0: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -which@2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -workerpool@6.1.5: - version "6.1.5" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.5.tgz#0f7cf076b6215fd7e1da903ff6f22ddd1886b581" - integrity sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw== - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -ws@^7.4.5: - version "7.5.5" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881" - integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== - -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - -yargs-parser@20.2.4: - version "20.2.4" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" - integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== - -yargs-parser@^20.2.2: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - -yargs-unparser@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" - integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== - dependencies: - camelcase "^6.0.0" - decamelize "^4.0.0" - flat "^5.0.2" - is-plain-obj "^2.1.0" - -yargs@16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/examples/yarn.lock b/examples/yarn.lock deleted file mode 100644 index fb57ccd1..00000000 --- a/examples/yarn.lock +++ /dev/null @@ -1,4 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - diff --git a/ts/jest.config.js b/jest.config.js similarity index 100% rename from ts/jest.config.js rename to jest.config.js diff --git a/lang/Cargo.toml b/lang/Cargo.toml deleted file mode 100644 index 8ae10adf..00000000 --- a/lang/Cargo.toml +++ /dev/null @@ -1,44 +0,0 @@ -[package] -name = "anchor-lang" -version = "0.24.2" -authors = ["Serum Foundation "] -repository = "https://github.com/project-serum/anchor" -rust-version = "1.56" -edition = "2021" -license = "Apache-2.0" -description = "Solana Sealevel eDSL" - -[features] -init-if-needed = ["anchor-derive-accounts/init-if-needed"] -derive = [] -default = [] -anchor-debug = [ - "anchor-attribute-access-control/anchor-debug", - "anchor-attribute-account/anchor-debug", - "anchor-attribute-constant/anchor-debug", - "anchor-attribute-error/anchor-debug", - "anchor-attribute-event/anchor-debug", - "anchor-attribute-interface/anchor-debug", - "anchor-attribute-program/anchor-debug", - "anchor-attribute-program/anchor-debug", - "anchor-attribute-state/anchor-debug", - "anchor-derive-accounts/anchor-debug" -] - -[dependencies] -anchor-attribute-access-control = { path = "./attribute/access-control", version = "0.24.2" } -anchor-attribute-account = { path = "./attribute/account", version = "0.24.2" } -anchor-attribute-constant = { path = "./attribute/constant", version = "0.24.2" } -anchor-attribute-error = { path = "./attribute/error", version = "0.24.2" } -anchor-attribute-program = { path = "./attribute/program", version = "0.24.2" } -anchor-attribute-state = { path = "./attribute/state", version = "0.24.2" } -anchor-attribute-interface = { path = "./attribute/interface", version = "0.24.2" } -anchor-attribute-event = { path = "./attribute/event", version = "0.24.2" } -anchor-derive-accounts = { path = "./derive/accounts", version = "0.24.2" } -arrayref = "0.3.6" -base64 = "0.13.0" -borsh = "0.9" -bytemuck = "1.4.0" -solana-program = "~1.9.13" -thiserror = "1.0.20" -bincode = "1.3.3" diff --git a/lang/attribute/access-control/Cargo.toml b/lang/attribute/access-control/Cargo.toml deleted file mode 100644 index 8647d83c..00000000 --- a/lang/attribute/access-control/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "anchor-attribute-access-control" -version = "0.24.2" -authors = ["Serum Foundation "] -repository = "https://github.com/project-serum/anchor" -license = "Apache-2.0" -description = "Anchor attribute macro for instruction access control" -rust-version = "1.56" -edition = "2021" - -[lib] -proc-macro = true - -[features] -anchor-debug = ["anchor-syn/anchor-debug"] - -[dependencies] -proc-macro2 = "1.0" -quote = "1.0" -syn = { version = "1.0.60", features = ["full"] } -anyhow = "1.0.32" -anchor-syn = { path = "../../syn", version = "0.24.2" } -regex = "1.0" diff --git a/lang/attribute/access-control/src/lib.rs b/lang/attribute/access-control/src/lib.rs deleted file mode 100644 index 6b39b9b7..00000000 --- a/lang/attribute/access-control/src/lib.rs +++ /dev/null @@ -1,81 +0,0 @@ -extern crate proc_macro; - -use quote::quote; -use syn::parse_macro_input; - -/// Executes the given access control method before running the decorated -/// instruction handler. Any method in scope of the attribute can be invoked -/// with any arguments from the associated instruction handler. -/// -/// # Example -/// -/// ```ignore -/// use anchor_lang::prelude::*; -/// -/// #[program] -/// mod errors { -/// use super::*; -/// -/// #[access_control(Create::accounts(&ctx, bump_seed))] -/// pub fn create(ctx: Context, bump_seed: u8) -> Result<()> { -/// let my_account = &mut ctx.accounts.my_account; -/// my_account.bump_seed = bump_seed; -/// } -/// } -/// -/// #[derive(Accounts)] -/// pub struct Create { -/// #[account(init)] -/// my_account: Account<'info, MyAccount>, -/// } -/// -/// impl Create { -/// pub fn accounts(ctx: &Context, bump_seed: u8) -> Result<()> { -/// let seeds = &[ctx.accounts.my_account.to_account_info().key.as_ref(), &[bump_seed]]; -/// Pubkey::create_program_address(seeds, ctx.program_id) -/// .map_err(|_| ErrorCode::InvalidNonce)?; -/// Ok(()) -/// } -/// } -/// ``` -/// -/// This example demonstrates a useful pattern. Not only can you use -/// `#[access_control]` to ensure any invariants or preconditions hold prior to -/// executing an instruction, but also it can be used to finish any validation -/// on the `Accounts` struct, particularly when instruction arguments are -/// needed. Here, we use the given `bump_seed` to verify it creates a valid -/// program-derived address. -#[proc_macro_attribute] -pub fn access_control( - args: proc_macro::TokenStream, - input: proc_macro::TokenStream, -) -> proc_macro::TokenStream { - let mut args = args.to_string(); - args.retain(|c| !c.is_whitespace()); - let access_control: Vec = args - .split(')') - .filter(|ac| !ac.is_empty()) - .map(|ac| format!("{})", ac)) // Put back on the split char. - .map(|ac| format!("{}?;", ac)) // Add `?;` syntax. - .map(|ac| ac.parse().unwrap()) - .collect(); - - let item_fn = parse_macro_input!(input as syn::ItemFn); - - let fn_attrs = item_fn.attrs; - let fn_vis = item_fn.vis; - let fn_sig = item_fn.sig; - let fn_block = item_fn.block; - - let fn_stmts = fn_block.stmts; - - proc_macro::TokenStream::from(quote! { - #(#fn_attrs)* - #fn_vis #fn_sig { - - #(#access_control)* - - #(#fn_stmts)* - } - }) -} diff --git a/lang/attribute/account/Cargo.toml b/lang/attribute/account/Cargo.toml deleted file mode 100644 index b565d56d..00000000 --- a/lang/attribute/account/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "anchor-attribute-account" -version = "0.24.2" -authors = ["Serum Foundation "] -repository = "https://github.com/project-serum/anchor" -license = "Apache-2.0" -description = "Anchor attribute macro for defining an account" -rust-version = "1.56" -edition = "2021" - -[lib] -proc-macro = true - -[features] -anchor-debug = ["anchor-syn/anchor-debug"] - -[dependencies] -proc-macro2 = "1.0" -quote = "1.0" -syn = { version = "1.0.60", features = ["full"] } -anyhow = "1.0.32" -anchor-syn = { path = "../../syn", version = "0.24.2", features = ["hash"] } -rustversion = "1.0.3" -bs58 = "0.4.0" \ No newline at end of file diff --git a/lang/attribute/account/src/id.rs b/lang/attribute/account/src/id.rs deleted file mode 100644 index 83d87abd..00000000 --- a/lang/attribute/account/src/id.rs +++ /dev/null @@ -1,296 +0,0 @@ -//! Copied from solana/sdk/macro so that Anchor programs don't need to specify -//! `solana_program` as an additional crate dependency, but instead can access -//! it via `anchor_lang::declare_id`. -//! -//! Convenience macro to declare a static public key and functions to interact with it -//! -//! Input: a single literal base58 string representation of a program's id - -extern crate proc_macro; - -use proc_macro2::{Delimiter, Span, TokenTree}; -use quote::{quote, ToTokens}; -use std::convert::TryFrom; -use syn::{ - bracketed, - parse::{Parse, ParseStream, Result}, - punctuated::Punctuated, - token::Bracket, - Expr, Ident, LitByte, LitStr, Path, Token, -}; - -fn parse_id( - input: ParseStream, - pubkey_type: proc_macro2::TokenStream, -) -> Result { - let id = if input.peek(syn::LitStr) { - let id_literal: LitStr = input.parse()?; - parse_pubkey(&id_literal, &pubkey_type)? - } else { - let expr: Expr = input.parse()?; - quote! { #expr } - }; - - if !input.is_empty() { - let stream: proc_macro2::TokenStream = input.parse()?; - return Err(syn::Error::new_spanned(stream, "unexpected token")); - } - Ok(id) -} - -fn id_to_tokens( - id: &proc_macro2::TokenStream, - pubkey_type: proc_macro2::TokenStream, - tokens: &mut proc_macro2::TokenStream, -) { - tokens.extend(quote! { - /// The static program ID - pub static ID: #pubkey_type = #id; - - /// Confirms that a given pubkey is equivalent to the program ID - pub fn check_id(id: &#pubkey_type) -> bool { - id == &ID - } - - /// Returns the program ID - pub fn id() -> #pubkey_type { - ID - } - - #[cfg(test)] - #[test] - fn test_id() { - assert!(check_id(&id())); - } - }); -} - -fn deprecated_id_to_tokens( - id: &proc_macro2::TokenStream, - pubkey_type: proc_macro2::TokenStream, - tokens: &mut proc_macro2::TokenStream, -) { - tokens.extend(quote! { - /// The static program ID - pub static ID: #pubkey_type = #id; - - /// Confirms that a given pubkey is equivalent to the program ID - #[deprecated()] - pub fn check_id(id: &#pubkey_type) -> bool { - id == &ID - } - - /// Returns the program ID - #[deprecated()] - pub fn id() -> #pubkey_type { - ID - } - - #[cfg(test)] - #[test] - fn test_id() { - #[allow(deprecated)] - assert!(check_id(&id())); - } - }); -} - -pub struct Id(proc_macro2::TokenStream); - -impl Parse for Id { - fn parse(input: ParseStream) -> Result { - parse_id( - input, - quote! { anchor_lang::solana_program::pubkey::Pubkey }, - ) - .map(Self) - } -} - -impl ToTokens for Id { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - id_to_tokens( - &self.0, - quote! { anchor_lang::solana_program::pubkey::Pubkey }, - tokens, - ) - } -} - -struct IdDeprecated(proc_macro2::TokenStream); - -impl Parse for IdDeprecated { - fn parse(input: ParseStream) -> Result { - parse_id( - input, - quote! { anchor_lang::solana_program::pubkey::Pubkey }, - ) - .map(Self) - } -} - -impl ToTokens for IdDeprecated { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - deprecated_id_to_tokens( - &self.0, - quote! { anchor_lang::solana_program::pubkey::Pubkey }, - tokens, - ) - } -} - -struct ProgramSdkId(proc_macro2::TokenStream); -impl Parse for ProgramSdkId { - fn parse(input: ParseStream) -> Result { - parse_id( - input, - quote! { anchor_lang::solana_program::pubkey::Pubkey }, - ) - .map(Self) - } -} - -impl ToTokens for ProgramSdkId { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - id_to_tokens( - &self.0, - quote! { anchor_lang::solana_program::pubkey::Pubkey }, - tokens, - ) - } -} - -struct ProgramSdkIdDeprecated(proc_macro2::TokenStream); -impl Parse for ProgramSdkIdDeprecated { - fn parse(input: ParseStream) -> Result { - parse_id( - input, - quote! { anchor_lang::solana_program::pubkey::Pubkey }, - ) - .map(Self) - } -} - -impl ToTokens for ProgramSdkIdDeprecated { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - deprecated_id_to_tokens( - &self.0, - quote! { anchor_lang::solana_program::pubkey::Pubkey }, - tokens, - ) - } -} - -#[allow(dead_code)] // `respan` may be compiled out -struct RespanInput { - to_respan: Path, - respan_using: Span, -} - -impl Parse for RespanInput { - fn parse(input: ParseStream) -> Result { - let to_respan: Path = input.parse()?; - let _comma: Token![,] = input.parse()?; - let respan_tree: TokenTree = input.parse()?; - match respan_tree { - TokenTree::Group(g) if g.delimiter() == Delimiter::None => { - let ident: Ident = syn::parse2(g.stream())?; - Ok(RespanInput { - to_respan, - respan_using: ident.span(), - }) - } - val => Err(syn::Error::new_spanned( - val, - "expected None-delimited group", - )), - } - } -} - -fn parse_pubkey( - id_literal: &LitStr, - pubkey_type: &proc_macro2::TokenStream, -) -> Result { - let id_vec = bs58::decode(id_literal.value()) - .into_vec() - .map_err(|_| syn::Error::new_spanned(&id_literal, "failed to decode base58 string"))?; - let id_array = <[u8; 32]>::try_from(<&[u8]>::clone(&&id_vec[..])).map_err(|_| { - syn::Error::new_spanned( - &id_literal, - format!("pubkey array is not 32 bytes long: len={}", id_vec.len()), - ) - })?; - let bytes = id_array.iter().map(|b| LitByte::new(*b, Span::call_site())); - Ok(quote! { - #pubkey_type::new_from_array( - [#(#bytes,)*] - ) - }) -} - -struct Pubkeys { - method: Ident, - num: usize, - pubkeys: proc_macro2::TokenStream, -} -impl Parse for Pubkeys { - fn parse(input: ParseStream) -> Result { - let pubkey_type = quote! { - anchor_lang::solana_program::pubkey::Pubkey - }; - - let method = input.parse()?; - let _comma: Token![,] = input.parse()?; - let (num, pubkeys) = if input.peek(syn::LitStr) { - let id_literal: LitStr = input.parse()?; - (1, parse_pubkey(&id_literal, &pubkey_type)?) - } else if input.peek(Bracket) { - let pubkey_strings; - bracketed!(pubkey_strings in input); - let punctuated: Punctuated = - Punctuated::parse_terminated(&pubkey_strings)?; - let mut pubkeys: Punctuated = Punctuated::new(); - for string in punctuated.iter() { - pubkeys.push(parse_pubkey(string, &pubkey_type)?); - } - (pubkeys.len(), quote! {#pubkeys}) - } else { - let stream: proc_macro2::TokenStream = input.parse()?; - return Err(syn::Error::new_spanned(stream, "unexpected token")); - }; - - Ok(Pubkeys { - method, - num, - pubkeys, - }) - } -} - -impl ToTokens for Pubkeys { - fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { - let Pubkeys { - method, - num, - pubkeys, - } = self; - - let pubkey_type = quote! { - anchor_lang::solana_program::pubkey::Pubkey - }; - if *num == 1 { - tokens.extend(quote! { - pub fn #method() -> #pubkey_type { - #pubkeys - } - }); - } else { - tokens.extend(quote! { - pub fn #method() -> ::std::vec::Vec<#pubkey_type> { - vec![#pubkeys] - } - }); - } - } -} diff --git a/lang/attribute/account/src/lib.rs b/lang/attribute/account/src/lib.rs deleted file mode 100644 index 8692d1fc..00000000 --- a/lang/attribute/account/src/lib.rs +++ /dev/null @@ -1,321 +0,0 @@ -extern crate proc_macro; - -use quote::quote; -use syn::parse_macro_input; - -mod id; - -/// An attribute for a data structure representing a Solana account. -/// -/// `#[account]` generates trait implementations for the following traits: -/// -/// - [`AccountSerialize`](./trait.AccountSerialize.html) -/// - [`AccountDeserialize`](./trait.AccountDeserialize.html) -/// - [`AnchorSerialize`](./trait.AnchorSerialize.html) -/// - [`AnchorDeserialize`](./trait.AnchorDeserialize.html) -/// - [`Clone`](https://doc.rust-lang.org/std/clone/trait.Clone.html) -/// - [`Discriminator`](./trait.Discriminator.html) -/// - [`Owner`](./trait.Owner.html) -/// -/// When implementing account serialization traits the first 8 bytes are -/// reserved for a unique account discriminator, self described by the first 8 -/// bytes of the SHA256 of the account's Rust ident. -/// -/// As a result, any calls to `AccountDeserialize`'s `try_deserialize` will -/// check this discriminator. If it doesn't match, an invalid account was given, -/// and the account deserialization will exit with an error. -/// -/// # Zero Copy Deserialization -/// -/// **WARNING**: Zero copy deserialization is an experimental feature. It's -/// recommended to use it only when necessary, i.e., when you have extremely -/// large accounts that cannot be Borsh deserialized without hitting stack or -/// heap limits. -/// -/// ## Usage -/// -/// To enable zero-copy-deserialization, one can pass in the `zero_copy` -/// argument to the macro as follows: -/// -/// ```ignore -/// #[account(zero_copy)] -/// ``` -/// -/// This can be used to conveniently implement -/// [`ZeroCopy`](./trait.ZeroCopy.html) so that the account can be used -/// with [`AccountLoader`](./accounts/account_loader/struct.AccountLoader.html). -/// -/// Other than being more efficient, the most salient benefit this provides is -/// the ability to define account types larger than the max stack or heap size. -/// When using borsh, the account has to be copied and deserialized into a new -/// data structure and thus is constrained by stack and heap limits imposed by -/// the BPF VM. With zero copy deserialization, all bytes from the account's -/// backing `RefCell<&mut [u8]>` are simply re-interpreted as a reference to -/// the data structure. No allocations or copies necessary. Hence the ability -/// to get around stack and heap limitations. -/// -/// To facilitate this, all fields in an account must be constrained to be -/// "plain old data", i.e., they must implement -/// [`Pod`](https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html). Please review the -/// [`safety`](https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html#safety) -/// section before using. -#[proc_macro_attribute] -pub fn account( - args: proc_macro::TokenStream, - input: proc_macro::TokenStream, -) -> proc_macro::TokenStream { - let mut namespace = "".to_string(); - let mut is_zero_copy = false; - let args_str = args.to_string(); - let args: Vec<&str> = args_str.split(',').collect(); - if args.len() > 2 { - panic!("Only two args are allowed to the account attribute.") - } - for arg in args { - let ns = arg - .to_string() - .replace('\"', "") - .chars() - .filter(|c| !c.is_whitespace()) - .collect(); - if ns == "zero_copy" { - is_zero_copy = true; - } else { - namespace = ns; - } - } - - let account_strct = parse_macro_input!(input as syn::ItemStruct); - let account_name = &account_strct.ident; - let (impl_gen, type_gen, where_clause) = account_strct.generics.split_for_impl(); - - let discriminator: proc_macro2::TokenStream = { - // Namespace the discriminator to prevent collisions. - let discriminator_preimage = { - // For now, zero copy accounts can't be namespaced. - if namespace.is_empty() { - format!("account:{}", account_name) - } else { - format!("{}:{}", namespace, account_name) - } - }; - - let mut discriminator = [0u8; 8]; - discriminator.copy_from_slice( - &anchor_syn::hash::hash(discriminator_preimage.as_bytes()).to_bytes()[..8], - ); - format!("{:?}", discriminator).parse().unwrap() - }; - - let owner_impl = { - if namespace.is_empty() { - quote! { - #[automatically_derived] - impl #impl_gen anchor_lang::Owner for #account_name #type_gen #where_clause { - fn owner() -> Pubkey { - crate::ID - } - } - } - } else { - quote! {} - } - }; - - proc_macro::TokenStream::from({ - if is_zero_copy { - quote! { - #[zero_copy] - #account_strct - - #[automatically_derived] - unsafe impl #impl_gen anchor_lang::__private::bytemuck::Pod for #account_name #type_gen #where_clause {} - #[automatically_derived] - unsafe impl #impl_gen anchor_lang::__private::bytemuck::Zeroable for #account_name #type_gen #where_clause {} - - #[automatically_derived] - impl #impl_gen anchor_lang::ZeroCopy for #account_name #type_gen #where_clause {} - - #[automatically_derived] - impl #impl_gen anchor_lang::Discriminator for #account_name #type_gen #where_clause { - fn discriminator() -> [u8; 8] { - #discriminator - } - } - - // This trait is useful for clients deserializing accounts. - // It's expected on-chain programs deserialize via zero-copy. - #[automatically_derived] - impl #impl_gen anchor_lang::AccountDeserialize for #account_name #type_gen #where_clause { - fn try_deserialize(buf: &mut &[u8]) -> anchor_lang::Result { - if buf.len() < #discriminator.len() { - return Err(anchor_lang::error::ErrorCode::AccountDiscriminatorNotFound.into()); - } - let given_disc = &buf[..8]; - if &#discriminator != given_disc { - return Err(anchor_lang::error::ErrorCode::AccountDiscriminatorMismatch.into()); - } - Self::try_deserialize_unchecked(buf) - } - - fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result { - let data: &[u8] = &buf[8..]; - // Re-interpret raw bytes into the POD data structure. - let account = anchor_lang::__private::bytemuck::from_bytes(data); - // Copy out the bytes into a new, owned data structure. - Ok(*account) - } - } - - #owner_impl - } - } else { - quote! { - #[derive(AnchorSerialize, AnchorDeserialize, Clone)] - #account_strct - - #[automatically_derived] - impl #impl_gen anchor_lang::AccountSerialize for #account_name #type_gen #where_clause { - fn try_serialize(&self, writer: &mut W) -> anchor_lang::Result<()> { - if writer.write_all(&#discriminator).is_err() { - return Err(anchor_lang::error::ErrorCode::AccountDidNotSerialize.into()); - } - - if AnchorSerialize::serialize(self, writer).is_err() { - return Err(anchor_lang::error::ErrorCode::AccountDidNotSerialize.into()); - } - Ok(()) - } - } - - #[automatically_derived] - impl #impl_gen anchor_lang::AccountDeserialize for #account_name #type_gen #where_clause { - fn try_deserialize(buf: &mut &[u8]) -> anchor_lang::Result { - if buf.len() < #discriminator.len() { - return Err(anchor_lang::error::ErrorCode::AccountDiscriminatorNotFound.into()); - } - let given_disc = &buf[..8]; - if &#discriminator != given_disc { - return Err(anchor_lang::error::ErrorCode::AccountDiscriminatorMismatch.into()); - } - Self::try_deserialize_unchecked(buf) - } - - fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result { - let mut data: &[u8] = &buf[8..]; - AnchorDeserialize::deserialize(&mut data) - .map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotDeserialize.into()) - } - } - - #[automatically_derived] - impl #impl_gen anchor_lang::Discriminator for #account_name #type_gen #where_clause { - fn discriminator() -> [u8; 8] { - #discriminator - } - } - - #owner_impl - } - } - }) -} - -#[proc_macro_derive(ZeroCopyAccessor, attributes(accessor))] -pub fn derive_zero_copy_accessor(item: proc_macro::TokenStream) -> proc_macro::TokenStream { - let account_strct = parse_macro_input!(item as syn::ItemStruct); - let account_name = &account_strct.ident; - let (impl_gen, ty_gen, where_clause) = account_strct.generics.split_for_impl(); - - let fields = match &account_strct.fields { - syn::Fields::Named(n) => n, - _ => panic!("Fields must be named"), - }; - let methods: Vec = fields - .named - .iter() - .filter_map(|field: &syn::Field| { - field - .attrs - .iter() - .find(|attr| anchor_syn::parser::tts_to_string(&attr.path) == "accessor") - .map(|attr| { - let mut tts = attr.tokens.clone().into_iter(); - let g_stream = match tts.next().expect("Must have a token group") { - proc_macro2::TokenTree::Group(g) => g.stream(), - _ => panic!("Invalid syntax"), - }; - let accessor_ty = match g_stream.into_iter().next() { - Some(token) => token, - _ => panic!("Missing accessor type"), - }; - - let field_name = field.ident.as_ref().unwrap(); - - let get_field: proc_macro2::TokenStream = - format!("get_{}", field_name).parse().unwrap(); - let set_field: proc_macro2::TokenStream = - format!("set_{}", field_name).parse().unwrap(); - - quote! { - pub fn #get_field(&self) -> #accessor_ty { - anchor_lang::__private::ZeroCopyAccessor::get(&self.#field_name) - } - pub fn #set_field(&mut self, input: &#accessor_ty) { - self.#field_name = anchor_lang::__private::ZeroCopyAccessor::set(input); - } - } - }) - }) - .collect(); - proc_macro::TokenStream::from(quote! { - #[automatically_derived] - impl #impl_gen #account_name #ty_gen #where_clause { - #(#methods)* - } - }) -} - -/// A data structure that can be used as an internal field for a zero copy -/// deserialized account, i.e., a struct marked with `#[account(zero_copy)]`. -/// -/// This is just a convenient alias for -/// -/// ```ignore -/// #[derive(Copy, Clone)] -/// #[repr(packed)] -/// struct MyStruct {...} -/// ``` -#[proc_macro_attribute] -pub fn zero_copy( - _args: proc_macro::TokenStream, - item: proc_macro::TokenStream, -) -> proc_macro::TokenStream { - let account_strct = parse_macro_input!(item as syn::ItemStruct); - - // Takes the first repr. It's assumed that more than one are not on the - // struct. - let attr = account_strct - .attrs - .iter() - .find(|attr| anchor_syn::parser::tts_to_string(&attr.path) == "repr"); - - let repr = match attr { - Some(_attr) => quote! {}, - None => quote! {#[repr(C)]}, - }; - - proc_macro::TokenStream::from(quote! { - #[derive(anchor_lang::__private::ZeroCopyAccessor, Copy, Clone)] - #repr - #account_strct - }) -} - -/// Defines the program's ID. This should be used at the root of all Anchor -/// based programs. -#[proc_macro] -pub fn declare_id(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let id = parse_macro_input!(input as id::Id); - proc_macro::TokenStream::from(quote! {#id}) -} diff --git a/lang/attribute/constant/Cargo.toml b/lang/attribute/constant/Cargo.toml deleted file mode 100644 index c8e481d7..00000000 --- a/lang/attribute/constant/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "anchor-attribute-constant" -version = "0.24.2" -authors = ["Serum Foundation "] -repository = "https://github.com/project-serum/anchor" -license = "Apache-2.0" -description = "Anchor attribute macro for creating constant types" -rust-version = "1.56" -edition = "2021" - -[lib] -proc-macro = true - -[features] -anchor-debug = ["anchor-syn/anchor-debug"] - -[dependencies] -proc-macro2 = "1.0" -syn = { version = "1.0.60", features = ["full"] } -anchor-syn = { path = "../../syn", version = "0.24.2" } diff --git a/lang/attribute/constant/src/lib.rs b/lang/attribute/constant/src/lib.rs deleted file mode 100644 index 98092f4b..00000000 --- a/lang/attribute/constant/src/lib.rs +++ /dev/null @@ -1,11 +0,0 @@ -extern crate proc_macro; - -/// A marker attribute used to mark const values that should be included in the -/// generated IDL but functionally does nothing. -#[proc_macro_attribute] -pub fn constant( - _attr: proc_macro::TokenStream, - input: proc_macro::TokenStream, -) -> proc_macro::TokenStream { - input -} diff --git a/lang/attribute/error/Cargo.toml b/lang/attribute/error/Cargo.toml deleted file mode 100644 index efa6406c..00000000 --- a/lang/attribute/error/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "anchor-attribute-error" -version = "0.24.2" -authors = ["Serum Foundation "] -repository = "https://github.com/project-serum/anchor" -license = "Apache-2.0" -description = "Anchor attribute macro for creating error types" -rust-version = "1.56" -edition = "2021" - -[lib] -proc-macro = true - -[features] -anchor-debug = ["anchor-syn/anchor-debug"] - -[dependencies] -proc-macro2 = "1.0" -quote = "1.0" -syn = { version = "1.0.60", features = ["full"] } -anchor-syn = { path = "../../syn", version = "0.24.2" } diff --git a/lang/attribute/error/src/lib.rs b/lang/attribute/error/src/lib.rs deleted file mode 100644 index 3f403de2..00000000 --- a/lang/attribute/error/src/lib.rs +++ /dev/null @@ -1,116 +0,0 @@ -extern crate proc_macro; - -use proc_macro::TokenStream; -use quote::quote; - -use anchor_syn::codegen; -use anchor_syn::parser::error::{self as error_parser, ErrorInput}; -use anchor_syn::ErrorArgs; -use syn::{parse_macro_input, Expr}; - -/// Generates `Error` and `type Result = Result` types to be -/// used as return types from Anchor instruction handlers. Importantly, the -/// attribute implements -/// [`From`](https://doc.rust-lang.org/std/convert/trait.From.html) on the -/// `ErrorCode` to support converting from the user defined error enum *into* -/// the generated `Error`. -/// -/// # Example -/// -/// ```ignore -/// use anchor_lang::prelude::*; -/// -/// #[program] -/// mod errors { -/// use super::*; -/// pub fn hello(_ctx: Context) -> Result<()> { -/// Err(error!(MyError::Hello)) -/// } -/// } -/// -/// #[derive(Accounts)] -/// pub struct Hello {} -/// -/// #[error_code] -/// pub enum MyError { -/// #[msg("This is an error message clients will automatically display")] -/// Hello, -/// } -/// ``` -/// -/// Note that we generate a new `Error` type so that we can return either the -/// user defined error enum *or* a -/// [`ProgramError`](../solana_program/enum.ProgramError.html), which is used -/// pervasively, throughout solana program crates. The generated `Error` type -/// should almost never be used directly, as the user defined error is -/// preferred. In the example above, `error!(MyError::Hello)`. -/// -/// # Msg -/// -/// The `#[msg(..)]` attribute is inert, and is used only as a marker so that -/// parsers and IDLs can map error codes to error messages. -#[proc_macro_attribute] -pub fn error_code( - args: proc_macro::TokenStream, - input: proc_macro::TokenStream, -) -> proc_macro::TokenStream { - let args = match args.is_empty() { - true => None, - false => Some(parse_macro_input!(args as ErrorArgs)), - }; - let mut error_enum = parse_macro_input!(input as syn::ItemEnum); - let error = codegen::error::generate(error_parser::parse(&mut error_enum, args)); - proc_macro::TokenStream::from(error) -} - -/// Generates an [`Error::AnchorError`](../../anchor_lang/error/enum.Error.html) that includes file and line information. -/// -/// # Example -/// ```rust,ignore -/// #[program] -/// mod errors { -/// use super::*; -/// pub fn example(_ctx: Context) -> Result<()> { -/// Err(error!(MyError::Hello)) -/// } -/// } -/// -/// #[error_code] -/// pub enum MyError { -/// #[msg("This is an error message clients will automatically display")] -/// Hello, -/// } -/// ``` -#[proc_macro] -pub fn error(ts: proc_macro::TokenStream) -> TokenStream { - let input = parse_macro_input!(ts as ErrorInput); - let error_code = input.error_code; - create_error(error_code, true, None) -} - -fn create_error(error_code: Expr, source: bool, account_name: Option) -> TokenStream { - let error_origin = match (source, account_name) { - (false, None) => quote! { None }, - (false, Some(account_name)) => quote! { - Some(anchor_lang::error::ErrorOrigin::AccountName(#account_name.to_string())) - }, - (true, _) => quote! { - Some(anchor_lang::error::ErrorOrigin::Source(anchor_lang::error::Source { - filename: file!(), - line: line!() - })) - }, - }; - - TokenStream::from(quote! { - anchor_lang::error::Error::from( - anchor_lang::error::AnchorError { - error_name: #error_code.name(), - error_code_number: #error_code.into(), - error_msg: #error_code.to_string(), - error_origin: #error_origin, - compared_values: None - } - ) - }) -} diff --git a/lang/attribute/event/Cargo.toml b/lang/attribute/event/Cargo.toml deleted file mode 100644 index 89d3a6a4..00000000 --- a/lang/attribute/event/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "anchor-attribute-event" -version = "0.24.2" -authors = ["Serum Foundation "] -repository = "https://github.com/project-serum/anchor" -license = "Apache-2.0" -description = "Anchor attribute macro for defining an event" -rust-version = "1.56" -edition = "2021" - -[lib] -proc-macro = true - -[features] -anchor-debug = ["anchor-syn/anchor-debug"] - -[dependencies] -proc-macro2 = "1.0" -quote = "1.0" -syn = { version = "1.0.60", features = ["full"] } -anyhow = "1.0.32" -anchor-syn = { path = "../../syn", version = "0.24.2", features = ["hash"] } diff --git a/lang/attribute/event/src/lib.rs b/lang/attribute/event/src/lib.rs deleted file mode 100644 index cabe676d..00000000 --- a/lang/attribute/event/src/lib.rs +++ /dev/null @@ -1,92 +0,0 @@ -extern crate proc_macro; - -use quote::quote; -use syn::parse_macro_input; - -/// The event attribute allows a struct to be used with -/// [emit!](./macro.emit.html) so that programs can log significant events in -/// their programs that clients can subscribe to. Currently, this macro is for -/// structs only. -/// -/// See the [`emit!` macro](emit!) for an example. -#[proc_macro_attribute] -pub fn event( - _args: proc_macro::TokenStream, - input: proc_macro::TokenStream, -) -> proc_macro::TokenStream { - let event_strct = parse_macro_input!(input as syn::ItemStruct); - - let event_name = &event_strct.ident; - - let discriminator: proc_macro2::TokenStream = { - let discriminator_preimage = format!("event:{}", event_name); - let mut discriminator = [0u8; 8]; - discriminator.copy_from_slice( - &anchor_syn::hash::hash(discriminator_preimage.as_bytes()).to_bytes()[..8], - ); - format!("{:?}", discriminator).parse().unwrap() - }; - - proc_macro::TokenStream::from(quote! { - #[derive(anchor_lang::__private::EventIndex, AnchorSerialize, AnchorDeserialize)] - #event_strct - - impl anchor_lang::Event for #event_name { - fn data(&self) -> Vec { - let mut d = #discriminator.to_vec(); - d.append(&mut self.try_to_vec().unwrap()); - d - } - } - - impl anchor_lang::Discriminator for #event_name { - fn discriminator() -> [u8; 8] { - #discriminator - } - } - }) -} - -/// Logs an event that can be subscribed to by clients. -/// Uses the [`sol_log_data`](https://docs.rs/solana-program/latest/solana_program/log/fn.sol_log_data.html) -/// syscall which results in the following log: -/// ```ignore -/// Program data: -/// ``` -/// # Example -/// -/// ```rust,ignore -/// use anchor_lang::prelude::*; -/// -/// // handler function inside #[program] -/// pub fn initialize(_ctx: Context) -> Result<()> { -/// emit!(MyEvent { -/// data: 5, -/// label: [1,2,3,4,5], -/// }); -/// Ok(()) -/// } -/// -/// #[event] -/// pub struct MyEvent { -/// pub data: u64, -/// pub label: [u8; 5], -/// } -/// ``` -#[proc_macro] -pub fn emit(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - let data: proc_macro2::TokenStream = input.into(); - proc_macro::TokenStream::from(quote! { - { - anchor_lang::solana_program::log::sol_log_data(&[&anchor_lang::Event::data(&#data)]); - } - }) -} - -// EventIndex is a marker macro. It functionally does nothing other than -// allow one to mark fields with the `#[index]` inert attribute, which is -// used to add metadata to IDLs. -#[proc_macro_derive(EventIndex, attributes(index))] -pub fn derive_event(_item: proc_macro::TokenStream) -> proc_macro::TokenStream { - proc_macro::TokenStream::from(quote! {}) -} diff --git a/lang/attribute/interface/Cargo.toml b/lang/attribute/interface/Cargo.toml deleted file mode 100644 index b613cd36..00000000 --- a/lang/attribute/interface/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -name = "anchor-attribute-interface" -version = "0.24.2" -authors = ["Serum Foundation "] -repository = "https://github.com/project-serum/anchor" -license = "Apache-2.0" -description = "Attribute for defining a program interface trait" -rust-version = "1.56" -edition = "2021" - -[lib] -proc-macro = true - -[features] -anchor-debug = ["anchor-syn/anchor-debug"] - -[dependencies] -proc-macro2 = "1.0" -quote = "1.0" -syn = { version = "1.0.60", features = ["full"] } -anyhow = "1.0.32" -anchor-syn = { path = "../../syn", version = "0.24.2" } -heck = "0.3.2" diff --git a/lang/attribute/interface/src/lib.rs b/lang/attribute/interface/src/lib.rs deleted file mode 100644 index c2656eb5..00000000 --- a/lang/attribute/interface/src/lib.rs +++ /dev/null @@ -1,243 +0,0 @@ -extern crate proc_macro; - -use anchor_syn::parser; -use heck::SnakeCase; -use quote::quote; -use syn::parse_macro_input; - -/// The `#[interface]` attribute allows one to define an external program -/// dependency, without having any knowledge about the program, other than -/// the fact that it implements the given trait. -/// -/// Additionally, the attribute generates a client that can be used to perform -/// CPI to these external dependencies. -/// -/// # Example -/// -/// In the following example, we have a counter program, where the count -/// can only be set if the configured external program authorizes it. -/// -/// ## Defining an `#[interface]` -/// -/// First we define the program that depends on an external interface. -/// -/// ```ignore -/// use anchor_lang::prelude::*; -/// -/// #[interface] -/// pub trait Auth<'info, T: Accounts<'info>> { -/// fn is_authorized(ctx: Context, current: u64, new: u64) -> anchor_lang::Result<()>; -/// } -/// -/// #[program] -/// pub mod counter { -/// use super::*; -/// -/// #[state] -/// pub struct Counter { -/// pub count: u64, -/// pub auth_program: Pubkey, -/// } -/// -/// impl Counter { -/// pub fn new(_ctx: Context, auth_program: Pubkey) -> Result { -/// Ok(Self { -/// count: 0, -/// auth_program, -/// }) -/// } -/// -/// #[access_control(SetCount::accounts(&self, &ctx))] -/// pub fn set_count(&mut self, ctx: Context, new_count: u64) -> Result<()> { -/// // Ask the auth program if we should approve the transaction. -/// let cpi_program = ctx.accounts.auth_program.clone(); -/// let cpi_ctx = CpiContext::new(cpi_program, Empty {}); -/// -/// // This is the client generated by the `#[interface]` attribute. -/// auth::is_authorized(cpi_ctx, self.count, new_count)?; -/// -/// // Approved, so update. -/// self.count = new_count; -/// Ok(()) -/// } -/// } -/// } -/// -/// #[derive(Accounts)] -/// pub struct Empty {} -/// -/// #[derive(Accounts)] -/// pub struct SetCount<'info> { -/// auth_program: AccountInfo<'info>, -/// } -/// -/// impl<'info> SetCount<'info> { -/// pub fn accounts(counter: &Counter, ctx: &Context) -> Result<()> { -/// if ctx.accounts.auth_program.key != &counter.auth_program { -/// return Err(error!(ErrorCode::InvalidAuthProgram)); -/// } -/// Ok(()) -/// } -/// } -/// -/// #[error_code] -/// pub enum ErrorCode { -/// #[msg("Invalid auth program.")] -/// InvalidAuthProgram, -/// } -///``` -/// -/// ## Defining an implementation -/// -/// Now we define the program that implements the interface, which the above -/// program will call. -/// -/// ```ignore -/// use anchor_lang::prelude::*; -/// use counter::Auth; -/// -/// #[program] -/// pub mod counter_auth { -/// use super::*; -/// -/// #[state] -/// pub struct CounterAuth; -/// -/// impl<'info> Auth<'info, Empty> for CounterAuth { -/// fn is_authorized(_ctx: Context, current: u64, new: u64) -> Result<()> { -/// if current % 2 == 0 { -/// if new % 2 == 0 { -/// return Err(ProgramError::Custom(50).into()); // Arbitrary error code. -/// } -/// } else { -/// if new % 2 == 1 { -/// return Err(ProgramError::Custom(60).into()); // Arbitrary error code. -/// } -/// } -/// Ok(()) -/// } -/// } -/// } -/// #[derive(Accounts)] -/// pub struct Empty {} -/// ``` -/// -/// # Returning Values Across CPI -/// -/// The caller above uses a `Result` to act as a boolean. However, in order -/// for this feature to be maximally useful, we need a way to return values from -/// interfaces. For now, one can do this by writing to a shared account, e.g., -/// with the SPL's [Shared Memory Program](https://github.com/solana-labs/solana-program-library/tree/master/shared-memory). -/// In the future, Anchor will add the ability to return values across CPI -/// without having to worry about the details of shared memory accounts. -#[proc_macro_attribute] -pub fn interface( - _args: proc_macro::TokenStream, - input: proc_macro::TokenStream, -) -> proc_macro::TokenStream { - let item_trait = parse_macro_input!(input as syn::ItemTrait); - - let trait_name = item_trait.ident.to_string(); - let mod_name: proc_macro2::TokenStream = item_trait - .ident - .to_string() - .to_snake_case() - .parse() - .unwrap(); - - let methods: Vec = item_trait - .items - .iter() - .filter_map(|trait_item: &syn::TraitItem| match trait_item { - syn::TraitItem::Method(m) => Some(m), - _ => None, - }) - .map(|method: &syn::TraitItemMethod| { - let method_name = &method.sig.ident; - let args: Vec<&syn::PatType> = method - .sig - .inputs - .iter() - .filter_map(|arg: &syn::FnArg| match arg { - syn::FnArg::Typed(pat_ty) => Some(pat_ty), - // TODO: just map this to None once we allow this feature. - _ => panic!("Invalid syntax. No self allowed."), - }) - .filter(|pat_ty| { - let mut ty = parser::tts_to_string(&pat_ty.ty); - ty.retain(|s| !s.is_whitespace()); - !ty.starts_with("Context<") - }) - .collect(); - let args_no_tys: Vec<&Box> = args - .iter() - .map(|arg| { - &arg.pat - }) - .collect(); - let args_struct = { - if args.is_empty() { - quote! { - use anchor_lang::prelude::borsh; - #[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)] - struct Args; - } - } else { - quote! { - use anchor_lang::prelude::borsh; - #[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)] - struct Args { - #(#args),* - } - } - } - }; - - let sighash_arr = anchor_syn::codegen::program::common::sighash(&trait_name, &method_name.to_string()); - let sighash_tts: proc_macro2::TokenStream = - format!("{:?}", sighash_arr).parse().unwrap(); - quote! { - pub fn #method_name<'a,'b, 'c, 'info, T: anchor_lang::Accounts<'info> + anchor_lang::ToAccountMetas + anchor_lang::ToAccountInfos<'info>>( - ctx: anchor_lang::context::CpiContext<'a, 'b, 'c, 'info, T>, - #(#args),* - ) -> anchor_lang::Result<()> { - #args_struct - - let ix = { - let ix = Args { - #(#args_no_tys),* - }; - let mut ix_data = anchor_lang::AnchorSerialize::try_to_vec(&ix) - .map_err(|_| anchor_lang::error::ErrorCode::InstructionDidNotSerialize)?; - let mut data = #sighash_tts.to_vec(); - data.append(&mut ix_data); - let accounts = ctx.to_account_metas(None); - anchor_lang::solana_program::instruction::Instruction { - program_id: *ctx.program.key, - accounts, - data, - } - }; - let mut acc_infos = ctx.to_account_infos(); - acc_infos.push(ctx.program.clone()); - anchor_lang::solana_program::program::invoke_signed( - &ix, - &acc_infos, - ctx.signer_seeds, - ).map_err(Into::into) - } - } - }) - .collect(); - - proc_macro::TokenStream::from(quote! { - #item_trait - - /// Anchor generated module for invoking programs implementing an - /// `#[interface]` via CPI. - mod #mod_name { - use super::*; - #(#methods)* - } - }) -} diff --git a/lang/attribute/program/Cargo.toml b/lang/attribute/program/Cargo.toml deleted file mode 100644 index 943bfacb..00000000 --- a/lang/attribute/program/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "anchor-attribute-program" -version = "0.24.2" -authors = ["Serum Foundation "] -repository = "https://github.com/project-serum/anchor" -license = "Apache-2.0" -description = "Anchor attribute macro for defining a program" -rust-version = "1.56" -edition = "2021" - -[lib] -proc-macro = true - -[features] -anchor-debug = ["anchor-syn/anchor-debug"] - -[dependencies] -proc-macro2 = "1.0" -quote = "1.0" -syn = { version = "1.0.60", features = ["full"] } -anyhow = "1.0.32" -anchor-syn = { path = "../../syn", version = "0.24.2" } diff --git a/lang/attribute/program/src/lib.rs b/lang/attribute/program/src/lib.rs deleted file mode 100644 index 2531d26e..00000000 --- a/lang/attribute/program/src/lib.rs +++ /dev/null @@ -1,16 +0,0 @@ -extern crate proc_macro; - -use quote::ToTokens; -use syn::parse_macro_input; - -/// The `#[program]` attribute defines the module containing all instruction -/// handlers defining all entries into a Solana program. -#[proc_macro_attribute] -pub fn program( - _args: proc_macro::TokenStream, - input: proc_macro::TokenStream, -) -> proc_macro::TokenStream { - parse_macro_input!(input as anchor_syn::Program) - .to_token_stream() - .into() -} diff --git a/lang/attribute/state/Cargo.toml b/lang/attribute/state/Cargo.toml deleted file mode 100644 index a0ec80ff..00000000 --- a/lang/attribute/state/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "anchor-attribute-state" -version = "0.24.2" -authors = ["Serum Foundation "] -repository = "https://github.com/project-serum/anchor" -license = "Apache-2.0" -description = "Attribute for defining a program state struct" -rust-version = "1.56" -edition = "2021" - -[lib] -proc-macro = true - -[features] -anchor-debug = ["anchor-syn/anchor-debug"] - -[dependencies] -proc-macro2 = "1.0" -quote = "1.0" -syn = { version = "1.0.60", features = ["full"] } -anyhow = "1.0.32" -anchor-syn = { path = "../../syn", version = "0.24.2" } diff --git a/lang/attribute/state/src/lib.rs b/lang/attribute/state/src/lib.rs deleted file mode 100644 index 05807c49..00000000 --- a/lang/attribute/state/src/lib.rs +++ /dev/null @@ -1,92 +0,0 @@ -extern crate proc_macro; - -use quote::quote; -use syn::parse_macro_input; - -/// The `#[state]` attribute defines the program's state struct, i.e., the -/// program's global account singleton giving the program the illusion of state. -/// -/// To allocate space into the account on initialization, pass in the account -/// size into the macro, e.g., `#[state(SIZE)]`. Otherwise, the size of the -/// account returned by the struct's `new` constructor will determine the -/// account size. When determining a size, make sure to reserve enough space -/// for the 8 byte account discriminator prepended to the account. That is, -/// always use 8 extra bytes. -/// -/// # Zero Copy Deserialization -/// -/// Similar to the `#[account]` attribute one can enable zero copy -/// deserialization by using the `zero_copy` argument: -/// -/// ```ignore -/// #[state(zero_copy)] -/// ``` -/// -/// For more, see the [`account`](./attr.account.html) attribute. -#[deprecated( - since = "0.14.0", - note = "#[state] will be removed in a future version. Use a PDA with static seeds instead" -)] -#[proc_macro_attribute] -pub fn state( - args: proc_macro::TokenStream, - input: proc_macro::TokenStream, -) -> proc_macro::TokenStream { - let item_struct = parse_macro_input!(input as syn::ItemStruct); - let struct_ident = &item_struct.ident; - let is_zero_copy = args.to_string() == "zero_copy"; - - let size_override = { - if args.is_empty() { - // No size override given. The account size is whatever is given - // as the initialized value. Use the default implementation. - quote! { - impl anchor_lang::__private::AccountSize for #struct_ident { - fn size(&self) -> anchor_lang::Result { - Ok(8 + self - .try_to_vec() - .map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotSerialize)? - .len() as u64) - } - } - } - } else if is_zero_copy { - quote! { - impl anchor_lang::__private::AccountSize for #struct_ident { - fn size(&self) -> anchor_lang::Result { - let len = anchor_lang::__private::bytemuck::bytes_of(self).len() as u64; - Ok(8 + len) - } - } - } - } else { - let size = proc_macro2::TokenStream::from(args); - // Size override given to the macro. Use it. - quote! { - impl anchor_lang::__private::AccountSize for #struct_ident { - fn size(&self) -> anchor_lang::Result { - Ok(#size) - } - } - } - } - }; - - let attribute = match is_zero_copy { - false => quote! { - #[cfg_attr(feature = "anchor-deprecated-state", account)] - #[cfg_attr(not(feature = "anchor-deprecated-state"), account("state"))] - }, - true => quote! { - #[cfg_attr(feature = "anchor-deprecated-state", account(zero_copy))] - #[cfg_attr(not(feature = "anchor-deprecated-state"), account("state", zero_copy))] - }, - }; - - proc_macro::TokenStream::from(quote! { - #attribute - #item_struct - - #size_override - }) -} diff --git a/lang/derive/accounts/Cargo.toml b/lang/derive/accounts/Cargo.toml deleted file mode 100644 index 2cb6aa65..00000000 --- a/lang/derive/accounts/Cargo.toml +++ /dev/null @@ -1,24 +0,0 @@ -[package] -name = "anchor-derive-accounts" -version = "0.24.2" -authors = ["Serum Foundation "] -repository = "https://github.com/project-serum/anchor" -license = "Apache-2.0" -description = "Anchor Derive macro for accounts" -rust-version = "1.56" -edition = "2021" - -[lib] -proc-macro = true - -[features] -init-if-needed = ["anchor-syn/init-if-needed"] -default = [] -anchor-debug = ["anchor-syn/anchor-debug"] - -[dependencies] -proc-macro2 = "1.0" -quote = "1.0" -syn = { version = "1.0.60", features = ["full"] } -anyhow = "1.0.32" -anchor-syn = { path = "../../syn", version = "0.24.2" } diff --git a/lang/derive/accounts/src/lib.rs b/lang/derive/accounts/src/lib.rs deleted file mode 100644 index f098ce01..00000000 --- a/lang/derive/accounts/src/lib.rs +++ /dev/null @@ -1,548 +0,0 @@ -extern crate proc_macro; - -use proc_macro::TokenStream; -use quote::ToTokens; -use syn::parse_macro_input; - -/// Implements an [`Accounts`](./trait.Accounts.html) deserializer on the given -/// struct. Can provide further functionality through the use of attributes. -/// -/// # Table of Contents -/// - [Instruction Attribute](#instruction-attribute) -/// - [Constraints](#constraints) -/// -/// # Instruction Attribute -/// -/// You can access the instruction's arguments with the -/// `#[instruction(..)]` attribute. You have to list them -/// in the same order as in the instruction but you can -/// omit all arguments after the last one you need. -/// -/// # Example -/// -/// ```ignore -/// ... -/// pub fn initialize(ctx: Context, bump: u8, authority: Pubkey, data: u64) -> anchor_lang::Result<()> { -/// ... -/// Ok(()) -/// } -/// ... -/// #[derive(Accounts)] -/// #[instruction(bump: u8)] -/// pub struct Initialize<'info> { -/// ... -/// } -/// ``` -/// -/// # Constraints -/// -/// There are different types of constraints that can be applied with the `#[account(..)]` attribute. -/// -/// Attributes may reference other data structures. When `` is used in the tables below, an arbitrary expression -/// may be passed in as long as it evaluates to a value of the expected type, e.g. `owner = token_program.key()`. If `target_account` -/// used, the `target_account` must exist in the struct and the `.key()` is implicit, e.g. `payer = authority`. -/// -/// - [Normal Constraints](#normal-constraints) -/// - [SPL Constraints](#spl-constraints) -/// # Normal Constraints -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -///
AttributeDescription
-/// #[account(signer)]

#[account(signer @ <custom_error>)] -///
-/// Checks the given account signed the transaction.
-/// Custom errors are supported via @.
-/// Consider using the Signer type if you would only have this constraint on the account.

-/// Example: -///

-/// #[account(signer)]
-/// pub authority: AccountInfo<'info>,
-/// #[account(signer @ MyError::MyErrorCode)]
-/// pub payer: AccountInfo<'info>
-///                 
-///
-/// #[account(mut)]

#[account(mut @ <custom_error>)] -///
-/// Checks the given account is mutable.
-/// Makes anchor persist any state changes.
-/// Custom errors are supported via @.

-/// Example: -///

-/// #[account(mut)]
-/// pub data_account: Account<'info, MyData>,
-/// #[account(mut @ MyError::MyErrorCode)]
-/// pub data_account_two: Account<'info, MyData>
-///                 
-///
-/// #[account(init, payer = <target_account>, space = <num_bytes>)] -/// -/// Creates the account via a CPI to the system program and -/// initializes it (sets its account discriminator).
-/// Marks the account as mutable and is mutually exclusive with mut.
-/// Makes the account rent exempt unless skipped with rent_exempt = skip.

-/// Use #[account(zero)] for accounts larger than 10 Kibibyte.

-/// init has to be used with additional constraints: -///
    -///
  • -/// Requires the payer constraint to also be on the account. -/// The payer account pays for the -/// account creation. -///
  • -///
  • -/// Requires the system program to exist on the struct -/// and be called system_program. -///
  • -///
  • -/// Requires that the space constraint is specified. -/// When using the space constraint, one must remember to add 8 to it -/// which is the size of the account discriminator. This only has to be done -/// for accounts owned by anchor programs.
    -/// The given space number is the size of the account in bytes, so accounts that hold -/// a variable number of items such as a Vec should allocate sufficient space for all items that may -/// be added to the data structure because account size is fixed. -/// Check out the space reference -/// and the borsh library -/// (which anchor uses under the hood for serialization) specification to learn how much -/// space different data structures require. -///
  • -///
    -/// Example: -///
    -/// #[account]
    -/// pub struct MyData {
    -///     pub data: u64
    -/// }
    
    -/// #[derive(Accounts)]
    -/// pub struct Initialize<'info> {
    -///     #[account(init, payer = payer, space = 8 + 8)]
    -///     pub data_account_two: Account<'info, MyData>,
    -///     #[account(mut)]
    -///     pub payer: Signer<'info>,
    -///     pub system_program: Program<'info, System>,
    -/// }
    -///                 
    -///
-/// init can be combined with other constraints (at the same time): -///
    -///
  • -/// By default init sets the owner field of the created account to the -/// currently executing program. Add the owner constraint to specify a -/// different program owner. -///
  • -///
  • -/// Use the seeds constraint together with bumpto create PDAs.
    -/// init uses find_program_address to calculate the pda so the -/// bump value can be left empty.
    -/// However, if you want to use the bump in your instruction, -/// you can pass it in as instruction data and set the bump value like shown in the example, -/// using the instruction_data attribute. -/// Anchor will then check that the bump returned by find_program_address equals -/// the bump in the instruction data.
    -/// seeds::program cannot be used together with init because the creation of an -/// account requires its signature which for PDAs only the currently executing program can provide. -///
  • -///
-/// Example: -///
-/// #[derive(Accounts)]
-/// #[instruction(bump: u8)]
-/// pub struct Initialize<'info> {
-///     #[account(
-///         init, payer = payer, space = 8 + 8
-///         seeds = [b"example_seed"], bump = bump
-///     )]
-///     pub pda_data_account: Account<'info, MyData>,
-///     #[account(
-///         init, payer = payer,
-///         space = 8 + 8, owner = other_program.key()
-///     )]
-///     pub account_for_other_program: AccountInfo<'info>,
-///     #[account(
-///         init, payer = payer, space = 8 + 8,
-///         owner = other_program.key(),
-///         seeds = [b"other_seed"], bump
-///     )]
-///     pub pda_for_other_program: AccountInfo<'info>,
-///     #[account(mut)]
-///     pub payer: Signer<'info>,
-///     pub system_program: Program<'info, System>,
-///     pub other_program: Program<'info, OtherProgram>
-/// }
-///                 
-///
-/// #[account(init_if_needed, payer = <target_account>)]

-/// #[account(init_if_needed, payer = <target_account>, space = <num_bytes>)] -///
-/// Exact same functionality as the init constraint but only runs if the account does not exist yet.
-/// If the account does exist, it still checks whether the given init constraints are correct, -/// e.g. that the account has the expected amount of space and, if it's a PDA, the correct seeds etc.

-/// This feature should be used with care and is therefore behind a feature flag. -/// You can enable it by importing anchor-lang with the init-if-needed cargo feature.
-/// When using init_if_needed, you need to make sure you properly protect yourself -/// against re-initialization attacks. You need to include checks in your code that check -/// that the initialized account cannot be reset to its initial settings after the first time it was -/// initialized (unless that it what you want).
-/// Because of the possibility of re-initialization attacks and the general guideline that instructions -/// should avoid having multiple execution flows (which is important so they remain easy to understand), -/// consider breaking up your instruction into two instructions - one for initializing and one for using -/// the account - unless you have a good reason not to do so. -///

-/// Example: -///
-/// #[account]
-/// #[derive(Default)]
-/// pub struct MyData {
-///     pub data: u64
-/// }

-/// #[account]
-/// pub struct OtherData {
-///     pub data: u64
-/// }

-/// #[derive(Accounts)]
-/// pub struct Initialize<'info> {
-///     #[account(init_if_needed, payer = payer)]
-///     pub data_account: Account<'info, MyData>,
-///     #[account(init_if_needed, payer = payer, space = 8 + 8)]
-///     pub data_account_two: Account<'info, OtherData>,
-///     #[account(mut)]
-///     pub payer: Signer<'info>,
-///     pub system_program: Program<'info, System>
-/// }
-///                 
-///
-/// #[account(seeds = <seeds>, bump)]

-/// #[account(seeds = <seeds>, bump, seeds::program = <expr>)]

-/// #[account(seeds = <seeds>, bump = <expr>)]

-/// #[account(seeds = <seeds>, bump = <expr>, seeds::program = <expr>)]

-///
-/// Checks that given account is a PDA derived from the currently executing program, -/// the seeds, and if provided, the bump. If not provided, anchor uses the canonical -/// bump.
-/// Add seeds::program = <expr> to derive the PDA from a different -/// program than the currently executing one.
-/// This constraint behaves slightly differently when used with init. -/// See its description. -///

-/// Example: -///

-/// #[derive(Accounts)]
-/// #[instruction(first_bump: u8, second_bump: u8)]
-/// pub struct Example {
-///     #[account(seeds = [b"example_seed"], bump)]
-///     pub canonical_pda: AccountInfo<'info>,
-///     #[account(
-///         seeds = [b"example_seed"],
-///         bump,
-///         seeds::program = other_program.key()
-///     )]
-///     pub canonical_pda_two: AccountInfo<'info>,
-///     #[account(seeds = [b"other_seed"], bump = first_bump)]
-///     pub arbitrary_pda: AccountInfo<'info>
-///     #[account(
-///         seeds = [b"other_seed"],
-///         bump = second_bump,
-///         seeds::program = other_program.key()
-///     )]
-///     pub arbitrary_pda_two: AccountInfo<'info>,
-///     pub other_program: Program<'info, OtherProgram>
-/// }
-///                 
-///
-/// #[account(has_one = <target_account>)]

-/// #[account(has_one = <target_account> @ <custom_error>)] -///
-/// Checks the target_account field on the account matches the -/// key of the target_account field in the Accounts struct.
-/// Custom errors are supported via @.

-/// Example: -///

-/// #[account(mut, has_one = authority)]
-/// pub data: Account<'info, MyData>,
-/// pub authority: Signer<'info>
-///                 
-/// In this example has_one checks that data.authority = authority.key() -///
-/// #[account(address = <expr>)]

-/// #[account(address = <expr> @ <custom_error>)] -///
-/// Checks the account key matches the pubkey.
-/// Custom errors are supported via @.

-/// Example: -///

-/// #[account(address = crate::ID)]
-/// pub data: Account<'info, MyData>,
-/// #[account(address = crate::ID @ MyError::MyErrorCode)]
-/// pub data_two: Account<'info, MyData>
-///                 
-///
-/// #[account(owner = <expr>)]

-/// #[account(owner = <expr> @ <custom_error>)] -///
-/// Checks the account owner matches expr.
-/// Custom errors are supported via @.

-/// Example: -///

-/// #[account(owner = Token::ID @ MyError::MyErrorCode)]
-/// pub data: Account<'info, MyData>,
-/// #[account(owner = token_program.key())]
-/// pub data_two: Account<'info, MyData>,
-/// pub token_program: Program<'info, Token>
-///                 
-///
-/// #[account(executable)] -/// -/// Checks the account is executable (i.e. the account is a program).
-/// You may want to use the Program type instead.

-/// Example: -///

-/// #[account(executable)]
-/// pub my_program: AccountInfo<'info>
-///                 
-///
-/// #[account(rent_exempt = skip)]

-/// #[account(rent_exempt = enforce)] -///
-/// Enforces rent exemption with = enforce.
-/// Skips rent exemption check that would normally be done -/// through other constraints with = skip, -/// e.g. when used with the zero constraint

-/// Example: -///

-/// #[account(zero, rent_exempt = skip)]
-/// pub skipped_account: Account<'info, MyData>,
-/// #[account(rent_exempt = enforce)]
-/// pub enforced_account: AccountInfo<'info>
-///                 
-///
-/// #[account(zero)] -/// -/// Checks the account discriminator is zero.
-/// Enforces rent exemption unless skipped with rent_exempt = skip.

-/// Use this constraint if you want to create an account in a previous instruction -/// and then initialize it in your instruction instead of using init. -/// This is necessary for accounts that are larger than 10 Kibibyte because those -/// accounts cannot be created via a CPI (which is what init would do).

-/// Anchor adds internal data to the account when using zero just like it -/// does with init which is why zero implies mut. -///

-/// Example: -///

-/// #[account(zero)]
-/// pub my_account: Account<'info, MyData>
-///                 
-///
-/// #[account(close = <target_account>)] -/// -/// Marks the account as closed at the end of the instruction’s execution -/// (sets its discriminator to the CLOSED_ACCOUNT_DISCRIMINATOR) -/// and sends its lamports to the specified account.
-/// Setting the discriminator to a special variant -/// makes account revival attacks (where a subsequent instruction -/// adds the rent exemption lamports again) impossible.
-/// Requires mut to exist on the account. -///

-/// Example: -///

-/// #[account(mut, close = receiver)]
-/// pub data_account: Account<'info, MyData>,
-/// #[account(mut)]
-/// pub receiver: SystemAccount<'info>
-///                 
-///
-/// #[account(constraint = <expr>)]

#[account(constraint = <expr> @ <custom_error>)] -///
-/// Constraint that checks whether the given expression evaluates to true.
-/// Use this when no other constraint fits your use case. -///

-/// Example: -///

-/// #[account(constraint = one.keys[0].age == two.apple.age)]
-/// pub one: Account<'info, MyData>,
-/// pub two: Account<'info, OtherData>
-///                 
-///
-/// -/// # SPL Constraints -/// -/// Anchor provides constraints that make verifying SPL accounts easier. -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -/// -///
AttributeDescription
-/// #[account(token::mint = <target_account>, token::authority = <target_account>)] -/// -/// Can be used as a check or with init to create a token -/// account with the given mint address and authority.
-/// When used as a check, it's possible to only specify a subset of the constraints. -///

-/// Example: -///
-/// use anchor_spl::{mint, token::{TokenAccount, Mint, Token}};
-/// ...

-/// #[account(
-///     init,
-///     payer = payer,
-///     token::mint = mint,
-///     token::authority = payer,
-/// )]
-/// pub token: Account<'info, TokenAccount>,
-/// #[account(address = mint::USDC)]
-/// pub mint: Account<'info, Mint>,
-/// #[account(mut)]
-/// pub payer: Signer<'info>,
-/// pub token_program: Program<'info, Token>,
-/// pub system_program: Program<'info, System>
-///                 
-///
-/// #[account(mint::authority = <target_account>, mint::decimals = <expr>)] -///

-/// #[account(mint::authority = <target_account>, mint::decimals = <expr>, mint::freeze_authority = <target_account>)] -///
-/// Can be used as a check or with init to create a mint -/// account with the given mint decimals and mint authority.
-/// The freeze authority is optional when used with init.
-/// When used as a check, it's possible to only specify a subset of the constraints. -///

-/// Example: -///
-/// use anchor_spl::token::{Mint, Token};
-/// ...

-/// #[account(
-///     init,
-///     payer = payer,
-///     mint::decimals = 9,
-///     mint::authority = payer,
-/// )]
-/// pub mint_one: Account<'info, Mint>,
-/// #[account(
-///     init,
-///     payer = payer,
-///     mint::decimals = 9,
-///     mint::authority = payer,
-///     mint::freeze_authority = payer
-/// )]
-/// pub mint_two: Account<'info, Mint>,
-/// #[account(mut)]
-/// pub payer: Signer<'info>,
-/// pub token_program: Program<'info, Token>,
-/// pub system_program: Program<'info, System>
-///                 
-///
-/// #[account(associated_token::mint = <target_account>, associated_token::authority = <target_account>)] -/// -/// Can be used as a standalone as a check or with init to create an associated token -/// account with the given mint address and authority. -///

-/// Example: -///
-/// use anchor_spl::{
-///     associated_token::AssociatedToken,
-///     mint,
-///     token::{TokenAccount, Mint, Token}
-/// };
-/// ...

-/// #[account(
-///     init,
-///     payer = payer,
-///     associated_token::mint = mint,
-///     associated_token::authority = payer,
-/// )]
-/// pub token: Account<'info, TokenAccount>,
-/// #[account(
-///     associated_token::mint = mint,
-///     associated_token::authority = payer,
-/// )]
-/// pub second_token: Account<'info, TokenAccount>,
-/// #[account(address = mint::USDC)]
-/// pub mint: Account<'info, Mint>,
-/// #[account(mut)]
-/// pub payer: Signer<'info>,
-/// pub token_program: Program<'info, Token>,
-/// pub associated_token_program: Program<'info, AssociatedToken>,
-/// pub system_program: Program<'info, System>
-///                 
-///
-#[proc_macro_derive(Accounts, attributes(account, instruction))] -pub fn derive_anchor_deserialize(item: TokenStream) -> TokenStream { - parse_macro_input!(item as anchor_syn::AccountsStruct) - .to_token_stream() - .into() -} diff --git a/lang/src/account_meta.rs b/lang/src/account_meta.rs deleted file mode 100644 index 60bb5287..00000000 --- a/lang/src/account_meta.rs +++ /dev/null @@ -1,8 +0,0 @@ -use crate::ToAccountMetas; -use solana_program::instruction::AccountMeta; - -impl ToAccountMetas for AccountMeta { - fn to_account_metas(&self, _is_signer: Option) -> Vec { - vec![self.clone()] - } -} diff --git a/lang/src/accounts/account.rs b/lang/src/accounts/account.rs deleted file mode 100644 index f8306ac0..00000000 --- a/lang/src/accounts/account.rs +++ /dev/null @@ -1,425 +0,0 @@ -//! Account container that checks ownership on deserialization. - -use crate::bpf_writer::BpfWriter; -use crate::error::{Error, ErrorCode}; -use crate::{ - AccountDeserialize, AccountSerialize, Accounts, AccountsClose, AccountsExit, Key, Owner, - Result, ToAccountInfo, ToAccountInfos, ToAccountMetas, -}; -use solana_program::account_info::AccountInfo; -use solana_program::instruction::AccountMeta; -use solana_program::pubkey::Pubkey; -use solana_program::system_program; -use std::collections::BTreeMap; -use std::fmt; -use std::ops::{Deref, DerefMut}; - -/// Wrapper around [`AccountInfo`](crate::solana_program::account_info::AccountInfo) -/// that verifies program ownership and deserializes underlying data into a Rust type. -/// -/// # Table of Contents -/// - [Basic Functionality](#basic-functionality) -/// - [Using Account with non-anchor types](#using-account-with-non-anchor-types) -/// - [Out of the box wrapper types](#out-of-the-box-wrapper-types) -/// -/// # Basic Functionality -/// -/// Account checks that `Account.info.owner == T::owner()`. -/// This means that the data type that Accounts wraps around (`=T`) needs to -/// implement the [Owner trait](crate::Owner). -/// The `#[account]` attribute implements the Owner trait for -/// a struct using the `crate::ID` declared by [`declareId`](crate::declare_id) -/// in the same program. It follows that Account can also be used -/// with a `T` that comes from a different program. -/// -/// Checks: -/// -/// - `Account.info.owner == T::owner()` -/// - `!(Account.info.owner == SystemProgram && Account.info.lamports() == 0)` -/// -/// # Example -/// ```ignore -/// use anchor_lang::prelude::*; -/// use other_program::Auth; -/// -/// declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); -/// -/// #[program] -/// mod hello_anchor { -/// use super::*; -/// pub fn set_data(ctx: Context, data: u64) -> Result<()> { -/// if (*ctx.accounts.auth_account).authorized { -/// (*ctx.accounts.my_account).data = data; -/// } -/// Ok(()) -/// } -/// } -/// -/// #[account] -/// #[derive(Default)] -/// pub struct MyData { -/// pub data: u64 -/// } -/// -/// #[derive(Accounts)] -/// pub struct SetData<'info> { -/// #[account(mut)] -/// pub my_account: Account<'info, MyData> // checks that my_account.info.owner == Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS -/// pub auth_account: Account<'info, Auth> // checks that auth_account.info.owner == FEZGUxNhZWpYPj9MJCrZJvUo1iF9ys34UHx52y4SzVW9 -/// } -/// -/// // In a different program -/// -/// ... -/// declare_id!("FEZGUxNhZWpYPj9MJCrZJvUo1iF9ys34UHx52y4SzVW9"); -/// #[account] -/// #[derive(Default)] -/// pub struct Auth { -/// pub authorized: bool -/// } -/// ... -/// ``` -/// -/// # Using Account with non-anchor programs -/// -/// Account can also be used with non-anchor programs. The data types from -/// those programs are not annotated with `#[account]` so you have to -/// - create a wrapper type around the structs you want to wrap with Account -/// - implement the functions required by Account yourself -/// instead of using `#[account]`. You only have to implement a fraction of the -/// functions `#[account]` generates. See the example below for the code you have -/// to write. -/// -/// The mint wrapper type that Anchor provides out of the box for the token program ([source](https://github.com/project-serum/anchor/blob/master/spl/src/token.rs)) -/// ```ignore -/// #[derive(Clone)] -/// pub struct Mint(spl_token::state::Mint); -/// -/// // This is necessary so we can use "anchor_spl::token::Mint::LEN" -/// // because rust does not resolve "anchor_spl::token::Mint::LEN" to -/// // "spl_token::state::Mint::LEN" automatically -/// impl Mint { -/// pub const LEN: usize = spl_token::state::Mint::LEN; -/// } -/// -/// // You don't have to implement the "try_deserialize" function -/// // from this trait. It delegates to -/// // "try_deserialize_unchecked" by default which is what we want here -/// // because non-anchor accounts don't have a discriminator to check -/// impl anchor_lang::AccountDeserialize for Mint { -/// fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result { -/// spl_token::state::Mint::unpack(buf).map(Mint) -/// } -/// } -/// // AccountSerialize defaults to a no-op which is what we want here -/// // because it's a foreign program, so our program does not -/// // have permission to write to the foreign program's accounts anyway -/// impl anchor_lang::AccountSerialize for Mint {} -/// -/// impl anchor_lang::Owner for Mint { -/// fn owner() -> Pubkey { -/// // pub use spl_token::ID is used at the top of the file -/// ID -/// } -/// } -/// -/// // Implement the "std::ops::Deref" trait for better user experience -/// impl Deref for Mint { -/// type Target = spl_token::state::Mint; -/// -/// fn deref(&self) -> &Self::Target { -/// &self.0 -/// } -/// } -/// ``` -/// -/// ## Out of the box wrapper types -/// -/// ### Accessing BPFUpgradeableLoader Data -/// -/// Anchor provides wrapper types to access data stored in programs owned by the BPFUpgradeableLoader -/// such as the upgrade authority. If you're interested in the data of a program account, you can use -/// ```ignore -/// Account<'info, BpfUpgradeableLoaderState> -/// ``` -/// and then match on its contents inside your instruction function. -/// -/// Alternatively, you can use -/// ```ignore -/// Account<'info, ProgramData> -/// ``` -/// to let anchor do the matching for you and return the ProgramData variant of BpfUpgradeableLoaderState. -/// -/// # Example -/// ```ignore -/// use anchor_lang::prelude::*; -/// use crate::program::MyProgram; -/// -/// declare_id!("Cum9tTyj5HwcEiAmhgaS7Bbj4UczCwsucrCkxRECzM4e"); -/// -/// #[program] -/// pub mod my_program { -/// use super::*; -/// -/// pub fn set_initial_admin( -/// ctx: Context, -/// admin_key: Pubkey -/// ) -> Result<()> { -/// ctx.accounts.admin_settings.admin_key = admin_key; -/// Ok(()) -/// } -/// -/// pub fn set_admin(...){...} -/// -/// pub fn set_settings(...){...} -/// } -/// -/// #[account] -/// #[derive(Default, Debug)] -/// pub struct AdminSettings { -/// admin_key: Pubkey -/// } -/// -/// #[derive(Accounts)] -/// pub struct SetInitialAdmin<'info> { -/// #[account(init, payer = authority, seeds = [b"admin"], bump)] -/// pub admin_settings: Account<'info, AdminSettings>, -/// #[account(mut)] -/// pub authority: Signer<'info>, -/// #[account(constraint = program.programdata_address()? == Some(program_data.key()))] -/// pub program: Program<'info, MyProgram>, -/// #[account(constraint = program_data.upgrade_authority_address == Some(authority.key()))] -/// pub program_data: Account<'info, ProgramData>, -/// pub system_program: Program<'info, System>, -/// } -/// ``` -/// -/// This example solves a problem you may face if your program has admin settings: How do you set the -/// admin key for restricted functionality after deployment? Setting the admin key itself should -/// be a restricted action but how do you restrict it without having set an admin key? -/// You're stuck in a loop. -/// One solution is to use the upgrade authority of the program as the initial -/// (or permanent) admin key. -/// -/// ### SPL Types -/// -/// Anchor provides wrapper types to access accounts owned by the token program. Use -/// ```ignore -/// use anchor_spl::token::TokenAccount; -/// -/// #[derive(Accounts)] -/// pub struct Example { -/// pub my_acc: Account<'info, TokenAccount> -/// } -/// ``` -/// to access token accounts and -/// ```ignore -/// use anchor_spl::token::Mint; -/// -/// #[derive(Accounts)] -/// pub struct Example { -/// pub my_acc: Account<'info, Mint> -/// } -/// ``` -/// to access mint accounts. -#[derive(Clone)] -pub struct Account<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> { - account: T, - info: AccountInfo<'info>, -} - -impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone + fmt::Debug> fmt::Debug - for Account<'info, T> -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Account") - .field("account", &self.account) - .field("info", &self.info) - .finish() - } -} - -impl<'a, T: AccountSerialize + AccountDeserialize + crate::Owner + Clone> Account<'a, T> { - fn new(info: AccountInfo<'a>, account: T) -> Account<'a, T> { - Self { info, account } - } - - /// Deserializes the given `info` into a `Account`. - #[inline(never)] - pub fn try_from(info: &AccountInfo<'a>) -> Result> { - if info.owner == &system_program::ID && info.lamports() == 0 { - return Err(ErrorCode::AccountNotInitialized.into()); - } - if info.owner != &T::owner() { - return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram) - .with_pubkeys((*info.owner, T::owner()))); - } - let mut data: &[u8] = &info.try_borrow_data()?; - Ok(Account::new(info.clone(), T::try_deserialize(&mut data)?)) - } - - /// Deserializes the given `info` into a `Account` without checking - /// the account discriminator. Be careful when using this and avoid it if - /// possible. - #[inline(never)] - pub fn try_from_unchecked(info: &AccountInfo<'a>) -> Result> { - if info.owner == &system_program::ID && info.lamports() == 0 { - return Err(ErrorCode::AccountNotInitialized.into()); - } - if info.owner != &T::owner() { - return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram) - .with_pubkeys((*info.owner, T::owner()))); - } - let mut data: &[u8] = &info.try_borrow_data()?; - Ok(Account::new( - info.clone(), - T::try_deserialize_unchecked(&mut data)?, - )) - } - - /// Reloads the account from storage. This is useful, for example, when - /// observing side effects after CPI. - pub fn reload(&mut self) -> Result<()> { - let mut data: &[u8] = &self.info.try_borrow_data()?; - self.account = T::try_deserialize(&mut data)?; - Ok(()) - } - - pub fn into_inner(self) -> T { - self.account - } - - /// Sets the inner account. - /// - /// Instead of this: - /// ```ignore - /// pub fn new_user(ctx: Context, new_user:User) -> Result<()> { - /// (*ctx.accounts.user_to_create).name = new_user.name; - /// (*ctx.accounts.user_to_create).age = new_user.age; - /// (*ctx.accounts.user_to_create).address = new_user.address; - /// } - /// ``` - /// You can do this: - /// ```ignore - /// pub fn new_user(ctx: Context, new_user:User) -> Result<()> { - /// ctx.accounts.user_to_create.set_inner(new_user); - /// } - /// ``` - pub fn set_inner(&mut self, inner: T) { - self.account = inner; - } -} - -impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> Accounts<'info> - for Account<'info, T> -where - T: AccountSerialize + AccountDeserialize + Owner + Clone, -{ - #[inline(never)] - fn try_accounts( - _program_id: &Pubkey, - accounts: &mut &[AccountInfo<'info>], - _ix_data: &[u8], - _bumps: &mut BTreeMap, - ) -> Result { - if accounts.is_empty() { - return Err(ErrorCode::AccountNotEnoughKeys.into()); - } - let account = &accounts[0]; - *accounts = &accounts[1..]; - Account::try_from(account) - } -} - -impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AccountsExit<'info> - for Account<'info, T> -{ - fn exit(&self, program_id: &Pubkey) -> Result<()> { - // Only persist if the owner is the current program. - if &T::owner() == program_id { - let info = self.to_account_info(); - let mut data = info.try_borrow_mut_data()?; - let dst: &mut [u8] = &mut data; - let mut writer = BpfWriter::new(dst); - self.account.try_serialize(&mut writer)?; - } - Ok(()) - } -} - -/// This function is for INTERNAL USE ONLY. -/// Do NOT use this function in a program. -/// Manual closing of `Account<'info, T>` types is NOT supported. -/// -/// Details: Using `close` with `Account<'info, T>` is not safe because -/// it requires the `mut` constraint but for that type the constraint -/// overwrites the "closed account" discriminator at the end of the instruction. -impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AccountsClose<'info> - for Account<'info, T> -{ - fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> { - crate::common::close(self.to_account_info(), sol_destination) - } -} - -impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> ToAccountMetas - for Account<'info, T> -{ - fn to_account_metas(&self, is_signer: Option) -> Vec { - let is_signer = is_signer.unwrap_or(self.info.is_signer); - let meta = match self.info.is_writable { - false => AccountMeta::new_readonly(*self.info.key, is_signer), - true => AccountMeta::new(*self.info.key, is_signer), - }; - vec![meta] - } -} - -impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> ToAccountInfos<'info> - for Account<'info, T> -{ - fn to_account_infos(&self) -> Vec> { - vec![self.info.clone()] - } -} - -impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AsRef> - for Account<'info, T> -{ - fn as_ref(&self) -> &AccountInfo<'info> { - &self.info - } -} - -impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AsRef - for Account<'info, T> -{ - fn as_ref(&self) -> &T { - &self.account - } -} - -impl<'a, T: AccountSerialize + AccountDeserialize + Owner + Clone> Deref for Account<'a, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - &(*self).account - } -} - -impl<'a, T: AccountSerialize + AccountDeserialize + Owner + Clone> DerefMut for Account<'a, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - #[cfg(feature = "anchor-debug")] - if !self.info.is_writable { - solana_program::msg!("The given Account is not mutable"); - panic!(); - } - &mut self.account - } -} - -impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> Key for Account<'info, T> { - fn key(&self) -> Pubkey { - *self.info.key - } -} diff --git a/lang/src/accounts/account_info.rs b/lang/src/accounts/account_info.rs deleted file mode 100644 index de23283a..00000000 --- a/lang/src/accounts/account_info.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! AccountInfo can be used as a type but -//! [Unchecked Account](crate::accounts::unchecked_account::UncheckedAccount) -//! should be used instead. - -use crate::error::ErrorCode; -use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas}; -use solana_program::account_info::AccountInfo; -use solana_program::instruction::AccountMeta; -use solana_program::pubkey::Pubkey; -use std::collections::BTreeMap; - -impl<'info> Accounts<'info> for AccountInfo<'info> { - fn try_accounts( - _program_id: &Pubkey, - accounts: &mut &[AccountInfo<'info>], - _ix_data: &[u8], - _bumps: &mut BTreeMap, - ) -> Result { - if accounts.is_empty() { - return Err(ErrorCode::AccountNotEnoughKeys.into()); - } - let account = &accounts[0]; - *accounts = &accounts[1..]; - Ok(account.clone()) - } -} - -impl<'info> ToAccountMetas for AccountInfo<'info> { - fn to_account_metas(&self, is_signer: Option) -> Vec { - let is_signer = is_signer.unwrap_or(self.is_signer); - let meta = match self.is_writable { - false => AccountMeta::new_readonly(*self.key, is_signer), - true => AccountMeta::new(*self.key, is_signer), - }; - vec![meta] - } -} - -impl<'info> ToAccountInfos<'info> for AccountInfo<'info> { - fn to_account_infos(&self) -> Vec> { - vec![self.clone()] - } -} - -impl<'info> AccountsExit<'info> for AccountInfo<'info> {} - -impl<'info> Key for AccountInfo<'info> { - fn key(&self) -> Pubkey { - *self.key - } -} diff --git a/lang/src/accounts/account_loader.rs b/lang/src/accounts/account_loader.rs deleted file mode 100644 index 0432eb4c..00000000 --- a/lang/src/accounts/account_loader.rs +++ /dev/null @@ -1,286 +0,0 @@ -//! Type facilitating on demand zero copy deserialization. - -use crate::bpf_writer::BpfWriter; -use crate::error::{Error, ErrorCode}; -use crate::{ - Accounts, AccountsClose, AccountsExit, Key, Owner, Result, ToAccountInfo, ToAccountInfos, - ToAccountMetas, ZeroCopy, -}; -use arrayref::array_ref; -use solana_program::account_info::AccountInfo; -use solana_program::instruction::AccountMeta; -use solana_program::pubkey::Pubkey; -use std::cell::{Ref, RefMut}; -use std::collections::BTreeMap; -use std::fmt; -use std::io::Write; -use std::marker::PhantomData; -use std::mem; -use std::ops::DerefMut; - -/// Type facilitating on demand zero copy deserialization. -/// -/// Note that using accounts in this way is distinctly different from using, -/// for example, the [`Account`](./struct.Account.html). Namely, -/// one must call -/// - `load_init` after initializing an account (this will ignore the missing -/// account discriminator that gets added only after the user's instruction code) -/// - `load` when the account is not mutable -/// - `load_mut` when the account is mutable -/// -/// For more details on zero-copy-deserialization, see the -/// [`account`](./attr.account.html) attribute. -///

-/// ⚠️ When using this type it's important to be mindful -/// of any calls to the load functions so as not to -/// induce a RefCell panic, especially when sharing accounts across CPI -/// boundaries. When in doubt, one should make sure all refs resulting from -/// a call to a load function are dropped before CPI. -/// This can be done explicitly by calling drop(my_var) or implicitly -/// by wrapping the code using the Ref in braces {..} or -/// moving it into its own function. -///

-/// -/// # Example -/// ```ignore -/// use anchor_lang::prelude::*; -/// -/// declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); -/// -/// #[program] -/// pub mod bar { -/// use super::*; -/// -/// pub fn create_bar(ctx: Context, data: u64) -> Result<()> { -/// let bar = &mut ctx.accounts.bar.load_init()?; -/// bar.authority = ctx.accounts.authority.key(); -/// bar.data = data; -/// Ok(()) -/// } -/// -/// pub fn update_bar(ctx: Context, data: u64) -> Result<()> { -/// (*ctx.accounts.bar.load_mut()?).data = data; -/// Ok(()) -/// } -/// } -/// -/// #[account(zero_copy)] -/// #[derive(Default)] -/// pub struct Bar { -/// authority: Pubkey, -/// data: u64 -/// } -/// -/// #[derive(Accounts)] -/// pub struct CreateBar<'info> { -/// #[account( -/// init, -/// payer = authority -/// )] -/// bar: AccountLoader<'info, Bar>, -/// #[account(mut)] -/// authority: Signer<'info>, -/// system_program: AccountInfo<'info>, -/// } -/// -/// #[derive(Accounts)] -/// pub struct UpdateBar<'info> { -/// #[account( -/// mut, -/// has_one = authority, -/// )] -/// pub bar: AccountLoader<'info, Bar>, -/// pub authority: Signer<'info>, -/// } -/// ``` -#[derive(Clone)] -pub struct AccountLoader<'info, T: ZeroCopy + Owner> { - acc_info: AccountInfo<'info>, - phantom: PhantomData<&'info T>, -} - -impl<'info, T: ZeroCopy + Owner + fmt::Debug> fmt::Debug for AccountLoader<'info, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("AccountLoader") - .field("acc_info", &self.acc_info) - .field("phantom", &self.phantom) - .finish() - } -} - -impl<'info, T: ZeroCopy + Owner> AccountLoader<'info, T> { - fn new(acc_info: AccountInfo<'info>) -> AccountLoader<'info, T> { - Self { - acc_info, - phantom: PhantomData, - } - } - - /// Constructs a new `Loader` from a previously initialized account. - #[inline(never)] - pub fn try_from(acc_info: &AccountInfo<'info>) -> Result> { - if acc_info.owner != &T::owner() { - return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram) - .with_pubkeys((*acc_info.owner, T::owner()))); - } - let data: &[u8] = &acc_info.try_borrow_data()?; - if data.len() < T::discriminator().len() { - return Err(ErrorCode::AccountDiscriminatorNotFound.into()); - } - // Discriminator must match. - let disc_bytes = array_ref![data, 0, 8]; - if disc_bytes != &T::discriminator() { - return Err(ErrorCode::AccountDiscriminatorMismatch.into()); - } - - Ok(AccountLoader::new(acc_info.clone())) - } - - /// Constructs a new `Loader` from an uninitialized account. - #[inline(never)] - pub fn try_from_unchecked( - _program_id: &Pubkey, - acc_info: &AccountInfo<'info>, - ) -> Result> { - if acc_info.owner != &T::owner() { - return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram) - .with_pubkeys((*acc_info.owner, T::owner()))); - } - Ok(AccountLoader::new(acc_info.clone())) - } - - /// Returns a Ref to the account data structure for reading. - pub fn load(&self) -> Result> { - let data = self.acc_info.try_borrow_data()?; - if data.len() < T::discriminator().len() { - return Err(ErrorCode::AccountDiscriminatorNotFound.into()); - } - - let disc_bytes = array_ref![data, 0, 8]; - if disc_bytes != &T::discriminator() { - return Err(ErrorCode::AccountDiscriminatorMismatch.into()); - } - - Ok(Ref::map(data, |data| { - bytemuck::from_bytes(&data[8..mem::size_of::() + 8]) - })) - } - - /// Returns a `RefMut` to the account data structure for reading or writing. - pub fn load_mut(&self) -> Result> { - // AccountInfo api allows you to borrow mut even if the account isn't - // writable, so add this check for a better dev experience. - if !self.acc_info.is_writable { - return Err(ErrorCode::AccountNotMutable.into()); - } - - let data = self.acc_info.try_borrow_mut_data()?; - if data.len() < T::discriminator().len() { - return Err(ErrorCode::AccountDiscriminatorNotFound.into()); - } - - let disc_bytes = array_ref![data, 0, 8]; - if disc_bytes != &T::discriminator() { - return Err(ErrorCode::AccountDiscriminatorMismatch.into()); - } - - Ok(RefMut::map(data, |data| { - bytemuck::from_bytes_mut(&mut data.deref_mut()[8..mem::size_of::() + 8]) - })) - } - - /// Returns a `RefMut` to the account data structure for reading or writing. - /// Should only be called once, when the account is being initialized. - pub fn load_init(&self) -> Result> { - // AccountInfo api allows you to borrow mut even if the account isn't - // writable, so add this check for a better dev experience. - if !self.acc_info.is_writable { - return Err(ErrorCode::AccountNotMutable.into()); - } - - let data = self.acc_info.try_borrow_mut_data()?; - - // The discriminator should be zero, since we're initializing. - let mut disc_bytes = [0u8; 8]; - disc_bytes.copy_from_slice(&data[..8]); - let discriminator = u64::from_le_bytes(disc_bytes); - if discriminator != 0 { - return Err(ErrorCode::AccountDiscriminatorAlreadySet.into()); - } - - Ok(RefMut::map(data, |data| { - bytemuck::from_bytes_mut(&mut data.deref_mut()[8..mem::size_of::() + 8]) - })) - } -} - -impl<'info, T: ZeroCopy + Owner> Accounts<'info> for AccountLoader<'info, T> { - #[inline(never)] - fn try_accounts( - _program_id: &Pubkey, - accounts: &mut &[AccountInfo<'info>], - _ix_data: &[u8], - _bumps: &mut BTreeMap, - ) -> Result { - if accounts.is_empty() { - return Err(ErrorCode::AccountNotEnoughKeys.into()); - } - let account = &accounts[0]; - *accounts = &accounts[1..]; - let l = AccountLoader::try_from(account)?; - Ok(l) - } -} - -impl<'info, T: ZeroCopy + Owner> AccountsExit<'info> for AccountLoader<'info, T> { - // The account *cannot* be loaded when this is called. - fn exit(&self, _program_id: &Pubkey) -> Result<()> { - let mut data = self.acc_info.try_borrow_mut_data()?; - let dst: &mut [u8] = &mut data; - let mut writer = BpfWriter::new(dst); - writer.write_all(&T::discriminator()).unwrap(); - Ok(()) - } -} - -/// This function is for INTERNAL USE ONLY. -/// Do NOT use this function in a program. -/// Manual closing of `AccountLoader<'info, T>` types is NOT supported. -/// -/// Details: Using `close` with `AccountLoader<'info, T>` is not safe because -/// it requires the `mut` constraint but for that type the constraint -/// overwrites the "closed account" discriminator at the end of the instruction. -impl<'info, T: ZeroCopy + Owner> AccountsClose<'info> for AccountLoader<'info, T> { - fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> { - crate::common::close(self.to_account_info(), sol_destination) - } -} - -impl<'info, T: ZeroCopy + Owner> ToAccountMetas for AccountLoader<'info, T> { - fn to_account_metas(&self, is_signer: Option) -> Vec { - let is_signer = is_signer.unwrap_or(self.acc_info.is_signer); - let meta = match self.acc_info.is_writable { - false => AccountMeta::new_readonly(*self.acc_info.key, is_signer), - true => AccountMeta::new(*self.acc_info.key, is_signer), - }; - vec![meta] - } -} - -impl<'info, T: ZeroCopy + Owner> AsRef> for AccountLoader<'info, T> { - fn as_ref(&self) -> &AccountInfo<'info> { - &self.acc_info - } -} - -impl<'info, T: ZeroCopy + Owner> ToAccountInfos<'info> for AccountLoader<'info, T> { - fn to_account_infos(&self) -> Vec> { - vec![self.acc_info.clone()] - } -} - -impl<'info, T: ZeroCopy + Owner> Key for AccountLoader<'info, T> { - fn key(&self) -> Pubkey { - *self.acc_info.key - } -} diff --git a/lang/src/accounts/boxed.rs b/lang/src/accounts/boxed.rs deleted file mode 100644 index 83ef8357..00000000 --- a/lang/src/accounts/boxed.rs +++ /dev/null @@ -1,56 +0,0 @@ -//! Box type to save stack space. -//! -//! Sometimes accounts are too large for the stack, -//! leading to stack violations. -//! -//! Boxing the account can help. -//! -//! # Example -//! ```ignore -//! #[derive(Accounts)] -//! pub struct Example { -//! pub my_acc: Box> -//! } -//! ``` - -use crate::{Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, ToAccountMetas}; -use solana_program::account_info::AccountInfo; -use solana_program::instruction::AccountMeta; -use solana_program::pubkey::Pubkey; -use std::collections::BTreeMap; -use std::ops::Deref; - -impl<'info, T: Accounts<'info>> Accounts<'info> for Box { - fn try_accounts( - program_id: &Pubkey, - accounts: &mut &[AccountInfo<'info>], - ix_data: &[u8], - bumps: &mut BTreeMap, - ) -> Result { - T::try_accounts(program_id, accounts, ix_data, bumps).map(Box::new) - } -} - -impl<'info, T: AccountsExit<'info>> AccountsExit<'info> for Box { - fn exit(&self, program_id: &Pubkey) -> Result<()> { - T::exit(Deref::deref(self), program_id) - } -} - -impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Box { - fn to_account_infos(&self) -> Vec> { - T::to_account_infos(self) - } -} - -impl ToAccountMetas for Box { - fn to_account_metas(&self, is_signer: Option) -> Vec { - T::to_account_metas(self, is_signer) - } -} - -impl<'info, T: AccountsClose<'info>> AccountsClose<'info> for Box { - fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> { - T::close(self, sol_destination) - } -} diff --git a/lang/src/accounts/cpi_account.rs b/lang/src/accounts/cpi_account.rs deleted file mode 100644 index 5f956789..00000000 --- a/lang/src/accounts/cpi_account.rs +++ /dev/null @@ -1,128 +0,0 @@ -use crate::*; -use crate::{error::ErrorCode, prelude::Account}; -use solana_program::account_info::AccountInfo; -use solana_program::instruction::AccountMeta; -use solana_program::pubkey::Pubkey; -use std::collections::BTreeMap; -use std::ops::{Deref, DerefMut}; - -/// Container for any account *not* owned by the current program. -#[derive(Clone)] -#[deprecated(since = "0.15.0", note = "Please use Account instead")] -pub struct CpiAccount<'a, T: AccountDeserialize + Clone> { - info: AccountInfo<'a>, - account: Box, -} - -#[allow(deprecated)] -impl<'a, T: AccountDeserialize + Clone> CpiAccount<'a, T> { - fn new(info: AccountInfo<'a>, account: Box) -> CpiAccount<'a, T> { - Self { info, account } - } - - /// Deserializes the given `info` into a `CpiAccount`. - pub fn try_from(info: &AccountInfo<'a>) -> Result> { - let mut data: &[u8] = &info.try_borrow_data()?; - Ok(CpiAccount::new( - info.clone(), - Box::new(T::try_deserialize(&mut data)?), - )) - } - - pub fn try_from_unchecked(info: &AccountInfo<'a>) -> Result> { - Self::try_from(info) - } - - /// Reloads the account from storage. This is useful, for example, when - /// observing side effects after CPI. - pub fn reload(&mut self) -> Result<()> { - let mut data: &[u8] = &self.info.try_borrow_data()?; - self.account = Box::new(T::try_deserialize(&mut data)?); - Ok(()) - } -} - -#[allow(deprecated)] -impl<'info, T> Accounts<'info> for CpiAccount<'info, T> -where - T: AccountDeserialize + Clone, -{ - #[inline(never)] - fn try_accounts( - _program_id: &Pubkey, - accounts: &mut &[AccountInfo<'info>], - _ix_data: &[u8], - _bumps: &mut BTreeMap, - ) -> Result { - if accounts.is_empty() { - return Err(ErrorCode::AccountNotEnoughKeys.into()); - } - let account = &accounts[0]; - *accounts = &accounts[1..]; - // No owner check is done here. - let pa = CpiAccount::try_from(account)?; - Ok(pa) - } -} - -#[allow(deprecated)] -impl<'info, T: AccountDeserialize + Clone> ToAccountMetas for CpiAccount<'info, T> { - fn to_account_metas(&self, is_signer: Option) -> Vec { - let is_signer = is_signer.unwrap_or(self.info.is_signer); - let meta = match self.info.is_writable { - false => AccountMeta::new_readonly(*self.info.key, is_signer), - true => AccountMeta::new(*self.info.key, is_signer), - }; - vec![meta] - } -} - -#[allow(deprecated)] -impl<'info, T: AccountDeserialize + Clone> ToAccountInfos<'info> for CpiAccount<'info, T> { - fn to_account_infos(&self) -> Vec> { - vec![self.info.clone()] - } -} - -#[allow(deprecated)] -impl<'info, T: AccountDeserialize + Clone> AsRef> for CpiAccount<'info, T> { - fn as_ref(&self) -> &AccountInfo<'info> { - &self.info - } -} - -#[allow(deprecated)] -impl<'a, T: AccountDeserialize + Clone> Deref for CpiAccount<'a, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.account - } -} - -#[allow(deprecated)] -impl<'a, T: AccountDeserialize + Clone> DerefMut for CpiAccount<'a, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.account - } -} - -#[allow(deprecated)] -impl<'info, T: AccountDeserialize + Clone> AccountsExit<'info> for CpiAccount<'info, T> {} - -#[allow(deprecated)] -impl<'info, T> From> for CpiAccount<'info, T> -where - T: AccountSerialize + AccountDeserialize + Owner + Clone, -{ - fn from(a: Account<'info, T>) -> Self { - Self::new(a.to_account_info(), Box::new(a.into_inner())) - } -} - -#[allow(deprecated)] -impl<'info, T: AccountDeserialize + Clone> Key for CpiAccount<'info, T> { - fn key(&self) -> Pubkey { - *self.info.key - } -} diff --git a/lang/src/accounts/cpi_state.rs b/lang/src/accounts/cpi_state.rs deleted file mode 100644 index 401dabfa..00000000 --- a/lang/src/accounts/cpi_state.rs +++ /dev/null @@ -1,148 +0,0 @@ -use crate::error::ErrorCode; -#[allow(deprecated)] -use crate::{accounts::state::ProgramState, context::CpiStateContext}; -use crate::{ - AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Key, Result, ToAccountInfos, - ToAccountMetas, -}; -use solana_program::account_info::AccountInfo; -use solana_program::instruction::AccountMeta; -use solana_program::pubkey::Pubkey; -use std::collections::BTreeMap; -use std::ops::{Deref, DerefMut}; - -/// Boxed container for the program state singleton, used when the state -/// is for a program not currently executing. -#[derive(Clone)] -#[deprecated] -pub struct CpiState<'info, T: AccountSerialize + AccountDeserialize + Clone> { - inner: Box>, -} - -#[derive(Clone)] -struct Inner<'info, T: AccountSerialize + AccountDeserialize + Clone> { - info: AccountInfo<'info>, - account: T, -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> CpiState<'info, T> { - pub fn new(i: AccountInfo<'info>, account: T) -> CpiState<'info, T> { - Self { - inner: Box::new(Inner { info: i, account }), - } - } - - /// Deserializes the given `info` into a `CpiState`. - #[inline(never)] - pub fn try_from(info: &AccountInfo<'info>) -> Result> { - let mut data: &[u8] = &info.try_borrow_data()?; - Ok(CpiState::new(info.clone(), T::try_deserialize(&mut data)?)) - } - - fn seed() -> &'static str { - ProgramState::::seed() - } - - pub fn address(program_id: &Pubkey) -> Pubkey { - let (base, _nonce) = Pubkey::find_program_address(&[], program_id); - let seed = Self::seed(); - let owner = program_id; - Pubkey::create_with_seed(&base, seed, owner).unwrap() - } - - /// Convenience api for creating a `CpiStateContext`. - pub fn context<'a, 'b, 'c, A: Accounts<'info>>( - &self, - program: AccountInfo<'info>, - accounts: A, - ) -> CpiStateContext<'a, 'b, 'c, 'info, A> { - CpiStateContext::new(program, self.inner.info.clone(), accounts) - } -} - -#[allow(deprecated)] -impl<'info, T> Accounts<'info> for CpiState<'info, T> -where - T: AccountSerialize + AccountDeserialize + Clone, -{ - #[inline(never)] - fn try_accounts( - _program_id: &Pubkey, - accounts: &mut &[AccountInfo<'info>], - _ix_data: &[u8], - _bumps: &mut BTreeMap, - ) -> Result { - if accounts.is_empty() { - return Err(ErrorCode::AccountNotEnoughKeys.into()); - } - let account = &accounts[0]; - *accounts = &accounts[1..]; - - // No owner or address check is done here. One must use the - // #[account(state = )] constraint. - - CpiState::try_from(account) - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountMetas - for CpiState<'info, T> -{ - fn to_account_metas(&self, is_signer: Option) -> Vec { - let is_signer = is_signer.unwrap_or(self.inner.info.is_signer); - let meta = match self.inner.info.is_writable { - false => AccountMeta::new_readonly(*self.inner.info.key, is_signer), - true => AccountMeta::new(*self.inner.info.key, is_signer), - }; - vec![meta] - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'info> - for CpiState<'info, T> -{ - fn to_account_infos(&self) -> Vec> { - vec![self.inner.info.clone()] - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AsRef> - for CpiState<'info, T> -{ - fn as_ref(&self) -> &AccountInfo<'info> { - &self.inner.info - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> Deref for CpiState<'info, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - &(*self.inner).account - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> DerefMut for CpiState<'info, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut DerefMut::deref_mut(&mut self.inner).account - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AccountsExit<'info> - for CpiState<'info, T> -{ -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> Key for CpiState<'info, T> { - fn key(&self) -> Pubkey { - *self.inner.info.key - } -} diff --git a/lang/src/accounts/loader.rs b/lang/src/accounts/loader.rs deleted file mode 100644 index 516e5a98..00000000 --- a/lang/src/accounts/loader.rs +++ /dev/null @@ -1,234 +0,0 @@ -use crate::bpf_writer::BpfWriter; -use crate::error::{Error, ErrorCode}; -use crate::{ - Accounts, AccountsClose, AccountsExit, Key, Result, ToAccountInfo, ToAccountInfos, - ToAccountMetas, ZeroCopy, -}; -use arrayref::array_ref; -use solana_program::account_info::AccountInfo; -use solana_program::instruction::AccountMeta; -use solana_program::pubkey::Pubkey; -use std::cell::{Ref, RefMut}; -use std::collections::BTreeMap; -use std::fmt; -use std::io::Write; -use std::marker::PhantomData; -use std::ops::DerefMut; - -/// Account loader facilitating on demand zero copy deserialization. -/// Note that using accounts in this way is distinctly different from using, -/// for example, the [`Account`](./struct.Account.html). Namely, -/// one must call `load`, `load_mut`, or `load_init`, before reading or writing -/// to the account. For more details on zero-copy-deserialization, see the -/// [`account`](./attr.account.html) attribute. -/// -/// When using it's important to be mindful of any calls to `load` so as not to -/// induce a `RefCell` panic, especially when sharing accounts across CPI -/// boundaries. When in doubt, one should make sure all refs resulting from a -/// call to `load` are dropped before CPI. -#[deprecated(since = "0.18.0", note = "Please use AccountLoader instead")] -pub struct Loader<'info, T: ZeroCopy> { - acc_info: AccountInfo<'info>, - phantom: PhantomData<&'info T>, -} - -#[allow(deprecated)] -impl<'info, T: ZeroCopy + fmt::Debug> fmt::Debug for Loader<'info, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Loader") - .field("acc_info", &self.acc_info) - .field("phantom", &self.phantom) - .finish() - } -} - -#[allow(deprecated)] -impl<'info, T: ZeroCopy> Loader<'info, T> { - fn new(acc_info: AccountInfo<'info>) -> Loader<'info, T> { - Self { - acc_info, - phantom: PhantomData, - } - } - - /// Constructs a new `Loader` from a previously initialized account. - #[inline(never)] - #[allow(deprecated)] - pub fn try_from( - program_id: &Pubkey, - acc_info: &AccountInfo<'info>, - ) -> Result> { - if acc_info.owner != program_id { - return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram) - .with_pubkeys((*acc_info.owner, *program_id))); - } - let data: &[u8] = &acc_info.try_borrow_data()?; - if data.len() < T::discriminator().len() { - return Err(ErrorCode::AccountDiscriminatorNotFound.into()); - } - // Discriminator must match. - let disc_bytes = array_ref![data, 0, 8]; - if disc_bytes != &T::discriminator() { - return Err(ErrorCode::AccountDiscriminatorMismatch.into()); - } - - Ok(Loader::new(acc_info.clone())) - } - - /// Constructs a new `Loader` from an uninitialized account. - #[allow(deprecated)] - #[inline(never)] - pub fn try_from_unchecked( - program_id: &Pubkey, - acc_info: &AccountInfo<'info>, - ) -> Result> { - if acc_info.owner != program_id { - return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram) - .with_pubkeys((*acc_info.owner, *program_id))); - } - Ok(Loader::new(acc_info.clone())) - } - - /// Returns a Ref to the account data structure for reading. - #[allow(deprecated)] - pub fn load(&self) -> Result> { - let data = self.acc_info.try_borrow_data()?; - if data.len() < T::discriminator().len() { - return Err(ErrorCode::AccountDiscriminatorNotFound.into()); - } - - let disc_bytes = array_ref![data, 0, 8]; - if disc_bytes != &T::discriminator() { - return Err(ErrorCode::AccountDiscriminatorMismatch.into()); - } - - Ok(Ref::map(data, |data| bytemuck::from_bytes(&data[8..]))) - } - - /// Returns a `RefMut` to the account data structure for reading or writing. - #[allow(deprecated)] - pub fn load_mut(&self) -> Result> { - // AccountInfo api allows you to borrow mut even if the account isn't - // writable, so add this check for a better dev experience. - if !self.acc_info.is_writable { - return Err(ErrorCode::AccountNotMutable.into()); - } - - let data = self.acc_info.try_borrow_mut_data()?; - if data.len() < T::discriminator().len() { - return Err(ErrorCode::AccountDiscriminatorNotFound.into()); - } - - let disc_bytes = array_ref![data, 0, 8]; - if disc_bytes != &T::discriminator() { - return Err(ErrorCode::AccountDiscriminatorMismatch.into()); - } - - Ok(RefMut::map(data, |data| { - bytemuck::from_bytes_mut(&mut data.deref_mut()[8..]) - })) - } - - /// Returns a `RefMut` to the account data structure for reading or writing. - /// Should only be called once, when the account is being initialized. - #[allow(deprecated)] - pub fn load_init(&self) -> Result> { - // AccountInfo api allows you to borrow mut even if the account isn't - // writable, so add this check for a better dev experience. - if !self.acc_info.is_writable { - return Err(ErrorCode::AccountNotMutable.into()); - } - - let data = self.acc_info.try_borrow_mut_data()?; - - // The discriminator should be zero, since we're initializing. - let mut disc_bytes = [0u8; 8]; - disc_bytes.copy_from_slice(&data[..8]); - let discriminator = u64::from_le_bytes(disc_bytes); - if discriminator != 0 { - return Err(ErrorCode::AccountDiscriminatorAlreadySet.into()); - } - - Ok(RefMut::map(data, |data| { - bytemuck::from_bytes_mut(&mut data.deref_mut()[8..]) - })) - } -} - -#[allow(deprecated)] -impl<'info, T: ZeroCopy> Accounts<'info> for Loader<'info, T> { - #[inline(never)] - fn try_accounts( - program_id: &Pubkey, - accounts: &mut &[AccountInfo<'info>], - _ix_data: &[u8], - _bumps: &mut BTreeMap, - ) -> Result { - if accounts.is_empty() { - return Err(ErrorCode::AccountNotEnoughKeys.into()); - } - let account = &accounts[0]; - *accounts = &accounts[1..]; - let l = Loader::try_from(program_id, account)?; - Ok(l) - } -} - -#[allow(deprecated)] -impl<'info, T: ZeroCopy> AccountsExit<'info> for Loader<'info, T> { - // The account *cannot* be loaded when this is called. - fn exit(&self, _program_id: &Pubkey) -> Result<()> { - let mut data = self.acc_info.try_borrow_mut_data()?; - let dst: &mut [u8] = &mut data; - let mut writer = BpfWriter::new(dst); - writer.write_all(&T::discriminator()).unwrap(); - Ok(()) - } -} - -/// This function is for INTERNAL USE ONLY. -/// Do NOT use this function in a program. -/// Manual closing of `Loader<'info, T>` types is NOT supported. -/// -/// Details: Using `close` with `Loader<'info, T>` is not safe because -/// it requires the `mut` constraint but for that type the constraint -/// overwrites the "closed account" discriminator at the end of the instruction. -#[allow(deprecated)] -impl<'info, T: ZeroCopy> AccountsClose<'info> for Loader<'info, T> { - fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> { - crate::common::close(self.to_account_info(), sol_destination) - } -} - -#[allow(deprecated)] -impl<'info, T: ZeroCopy> ToAccountMetas for Loader<'info, T> { - fn to_account_metas(&self, is_signer: Option) -> Vec { - let is_signer = is_signer.unwrap_or(self.acc_info.is_signer); - let meta = match self.acc_info.is_writable { - false => AccountMeta::new_readonly(*self.acc_info.key, is_signer), - true => AccountMeta::new(*self.acc_info.key, is_signer), - }; - vec![meta] - } -} - -#[allow(deprecated)] -impl<'info, T: ZeroCopy> AsRef> for Loader<'info, T> { - fn as_ref(&self) -> &AccountInfo<'info> { - &self.acc_info - } -} - -#[allow(deprecated)] -impl<'info, T: ZeroCopy> ToAccountInfos<'info> for Loader<'info, T> { - fn to_account_infos(&self) -> Vec> { - vec![self.acc_info.clone()] - } -} - -#[allow(deprecated)] -impl<'info, T: ZeroCopy> Key for Loader<'info, T> { - fn key(&self) -> Pubkey { - *self.acc_info.key - } -} diff --git a/lang/src/accounts/mod.rs b/lang/src/accounts/mod.rs deleted file mode 100644 index 8839ef35..00000000 --- a/lang/src/accounts/mod.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! Account types that can be used in the account validation struct. - -pub mod account; -pub mod account_info; -pub mod account_loader; -pub mod boxed; -#[doc(hidden)] -#[allow(deprecated)] -pub mod cpi_account; -#[doc(hidden)] -#[allow(deprecated)] -pub mod cpi_state; -#[doc(hidden)] -#[allow(deprecated)] -pub mod loader; -pub mod program; -#[doc(hidden)] -#[allow(deprecated)] -pub mod program_account; -pub mod signer; -#[doc(hidden)] -#[allow(deprecated)] -pub mod state; -pub mod system_account; -pub mod sysvar; -pub mod unchecked_account; diff --git a/lang/src/accounts/program.rs b/lang/src/accounts/program.rs deleted file mode 100644 index fcdc0be5..00000000 --- a/lang/src/accounts/program.rs +++ /dev/null @@ -1,197 +0,0 @@ -//! Type validating that the account is the given Program - -use crate::error::{Error, ErrorCode}; -use crate::{ - AccountDeserialize, Accounts, AccountsExit, Id, Key, Result, ToAccountInfos, ToAccountMetas, -}; -use solana_program::account_info::AccountInfo; -use solana_program::bpf_loader_upgradeable::{self, UpgradeableLoaderState}; -use solana_program::instruction::AccountMeta; -use solana_program::pubkey::Pubkey; -use std::collections::BTreeMap; -use std::fmt; -use std::marker::PhantomData; -use std::ops::Deref; - -/// Type validating that the account is the given Program -/// -/// The type has a `programdata_address` function that will return `Option::Some` -/// if the program is owned by the [`BPFUpgradeableLoader`](https://docs.rs/solana-program/latest/solana_program/bpf_loader_upgradeable/index.html) -/// which will contain the `programdata_address` property of the `Program` variant of the [`UpgradeableLoaderState`](https://docs.rs/solana-program/latest/solana_program/bpf_loader_upgradeable/enum.UpgradeableLoaderState.html) enum. -/// -/// # Table of Contents -/// - [Basic Functionality](#basic-functionality) -/// - [Out of the Box Types](#out-of-the-box-types) -/// -/// # Basic Functionality -/// -/// Checks: -/// -/// - `account_info.key == expected_program` -/// - `account_info.executable == true` -/// -/// # Example -/// ```ignore -/// #[program] -/// mod my_program { -/// fn set_admin_settings(...){...} -/// } -/// -/// #[account] -/// #[derive(Default)] -/// pub struct AdminSettings { -/// ... -/// } -/// -/// #[derive(Accounts)] -/// pub struct SetAdminSettings<'info> { -/// #[account(mut, seeds = [b"admin"], bump)] -/// pub admin_settings: Account<'info, AdminSettings>, -/// #[account(constraint = program.programdata_address()? == Some(program_data.key()))] -/// pub program: Program<'info, MyProgram>, -/// #[account(constraint = program_data.upgrade_authority_address == Some(authority.key()))] -/// pub program_data: Account<'info, ProgramData>, -/// pub authority: Signer<'info>, -/// } -/// ``` -/// The given program has a function with which the upgrade authority can set admin settings. -/// -/// The required constraints are as follows: -/// -/// - `program` is the account of the program itself. -/// Its constraint checks that `program_data` is the account that contains the program's upgrade authority. -/// Implicitly, this checks that `program` is a BPFUpgradeable program (`program.programdata_address()?` -/// will be `None` if it's not). -/// - `program_data`'s constraint checks that its upgrade authority is the `authority` account. -/// - Finally, `authority` needs to sign the transaction. -/// -/// # Out of the Box Types -/// -/// Between the [`anchor_lang`](https://docs.rs/anchor-lang/latest/anchor_lang) and [`anchor_spl`](https://docs.rs/anchor_spl/latest/anchor_spl) crates, -/// the following `Program` types are provided out of the box: -/// -/// - [`System`](https://docs.rs/anchor-lang/latest/anchor_lang/struct.System.html) -/// - [`AssociatedToken`](https://docs.rs/anchor-spl/latest/anchor_spl/associated_token/struct.AssociatedToken.html) -/// - [`Token`](https://docs.rs/anchor-spl/latest/anchor_spl/token/struct.Token.html) -/// -#[derive(Clone)] -pub struct Program<'info, T: Id + Clone> { - info: AccountInfo<'info>, - _phantom: PhantomData, -} - -impl<'info, T: Id + Clone + fmt::Debug> fmt::Debug for Program<'info, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Program").field("info", &self.info).finish() - } -} - -impl<'a, T: Id + Clone> Program<'a, T> { - fn new(info: AccountInfo<'a>) -> Program<'a, T> { - Self { - info, - _phantom: PhantomData, - } - } - - /// Deserializes the given `info` into a `Program`. - #[inline(never)] - pub fn try_from(info: &AccountInfo<'a>) -> Result> { - if info.key != &T::id() { - return Err(Error::from(ErrorCode::InvalidProgramId).with_pubkeys((*info.key, T::id()))); - } - if !info.executable { - return Err(ErrorCode::InvalidProgramExecutable.into()); - } - - Ok(Program::new(info.clone())) - } - - pub fn programdata_address(&self) -> Result> { - if *self.info.owner == bpf_loader_upgradeable::ID { - let mut data: &[u8] = &self.info.try_borrow_data()?; - let upgradable_loader_state = - UpgradeableLoaderState::try_deserialize_unchecked(&mut data)?; - - match upgradable_loader_state { - UpgradeableLoaderState::Uninitialized - | UpgradeableLoaderState::Buffer { - authority_address: _, - } - | UpgradeableLoaderState::ProgramData { - slot: _, - upgrade_authority_address: _, - } => { - // Unreachable because check in try_from - // ensures that program is executable - // and therefore a program account. - unreachable!() - } - UpgradeableLoaderState::Program { - programdata_address, - } => Ok(Some(programdata_address)), - } - } else { - Ok(None) - } - } -} - -impl<'info, T> Accounts<'info> for Program<'info, T> -where - T: Id + Clone, -{ - #[inline(never)] - fn try_accounts( - _program_id: &Pubkey, - accounts: &mut &[AccountInfo<'info>], - _ix_data: &[u8], - _bumps: &mut BTreeMap, - ) -> Result { - if accounts.is_empty() { - return Err(ErrorCode::AccountNotEnoughKeys.into()); - } - let account = &accounts[0]; - *accounts = &accounts[1..]; - Program::try_from(account) - } -} - -impl<'info, T: Id + Clone> ToAccountMetas for Program<'info, T> { - fn to_account_metas(&self, is_signer: Option) -> Vec { - let is_signer = is_signer.unwrap_or(self.info.is_signer); - let meta = match self.info.is_writable { - false => AccountMeta::new_readonly(*self.info.key, is_signer), - true => AccountMeta::new(*self.info.key, is_signer), - }; - vec![meta] - } -} - -impl<'info, T: Id + Clone> ToAccountInfos<'info> for Program<'info, T> { - fn to_account_infos(&self) -> Vec> { - vec![self.info.clone()] - } -} - -impl<'info, T: Id + Clone> AsRef> for Program<'info, T> { - fn as_ref(&self) -> &AccountInfo<'info> { - &self.info - } -} - -impl<'info, T: Id + Clone> Deref for Program<'info, T> { - type Target = AccountInfo<'info>; - - fn deref(&self) -> &Self::Target { - &self.info - } -} - -impl<'info, T: AccountDeserialize + Id + Clone> AccountsExit<'info> for Program<'info, T> {} - -impl<'info, T: AccountDeserialize + Id + Clone> Key for Program<'info, T> { - fn key(&self) -> Pubkey { - *self.info.key - } -} diff --git a/lang/src/accounts/program_account.rs b/lang/src/accounts/program_account.rs deleted file mode 100644 index 312ad199..00000000 --- a/lang/src/accounts/program_account.rs +++ /dev/null @@ -1,195 +0,0 @@ -#[allow(deprecated)] -use crate::accounts::cpi_account::CpiAccount; -use crate::bpf_writer::BpfWriter; -use crate::error::{Error, ErrorCode}; -use crate::{ - AccountDeserialize, AccountSerialize, Accounts, AccountsClose, AccountsExit, Key, Result, - ToAccountInfo, ToAccountInfos, ToAccountMetas, -}; -use solana_program::account_info::AccountInfo; -use solana_program::instruction::AccountMeta; -use solana_program::pubkey::Pubkey; -use std::collections::BTreeMap; -use std::ops::{Deref, DerefMut}; - -/// Boxed container for a deserialized `account`. Use this to reference any -/// account owned by the currently executing program. -#[derive(Clone)] -#[deprecated(since = "0.15.0", note = "Please use Account instead")] -pub struct ProgramAccount<'info, T: AccountSerialize + AccountDeserialize + Clone> { - inner: Box>, -} - -#[derive(Clone)] -struct Inner<'info, T: AccountSerialize + AccountDeserialize + Clone> { - info: AccountInfo<'info>, - account: T, -} - -#[allow(deprecated)] -impl<'a, T: AccountSerialize + AccountDeserialize + Clone> ProgramAccount<'a, T> { - fn new(info: AccountInfo<'a>, account: T) -> ProgramAccount<'a, T> { - Self { - inner: Box::new(Inner { info, account }), - } - } - - /// Deserializes the given `info` into a `ProgramAccount`. - #[inline(never)] - pub fn try_from(program_id: &Pubkey, info: &AccountInfo<'a>) -> Result> { - if info.owner != program_id { - return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram) - .with_pubkeys((*info.owner, *program_id))); - } - let mut data: &[u8] = &info.try_borrow_data()?; - Ok(ProgramAccount::new( - info.clone(), - T::try_deserialize(&mut data)?, - )) - } - - /// Deserializes the given `info` into a `ProgramAccount` without checking - /// the account discriminator. Be careful when using this and avoid it if - /// possible. - #[inline(never)] - pub fn try_from_unchecked( - program_id: &Pubkey, - info: &AccountInfo<'a>, - ) -> Result> { - if info.owner != program_id { - return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram) - .with_pubkeys((*info.owner, *program_id))); - } - let mut data: &[u8] = &info.try_borrow_data()?; - Ok(ProgramAccount::new( - info.clone(), - T::try_deserialize_unchecked(&mut data)?, - )) - } - - pub fn into_inner(self) -> T { - self.inner.account - } -} - -#[allow(deprecated)] -impl<'info, T> Accounts<'info> for ProgramAccount<'info, T> -where - T: AccountSerialize + AccountDeserialize + Clone, -{ - #[inline(never)] - fn try_accounts( - program_id: &Pubkey, - accounts: &mut &[AccountInfo<'info>], - _ix_data: &[u8], - _bumps: &mut BTreeMap, - ) -> Result { - if accounts.is_empty() { - return Err(ErrorCode::AccountNotEnoughKeys.into()); - } - let account = &accounts[0]; - *accounts = &accounts[1..]; - ProgramAccount::try_from(program_id, account) - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AccountsExit<'info> - for ProgramAccount<'info, T> -{ - fn exit(&self, _program_id: &Pubkey) -> Result<()> { - let info = self.to_account_info(); - let mut data = info.try_borrow_mut_data()?; - let dst: &mut [u8] = &mut data; - let mut writer = BpfWriter::new(dst); - self.inner.account.try_serialize(&mut writer)?; - Ok(()) - } -} - -/// This function is for INTERNAL USE ONLY. -/// Do NOT use this function in a program. -/// Manual closing of `ProgramAccount<'info, T>` types is NOT supported. -/// -/// Details: Using `close` with `ProgramAccount<'info, T>` is not safe because -/// it requires the `mut` constraint but for that type the constraint -/// overwrites the "closed account" discriminator at the end of the instruction. -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AccountsClose<'info> - for ProgramAccount<'info, T> -{ - fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> { - crate::common::close(self.to_account_info(), sol_destination) - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountMetas - for ProgramAccount<'info, T> -{ - fn to_account_metas(&self, is_signer: Option) -> Vec { - let is_signer = is_signer.unwrap_or(self.inner.info.is_signer); - let meta = match self.inner.info.is_writable { - false => AccountMeta::new_readonly(*self.inner.info.key, is_signer), - true => AccountMeta::new(*self.inner.info.key, is_signer), - }; - vec![meta] - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'info> - for ProgramAccount<'info, T> -{ - fn to_account_infos(&self) -> Vec> { - vec![self.inner.info.clone()] - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AsRef> - for ProgramAccount<'info, T> -{ - fn as_ref(&self) -> &AccountInfo<'info> { - &self.inner.info - } -} - -#[allow(deprecated)] -impl<'a, T: AccountSerialize + AccountDeserialize + Clone> Deref for ProgramAccount<'a, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - &(*self.inner).account - } -} - -#[allow(deprecated)] -impl<'a, T: AccountSerialize + AccountDeserialize + Clone> DerefMut for ProgramAccount<'a, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - #[cfg(feature = "anchor-debug")] - if !self.inner.info.is_writable { - solana_program::msg!("The given ProgramAccount is not mutable"); - panic!(); - } - - &mut DerefMut::deref_mut(&mut self.inner).account - } -} - -#[allow(deprecated)] -impl<'info, T> From> for ProgramAccount<'info, T> -where - T: AccountSerialize + AccountDeserialize + Clone, -{ - fn from(a: CpiAccount<'info, T>) -> Self { - Self::new(a.to_account_info(), Deref::deref(&a).clone()) - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> Key for ProgramAccount<'info, T> { - fn key(&self) -> Pubkey { - *self.inner.info.key - } -} diff --git a/lang/src/accounts/signer.rs b/lang/src/accounts/signer.rs deleted file mode 100644 index 2cec796c..00000000 --- a/lang/src/accounts/signer.rs +++ /dev/null @@ -1,111 +0,0 @@ -//! Type validating that the account signed the transaction -use crate::error::ErrorCode; -use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas}; -use solana_program::account_info::AccountInfo; -use solana_program::instruction::AccountMeta; -use solana_program::pubkey::Pubkey; -use std::collections::BTreeMap; -use std::ops::Deref; - -/// Type validating that the account signed the transaction. No other ownership -/// or type checks are done. If this is used, one should not try to access the -/// underlying account data. -/// -/// Checks: -/// -/// - `Signer.info.is_signer == true` -/// -/// # Example -/// ```ignore -/// #[account] -/// #[derive(Default)] -/// pub struct MyData { -/// pub data: u64 -/// } -/// -/// #[derive(Accounts)] -/// pub struct Example<'info> { -/// #[account(init, payer = payer)] -/// pub my_acc: Account<'info, MyData>, -/// #[account(mut)] -/// pub payer: Signer<'info>, -/// pub system_program: Program<'info, System> -/// } -/// ``` -/// -/// When creating an account with `init`, the `payer` needs to sign the transaction. -#[derive(Debug, Clone)] -pub struct Signer<'info> { - info: AccountInfo<'info>, -} - -impl<'info> Signer<'info> { - fn new(info: AccountInfo<'info>) -> Signer<'info> { - Self { info } - } - - /// Deserializes the given `info` into a `Signer`. - #[inline(never)] - pub fn try_from(info: &AccountInfo<'info>) -> Result> { - if !info.is_signer { - return Err(ErrorCode::AccountNotSigner.into()); - } - Ok(Signer::new(info.clone())) - } -} - -impl<'info> Accounts<'info> for Signer<'info> { - #[inline(never)] - fn try_accounts( - _program_id: &Pubkey, - accounts: &mut &[AccountInfo<'info>], - _ix_data: &[u8], - _bumps: &mut BTreeMap, - ) -> Result { - if accounts.is_empty() { - return Err(ErrorCode::AccountNotEnoughKeys.into()); - } - let account = &accounts[0]; - *accounts = &accounts[1..]; - Signer::try_from(account) - } -} - -impl<'info> AccountsExit<'info> for Signer<'info> {} - -impl<'info> ToAccountMetas for Signer<'info> { - fn to_account_metas(&self, is_signer: Option) -> Vec { - let is_signer = is_signer.unwrap_or(self.info.is_signer); - let meta = match self.info.is_writable { - false => AccountMeta::new_readonly(*self.info.key, is_signer), - true => AccountMeta::new(*self.info.key, is_signer), - }; - vec![meta] - } -} - -impl<'info> ToAccountInfos<'info> for Signer<'info> { - fn to_account_infos(&self) -> Vec> { - vec![self.info.clone()] - } -} - -impl<'info> AsRef> for Signer<'info> { - fn as_ref(&self) -> &AccountInfo<'info> { - &self.info - } -} - -impl<'info> Deref for Signer<'info> { - type Target = AccountInfo<'info>; - - fn deref(&self) -> &Self::Target { - &self.info - } -} - -impl<'info> Key for Signer<'info> { - fn key(&self) -> Pubkey { - *self.info.key - } -} diff --git a/lang/src/accounts/state.rs b/lang/src/accounts/state.rs deleted file mode 100644 index 45e506ff..00000000 --- a/lang/src/accounts/state.rs +++ /dev/null @@ -1,170 +0,0 @@ -#[allow(deprecated)] -use crate::accounts::cpi_account::CpiAccount; -use crate::bpf_writer::BpfWriter; -use crate::error::{Error, ErrorCode}; -use crate::{ - AccountDeserialize, AccountSerialize, Accounts, AccountsExit, Key, Result, ToAccountInfo, - ToAccountInfos, ToAccountMetas, -}; -use solana_program::account_info::AccountInfo; -use solana_program::instruction::AccountMeta; -use solana_program::pubkey::Pubkey; -use std::collections::BTreeMap; -use std::ops::{Deref, DerefMut}; - -pub const PROGRAM_STATE_SEED: &str = "unversioned"; - -/// Boxed container for the program state singleton. -#[derive(Clone)] -#[deprecated] -pub struct ProgramState<'info, T: AccountSerialize + AccountDeserialize + Clone> { - inner: Box>, -} - -#[derive(Clone)] -struct Inner<'info, T: AccountSerialize + AccountDeserialize + Clone> { - info: AccountInfo<'info>, - account: T, -} - -#[allow(deprecated)] - -impl<'a, T: AccountSerialize + AccountDeserialize + Clone> ProgramState<'a, T> { - fn new(info: AccountInfo<'a>, account: T) -> ProgramState<'a, T> { - Self { - inner: Box::new(Inner { info, account }), - } - } - - /// Deserializes the given `info` into a `ProgramState`. - #[inline(never)] - pub fn try_from(program_id: &Pubkey, info: &AccountInfo<'a>) -> Result> { - if info.owner != program_id { - return Err(Error::from(ErrorCode::AccountOwnedByWrongProgram) - .with_pubkeys((*info.owner, *program_id))); - } - if info.key != &Self::address(program_id) { - solana_program::msg!("Invalid state address"); - return Err(ErrorCode::StateInvalidAddress.into()); - } - let mut data: &[u8] = &info.try_borrow_data()?; - Ok(ProgramState::new( - info.clone(), - T::try_deserialize(&mut data)?, - )) - } - - pub fn seed() -> &'static str { - PROGRAM_STATE_SEED - } - - pub fn address(program_id: &Pubkey) -> Pubkey { - address(program_id) - } -} - -#[allow(deprecated)] -impl<'info, T> Accounts<'info> for ProgramState<'info, T> -where - T: AccountSerialize + AccountDeserialize + Clone, -{ - #[inline(never)] - fn try_accounts( - program_id: &Pubkey, - accounts: &mut &[AccountInfo<'info>], - _ix_data: &[u8], - _bumps: &mut BTreeMap, - ) -> Result { - if accounts.is_empty() { - return Err(ErrorCode::AccountNotEnoughKeys.into()); - } - let account = &accounts[0]; - *accounts = &accounts[1..]; - ProgramState::try_from(program_id, account) - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountMetas - for ProgramState<'info, T> -{ - fn to_account_metas(&self, is_signer: Option) -> Vec { - let is_signer = is_signer.unwrap_or(self.inner.info.is_signer); - let meta = match self.inner.info.is_writable { - false => AccountMeta::new_readonly(*self.inner.info.key, is_signer), - true => AccountMeta::new(*self.inner.info.key, is_signer), - }; - vec![meta] - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> ToAccountInfos<'info> - for ProgramState<'info, T> -{ - fn to_account_infos(&self) -> Vec> { - vec![self.inner.info.clone()] - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AsRef> - for ProgramState<'info, T> -{ - fn as_ref(&self) -> &AccountInfo<'info> { - &self.inner.info - } -} - -#[allow(deprecated)] -impl<'a, T: AccountSerialize + AccountDeserialize + Clone> Deref for ProgramState<'a, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - &(*self.inner).account - } -} - -#[allow(deprecated)] -impl<'a, T: AccountSerialize + AccountDeserialize + Clone> DerefMut for ProgramState<'a, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut DerefMut::deref_mut(&mut self.inner).account - } -} - -#[allow(deprecated)] -impl<'info, T> From> for ProgramState<'info, T> -where - T: AccountSerialize + AccountDeserialize + Clone, -{ - fn from(a: CpiAccount<'info, T>) -> Self { - Self::new(a.to_account_info(), Deref::deref(&a).clone()) - } -} - -#[allow(deprecated)] -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AccountsExit<'info> - for ProgramState<'info, T> -{ - fn exit(&self, _program_id: &Pubkey) -> Result<()> { - let info = self.to_account_info(); - let mut data = info.try_borrow_mut_data()?; - let dst: &mut [u8] = &mut data; - let mut writer = BpfWriter::new(dst); - self.inner.account.try_serialize(&mut writer)?; - Ok(()) - } -} - -pub fn address(program_id: &Pubkey) -> Pubkey { - let (base, _nonce) = Pubkey::find_program_address(&[], program_id); - let seed = PROGRAM_STATE_SEED; - let owner = program_id; - Pubkey::create_with_seed(&base, seed, owner).unwrap() -} - -impl<'info, T: AccountSerialize + AccountDeserialize + Clone> Key for ProgramState<'info, T> { - fn key(&self) -> Pubkey { - *self.inner.info.key - } -} diff --git a/lang/src/accounts/system_account.rs b/lang/src/accounts/system_account.rs deleted file mode 100644 index a47ceafc..00000000 --- a/lang/src/accounts/system_account.rs +++ /dev/null @@ -1,90 +0,0 @@ -//! Type validating that the account is owned by the system program - -use crate::error::ErrorCode; -use crate::*; -use solana_program::account_info::AccountInfo; -use solana_program::instruction::AccountMeta; -use solana_program::pubkey::Pubkey; -use solana_program::system_program; -use std::collections::BTreeMap; -use std::ops::Deref; - -/// Type validating that the account is owned by the system program -/// -/// Checks: -/// -/// - `SystemAccount.info.owner == SystemProgram` -#[derive(Debug, Clone)] -pub struct SystemAccount<'info> { - info: AccountInfo<'info>, -} - -impl<'info> SystemAccount<'info> { - fn new(info: AccountInfo<'info>) -> SystemAccount<'info> { - Self { info } - } - - #[inline(never)] - pub fn try_from(info: &AccountInfo<'info>) -> Result> { - if *info.owner != system_program::ID { - return Err(ErrorCode::AccountNotSystemOwned.into()); - } - Ok(SystemAccount::new(info.clone())) - } -} - -impl<'info> Accounts<'info> for SystemAccount<'info> { - #[inline(never)] - fn try_accounts( - _program_id: &Pubkey, - accounts: &mut &[AccountInfo<'info>], - _ix_data: &[u8], - _bumps: &mut BTreeMap, - ) -> Result { - if accounts.is_empty() { - return Err(ErrorCode::AccountNotEnoughKeys.into()); - } - let account = &accounts[0]; - *accounts = &accounts[1..]; - SystemAccount::try_from(account) - } -} - -impl<'info> AccountsExit<'info> for SystemAccount<'info> {} - -impl<'info> ToAccountMetas for SystemAccount<'info> { - fn to_account_metas(&self, is_signer: Option) -> Vec { - let is_signer = is_signer.unwrap_or(self.info.is_signer); - let meta = match self.info.is_writable { - false => AccountMeta::new_readonly(*self.info.key, is_signer), - true => AccountMeta::new(*self.info.key, is_signer), - }; - vec![meta] - } -} - -impl<'info> ToAccountInfos<'info> for SystemAccount<'info> { - fn to_account_infos(&self) -> Vec> { - vec![self.info.clone()] - } -} - -impl<'info> AsRef> for SystemAccount<'info> { - fn as_ref(&self) -> &AccountInfo<'info> { - &self.info - } -} - -impl<'info> Deref for SystemAccount<'info> { - type Target = AccountInfo<'info>; - - fn deref(&self) -> &Self::Target { - &self.info - } -} - -impl<'info> Key for SystemAccount<'info> { - fn key(&self) -> Pubkey { - *self.info.key - } -} diff --git a/lang/src/accounts/sysvar.rs b/lang/src/accounts/sysvar.rs deleted file mode 100644 index e2e8336f..00000000 --- a/lang/src/accounts/sysvar.rs +++ /dev/null @@ -1,122 +0,0 @@ -//! Type validating that the account is a sysvar and deserializing it - -use crate::error::ErrorCode; -use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas}; -use solana_program::account_info::AccountInfo; -use solana_program::instruction::AccountMeta; -use solana_program::pubkey::Pubkey; -use std::collections::BTreeMap; -use std::fmt; -use std::ops::{Deref, DerefMut}; - -/// Type validating that the account is a sysvar and deserializing it. -/// -/// If possible, sysvars should not be used via accounts -/// but by using the [`get`](https://docs.rs/solana-program/latest/solana_program/sysvar/trait.Sysvar.html#method.get) -/// function on the desired sysvar. This is because using `get` -/// does not run the risk of Anchor having a bug in its `Sysvar` type -/// and using `get` also decreases tx size, making space for other -/// accounts that cannot be requested via syscall. -/// -/// # Example -/// ```ignore -/// // OK - via account in the account validation struct -/// #[derive(Accounts)] -/// pub struct Example<'info> { -/// pub clock: Sysvar<'info, Clock> -/// } -/// // BETTER - via syscall in the instruction function -/// fn better(ctx: Context) -> Result<()> { -/// let clock = Clock::get()?; -/// } -/// ``` -pub struct Sysvar<'info, T: solana_program::sysvar::Sysvar> { - info: AccountInfo<'info>, - account: T, -} - -impl<'info, T: solana_program::sysvar::Sysvar + fmt::Debug> fmt::Debug for Sysvar<'info, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Sysvar") - .field("info", &self.info) - .field("account", &self.account) - .finish() - } -} - -impl<'info, T: solana_program::sysvar::Sysvar> Sysvar<'info, T> { - pub fn from_account_info(acc_info: &AccountInfo<'info>) -> Result> { - match T::from_account_info(acc_info) { - Ok(val) => Ok(Sysvar { - info: acc_info.clone(), - account: val, - }), - Err(_) => Err(ErrorCode::AccountSysvarMismatch.into()), - } - } -} - -impl<'info, T: solana_program::sysvar::Sysvar> Clone for Sysvar<'info, T> { - fn clone(&self) -> Self { - Self { - info: self.info.clone(), - account: T::from_account_info(&self.info).unwrap(), - } - } -} - -impl<'info, T: solana_program::sysvar::Sysvar> Accounts<'info> for Sysvar<'info, T> { - fn try_accounts( - _program_id: &Pubkey, - accounts: &mut &[AccountInfo<'info>], - _ix_data: &[u8], - _bumps: &mut BTreeMap, - ) -> Result { - if accounts.is_empty() { - return Err(ErrorCode::AccountNotEnoughKeys.into()); - } - let account = &accounts[0]; - *accounts = &accounts[1..]; - Sysvar::from_account_info(account) - } -} - -impl<'info, T: solana_program::sysvar::Sysvar> ToAccountMetas for Sysvar<'info, T> { - fn to_account_metas(&self, _is_signer: Option) -> Vec { - vec![AccountMeta::new_readonly(*self.info.key, false)] - } -} - -impl<'info, T: solana_program::sysvar::Sysvar> ToAccountInfos<'info> for Sysvar<'info, T> { - fn to_account_infos(&self) -> Vec> { - vec![self.info.clone()] - } -} - -impl<'info, T: solana_program::sysvar::Sysvar> AsRef> for Sysvar<'info, T> { - fn as_ref(&self) -> &AccountInfo<'info> { - &self.info - } -} - -impl<'a, T: solana_program::sysvar::Sysvar> Deref for Sysvar<'a, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.account - } -} - -impl<'a, T: solana_program::sysvar::Sysvar> DerefMut for Sysvar<'a, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.account - } -} - -impl<'info, T: solana_program::sysvar::Sysvar> AccountsExit<'info> for Sysvar<'info, T> {} - -impl<'info, T: solana_program::sysvar::Sysvar> Key for Sysvar<'info, T> { - fn key(&self) -> Pubkey { - *self.info.key - } -} diff --git a/lang/src/accounts/unchecked_account.rs b/lang/src/accounts/unchecked_account.rs deleted file mode 100644 index 73576196..00000000 --- a/lang/src/accounts/unchecked_account.rs +++ /dev/null @@ -1,76 +0,0 @@ -//! Explicit wrapper for AccountInfo types to emphasize -//! that no checks are performed - -use crate::error::ErrorCode; -use crate::{Accounts, AccountsExit, Key, Result, ToAccountInfos, ToAccountMetas}; -use solana_program::account_info::AccountInfo; -use solana_program::instruction::AccountMeta; -use solana_program::pubkey::Pubkey; -use std::collections::BTreeMap; -use std::ops::Deref; - -/// Explicit wrapper for AccountInfo types to emphasize -/// that no checks are performed -#[derive(Debug, Clone)] -pub struct UncheckedAccount<'info>(AccountInfo<'info>); - -impl<'info> UncheckedAccount<'info> { - pub fn try_from(acc_info: AccountInfo<'info>) -> Self { - Self(acc_info) - } -} - -impl<'info> Accounts<'info> for UncheckedAccount<'info> { - fn try_accounts( - _program_id: &Pubkey, - accounts: &mut &[AccountInfo<'info>], - _ix_data: &[u8], - _bumps: &mut BTreeMap, - ) -> Result { - if accounts.is_empty() { - return Err(ErrorCode::AccountNotEnoughKeys.into()); - } - let account = &accounts[0]; - *accounts = &accounts[1..]; - Ok(UncheckedAccount(account.clone())) - } -} - -impl<'info> ToAccountMetas for UncheckedAccount<'info> { - fn to_account_metas(&self, is_signer: Option) -> Vec { - let is_signer = is_signer.unwrap_or(self.is_signer); - let meta = match self.is_writable { - false => AccountMeta::new_readonly(*self.key, is_signer), - true => AccountMeta::new(*self.key, is_signer), - }; - vec![meta] - } -} - -impl<'info> ToAccountInfos<'info> for UncheckedAccount<'info> { - fn to_account_infos(&self) -> Vec> { - vec![self.0.clone()] - } -} - -impl<'info> AccountsExit<'info> for UncheckedAccount<'info> {} - -impl<'info> AsRef> for UncheckedAccount<'info> { - fn as_ref(&self) -> &AccountInfo<'info> { - &self.0 - } -} - -impl<'info> Deref for UncheckedAccount<'info> { - type Target = AccountInfo<'info>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl<'info> Key for UncheckedAccount<'info> { - fn key(&self) -> Pubkey { - *self.0.key - } -} diff --git a/lang/src/bpf_upgradeable_state.rs b/lang/src/bpf_upgradeable_state.rs deleted file mode 100644 index 9da83856..00000000 --- a/lang/src/bpf_upgradeable_state.rs +++ /dev/null @@ -1,74 +0,0 @@ -use crate::error::ErrorCode; -use crate::{AccountDeserialize, AccountSerialize, Owner, Result}; -use solana_program::{ - bpf_loader_upgradeable::UpgradeableLoaderState, program_error::ProgramError, pubkey::Pubkey, -}; - -#[derive(Clone)] -pub struct ProgramData { - pub slot: u64, - pub upgrade_authority_address: Option, -} - -impl AccountDeserialize for ProgramData { - fn try_deserialize(buf: &mut &[u8]) -> Result { - ProgramData::try_deserialize_unchecked(buf) - } - - fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result { - let program_state = AccountDeserialize::try_deserialize_unchecked(buf)?; - - match program_state { - UpgradeableLoaderState::Uninitialized => Err(ErrorCode::AccountNotProgramData.into()), - UpgradeableLoaderState::Buffer { - authority_address: _, - } => Err(ErrorCode::AccountNotProgramData.into()), - UpgradeableLoaderState::Program { - programdata_address: _, - } => Err(ErrorCode::AccountNotProgramData.into()), - UpgradeableLoaderState::ProgramData { - slot, - upgrade_authority_address, - } => Ok(ProgramData { - slot, - upgrade_authority_address, - }), - } - } -} - -impl AccountSerialize for ProgramData { - fn try_serialize(&self, _writer: &mut W) -> Result<()> { - // no-op - Ok(()) - } -} - -impl Owner for ProgramData { - fn owner() -> solana_program::pubkey::Pubkey { - anchor_lang::solana_program::bpf_loader_upgradeable::ID - } -} - -impl Owner for UpgradeableLoaderState { - fn owner() -> Pubkey { - anchor_lang::solana_program::bpf_loader_upgradeable::ID - } -} - -impl AccountSerialize for UpgradeableLoaderState { - fn try_serialize(&self, _writer: &mut W) -> Result<()> { - // no-op - Ok(()) - } -} - -impl AccountDeserialize for UpgradeableLoaderState { - fn try_deserialize(buf: &mut &[u8]) -> Result { - UpgradeableLoaderState::try_deserialize_unchecked(buf) - } - - fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result { - bincode::deserialize(buf).map_err(|_| ProgramError::InvalidAccountData.into()) - } -} diff --git a/lang/src/bpf_writer.rs b/lang/src/bpf_writer.rs deleted file mode 100644 index 63ebdc47..00000000 --- a/lang/src/bpf_writer.rs +++ /dev/null @@ -1,46 +0,0 @@ -use solana_program::program_memory::sol_memcpy; -use std::cmp; -use std::io::{self, Write}; - -#[derive(Debug, Default)] -pub struct BpfWriter { - inner: T, - pos: u64, -} - -impl BpfWriter { - pub fn new(inner: T) -> Self { - Self { inner, pos: 0 } - } -} - -impl Write for BpfWriter<&mut [u8]> { - fn write(&mut self, buf: &[u8]) -> io::Result { - if self.pos >= self.inner.len() as u64 { - return Ok(0); - } - - let amt = cmp::min( - self.inner.len().saturating_sub(self.pos as usize), - buf.len(), - ); - sol_memcpy(&mut self.inner[(self.pos as usize)..], buf, amt); - self.pos += amt as u64; - Ok(amt) - } - - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - if self.write(buf)? == buf.len() { - Ok(()) - } else { - Err(io::Error::new( - io::ErrorKind::WriteZero, - "failed to write whole buffer", - )) - } - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} diff --git a/lang/src/common.rs b/lang/src/common.rs deleted file mode 100644 index 365d1281..00000000 --- a/lang/src/common.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::bpf_writer::BpfWriter; -use crate::error::ErrorCode; -use crate::prelude::error; -use crate::Result; -use solana_program::account_info::AccountInfo; -use std::io::Write; - -pub fn close<'info>(info: AccountInfo<'info>, sol_destination: AccountInfo<'info>) -> Result<()> { - // Transfer tokens from the account to the sol_destination. - let dest_starting_lamports = sol_destination.lamports(); - **sol_destination.lamports.borrow_mut() = - dest_starting_lamports.checked_add(info.lamports()).unwrap(); - **info.lamports.borrow_mut() = 0; - - // Mark the account discriminator as closed. - let mut data = info.try_borrow_mut_data()?; - let dst: &mut [u8] = &mut data; - let mut writer = BpfWriter::new(dst); - writer - .write_all(&crate::__private::CLOSED_ACCOUNT_DISCRIMINATOR) - .map_err(|_| error!(ErrorCode::AccountDidNotSerialize)) -} diff --git a/lang/src/context.rs b/lang/src/context.rs deleted file mode 100644 index f007bc51..00000000 --- a/lang/src/context.rs +++ /dev/null @@ -1,325 +0,0 @@ -//! Data structures that are used to provide non-argument inputs to program endpoints - -use crate::{Accounts, ToAccountInfos, ToAccountMetas}; -use solana_program::account_info::AccountInfo; -use solana_program::instruction::AccountMeta; -use solana_program::pubkey::Pubkey; -use std::collections::BTreeMap; -use std::fmt; - -/// Provides non-argument inputs to the program. -/// -/// # Example -/// ```ignore -/// pub fn set_data(ctx: Context, age: u64, other_data: u32) -> Result<()> { -/// // Set account data like this -/// (*ctx.accounts.my_account).age = age; -/// (*ctx.accounts.my_account).other_data = other_data; -/// // or like this -/// let my_account = &mut ctx.account.my_account; -/// my_account.age = age; -/// my_account.other_data = other_data; -/// Ok(()) -/// } -/// ``` -pub struct Context<'a, 'b, 'c, 'info, T> { - /// Currently executing program id. - pub program_id: &'a Pubkey, - /// Deserialized accounts. - pub accounts: &'b mut T, - /// Remaining accounts given but not deserialized or validated. - /// Be very careful when using this directly. - pub remaining_accounts: &'c [AccountInfo<'info>], - /// Bump seeds found during constraint validation. This is provided as a - /// convenience so that handlers don't have to recalculate bump seeds or - /// pass them in as arguments. - pub bumps: BTreeMap, -} - -impl<'a, 'b, 'c, 'info, T: fmt::Debug> fmt::Debug for Context<'a, 'b, 'c, 'info, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Context") - .field("program_id", &self.program_id) - .field("accounts", &self.accounts) - .field("remaining_accounts", &self.remaining_accounts) - .field("bumps", &self.bumps) - .finish() - } -} - -impl<'a, 'b, 'c, 'info, T: Accounts<'info>> Context<'a, 'b, 'c, 'info, T> { - pub fn new( - program_id: &'a Pubkey, - accounts: &'b mut T, - remaining_accounts: &'c [AccountInfo<'info>], - bumps: BTreeMap, - ) -> Self { - Self { - program_id, - accounts, - remaining_accounts, - bumps, - } - } -} - -/// Context specifying non-argument inputs for cross-program-invocations. -/// -/// # Example with and without PDA signature -/// ```ignore -/// // Callee Program -/// -/// use anchor_lang::prelude::*; -/// -/// declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); -/// -/// #[program] -/// pub mod callee { -/// use super::*; -/// pub fn init(ctx: Context) -> Result<()> { -/// (*ctx.accounts.data).authority = ctx.accounts.authority.key(); -/// Ok(()) -/// } -/// -/// pub fn set_data(ctx: Context, data: u64) -> Result<()> { -/// (*ctx.accounts.data_acc).data = data; -/// Ok(()) -/// } -/// } -/// -/// #[account] -/// #[derive(Default)] -/// pub struct Data { -/// data: u64, -/// authority: Pubkey, -/// } -/// -/// #[derive(Accounts)] -/// pub struct Init<'info> { -/// #[account(init, payer = payer)] -/// pub data: Account<'info, Data>, -/// pub payer: Signer<'info>, -/// pub authority: UncheckedAccount<'info>, -/// pub system_program: Program<'info, System> -/// } -/// -/// #[derive(Accounts)] -/// pub struct SetData<'info> { -/// #[account(mut, has_one = authority)] -/// pub data_acc: Account<'info, Data>, -/// pub authority: Signer<'info>, -/// } -/// -/// // Caller Program -/// -/// use anchor_lang::prelude::*; -/// use callee::{self, program::Callee}; -/// -/// declare_id!("Sxg7dBh5VLT8S1o6BqncZCPq9nhHHukjfVd6ohQJeAk"); -/// -/// #[program] -/// pub mod caller { -/// use super::*; -/// pub fn do_cpi(ctx: Context, data: u64) -> Result<()> { -/// let callee_id = ctx.accounts.callee.to_account_info(); -/// let callee_accounts = callee::cpi::accounts::SetData { -/// data_acc: ctx.accounts.data_acc.to_account_info(), -/// authority: ctx.accounts.callee_authority.to_account_info(), -/// }; -/// let cpi_ctx = CpiContext::new(callee_id, callee_accounts); -/// callee::cpi::set_data(cpi_ctx, data) -/// } -/// -/// pub fn do_cpi_with_pda_authority(ctx: Context, bump: u8, data: u64) -> Result<()> { -/// let seeds = &[&[b"example_seed", bytemuck::bytes_of(&bump)][..]]; -/// let callee_id = ctx.accounts.callee.to_account_info(); -/// let callee_accounts = callee::cpi::accounts::SetData { -/// data_acc: ctx.accounts.data_acc.to_account_info(), -/// authority: ctx.accounts.callee_authority.to_account_info(), -/// }; -/// let cpi_ctx = CpiContext::new_with_signer(callee_id, callee_accounts, seeds); -/// callee::cpi::set_data(cpi_ctx, data) -/// } -/// } -/// -/// // We can use "UncheckedAccount"s here because -/// // the callee program does the checks. -/// // We use "mut" so the autogenerated clients know -/// // that this account should be mutable. -/// #[derive(Accounts)] -/// pub struct DoCpi<'info> { -/// #[account(mut)] -/// pub data_acc: UncheckedAccount<'info>, -/// pub callee_authority: UncheckedAccount<'info>, -/// pub callee: Program<'info, Callee>, -/// } -/// -/// #[derive(Accounts)] -/// pub struct DoCpiWithPDAAuthority<'info> { -/// #[account(mut)] -/// pub data_acc: UncheckedAccount<'info>, -/// pub callee_authority: UncheckedAccount<'info>, -/// pub callee: Program<'info, Callee>, -/// } -/// ``` -pub struct CpiContext<'a, 'b, 'c, 'info, T> -where - T: ToAccountMetas + ToAccountInfos<'info>, -{ - pub accounts: T, - pub remaining_accounts: Vec>, - pub program: AccountInfo<'info>, - pub signer_seeds: &'a [&'b [&'c [u8]]], -} - -impl<'a, 'b, 'c, 'info, T> CpiContext<'a, 'b, 'c, 'info, T> -where - T: ToAccountMetas + ToAccountInfos<'info>, -{ - pub fn new(program: AccountInfo<'info>, accounts: T) -> Self { - Self { - accounts, - program, - remaining_accounts: Vec::new(), - signer_seeds: &[], - } - } - - #[must_use] - pub fn new_with_signer( - program: AccountInfo<'info>, - accounts: T, - signer_seeds: &'a [&'b [&'c [u8]]], - ) -> Self { - Self { - accounts, - program, - signer_seeds, - remaining_accounts: Vec::new(), - } - } - - #[must_use] - pub fn with_signer(mut self, signer_seeds: &'a [&'b [&'c [u8]]]) -> Self { - self.signer_seeds = signer_seeds; - self - } - - #[must_use] - pub fn with_remaining_accounts(mut self, ra: Vec>) -> Self { - self.remaining_accounts = ra; - self - } -} - -impl<'info, T: ToAccountInfos<'info> + ToAccountMetas> ToAccountInfos<'info> - for CpiContext<'_, '_, '_, 'info, T> -{ - fn to_account_infos(&self) -> Vec> { - let mut infos = self.accounts.to_account_infos(); - infos.extend_from_slice(&self.remaining_accounts); - infos.push(self.program.clone()); - infos - } -} - -impl<'info, T: ToAccountInfos<'info> + ToAccountMetas> ToAccountMetas - for CpiContext<'_, '_, '_, 'info, T> -{ - fn to_account_metas(&self, is_signer: Option) -> Vec { - let mut metas = self.accounts.to_account_metas(is_signer); - metas.append( - &mut self - .remaining_accounts - .iter() - .map(|acc| match acc.is_writable { - false => AccountMeta::new_readonly(*acc.key, acc.is_signer), - true => AccountMeta::new(*acc.key, acc.is_signer), - }) - .collect(), - ); - metas - } -} - -/// Context specifying non-argument inputs for cross-program-invocations -/// targeted at program state instructions. -#[doc(hidden)] -#[deprecated] -pub struct CpiStateContext<'a, 'b, 'c, 'info, T: Accounts<'info>> { - state: AccountInfo<'info>, - cpi_ctx: CpiContext<'a, 'b, 'c, 'info, T>, -} - -#[allow(deprecated)] -impl<'a, 'b, 'c, 'info, T: Accounts<'info>> CpiStateContext<'a, 'b, 'c, 'info, T> { - pub fn new(program: AccountInfo<'info>, state: AccountInfo<'info>, accounts: T) -> Self { - Self { - state, - cpi_ctx: CpiContext { - accounts, - program, - signer_seeds: &[], - remaining_accounts: Vec::new(), - }, - } - } - - pub fn new_with_signer( - program: AccountInfo<'info>, - state: AccountInfo<'info>, - accounts: T, - signer_seeds: &'a [&'b [&'c [u8]]], - ) -> Self { - Self { - state, - cpi_ctx: CpiContext { - accounts, - program, - signer_seeds, - remaining_accounts: Vec::new(), - }, - } - } - - #[must_use] - pub fn with_signer(mut self, signer_seeds: &'a [&'b [&'c [u8]]]) -> Self { - self.cpi_ctx = self.cpi_ctx.with_signer(signer_seeds); - self - } - - pub fn program(&self) -> &AccountInfo<'info> { - &self.cpi_ctx.program - } - - pub fn signer_seeds(&self) -> &[&[&[u8]]] { - self.cpi_ctx.signer_seeds - } -} - -#[allow(deprecated)] -impl<'a, 'b, 'c, 'info, T: Accounts<'info>> ToAccountMetas - for CpiStateContext<'a, 'b, 'c, 'info, T> -{ - fn to_account_metas(&self, is_signer: Option) -> Vec { - // State account is always first for state instructions. - let mut metas = vec![match self.state.is_writable { - false => AccountMeta::new_readonly(*self.state.key, false), - true => AccountMeta::new(*self.state.key, false), - }]; - metas.append(&mut self.cpi_ctx.accounts.to_account_metas(is_signer)); - metas - } -} - -#[allow(deprecated)] -impl<'a, 'b, 'c, 'info, T: Accounts<'info>> ToAccountInfos<'info> - for CpiStateContext<'a, 'b, 'c, 'info, T> -{ - fn to_account_infos(&self) -> Vec> { - let mut infos = self.cpi_ctx.accounts.to_account_infos(); - infos.push(self.state.clone()); - infos.push(self.cpi_ctx.program.clone()); - infos - } -} diff --git a/lang/src/ctor.rs b/lang/src/ctor.rs deleted file mode 100644 index c24d392e..00000000 --- a/lang/src/ctor.rs +++ /dev/null @@ -1,27 +0,0 @@ -use crate::{Accounts, ToAccountInfo}; -use solana_program::account_info::AccountInfo; - -/// The Ctor accounts that can be used to create any account within the program -/// itself (instead of creating the account on the client). -/// -/// This is used to create accounts at deterministic addresses, as a function of -/// nothing but a program ID--for example, to create state global program -/// structs and program IDL accounts. It's currently used **internally** within -/// the Anchor `#[program]` codegen. -#[derive(Accounts)] -pub struct Ctor<'info> { - // Payer of the transaction. - #[account(signer)] - pub from: AccountInfo<'info>, - // The deterministically defined "state" account being created via - // `create_account_with_seed`. - #[account(mut)] - pub to: AccountInfo<'info>, - // The program-derived-address signing off on the account creation. - // Seeds = &[] + bump seed. - pub base: AccountInfo<'info>, - // The system program. - pub system_program: AccountInfo<'info>, - // The program whose state is being constructed. - pub program: AccountInfo<'info>, -} diff --git a/lang/src/error.rs b/lang/src/error.rs deleted file mode 100644 index 0d825b35..00000000 --- a/lang/src/error.rs +++ /dev/null @@ -1,478 +0,0 @@ -use anchor_lang::error_code; -use borsh::maybestd::io::Error as BorshIoError; -use solana_program::{program_error::ProgramError, pubkey::Pubkey}; -use std::fmt::{Debug, Display}; - -/// The starting point for user defined error codes. -pub const ERROR_CODE_OFFSET: u32 = 6000; - -/// Error codes that can be returned by internal framework code. -/// -/// - >= 100 Instruction error codes -/// - >= 1000 IDL error codes -/// - >= 2000 constraint error codes -/// - >= 3000 account error codes -/// - = 4000 state error code -/// - >= 4100 misc error codes -/// - = 5000 deprecated error code -/// -/// The starting point for user-defined errors is defined -/// by the [ERROR_CODE_OFFSET](crate::error::ERROR_CODE_OFFSET). -#[error_code(offset = 0)] -pub enum ErrorCode { - // Instructions - /// 100 - 8 byte instruction identifier not provided - #[msg("8 byte instruction identifier not provided")] - InstructionMissing = 100, - /// 101 - Fallback functions are not supported - #[msg("Fallback functions are not supported")] - InstructionFallbackNotFound, - /// 102 - The program could not deserialize the given instruction - #[msg("The program could not deserialize the given instruction")] - InstructionDidNotDeserialize, - /// 103 - The program could not serialize the given instruction - #[msg("The program could not serialize the given instruction")] - InstructionDidNotSerialize, - - // IDL instructions - /// 1000 - The program was compiled without idl instructions - #[msg("The program was compiled without idl instructions")] - IdlInstructionStub = 1000, - /// 1001 - Invalid program given to the IDL instruction - #[msg("Invalid program given to the IDL instruction")] - IdlInstructionInvalidProgram, - - // Constraints - /// 2000 - A mut constraint was violated - #[msg("A mut constraint was violated")] - ConstraintMut = 2000, - /// 2001 - A has one constraint was violated - #[msg("A has one constraint was violated")] - ConstraintHasOne, - /// 2002 - A signer constraint was violated - #[msg("A signer constraint was violated")] - ConstraintSigner, - /// 2003 - A raw constraint was violated - #[msg("A raw constraint was violated")] - ConstraintRaw, - /// 2004 - An owner constraint was violated - #[msg("An owner constraint was violated")] - ConstraintOwner, - /// 2005 - A rent exemption constraint was violated - #[msg("A rent exemption constraint was violated")] - ConstraintRentExempt, - /// 2006 - A seeds constraint was violated - #[msg("A seeds constraint was violated")] - ConstraintSeeds, - /// 2007 - An executable constraint was violated - #[msg("An executable constraint was violated")] - ConstraintExecutable, - /// 2008 - A state constraint was violated - #[msg("A state constraint was violated")] - ConstraintState, - /// 2009 - An associated constraint was violated - #[msg("An associated constraint was violated")] - ConstraintAssociated, - /// 2010 - An associated init constraint was violated - #[msg("An associated init constraint was violated")] - ConstraintAssociatedInit, - /// 2011 - A close constraint was violated - #[msg("A close constraint was violated")] - ConstraintClose, - /// 2012 - An address constraint was violated - #[msg("An address constraint was violated")] - ConstraintAddress, - /// 2013 - Expected zero account discriminant - #[msg("Expected zero account discriminant")] - ConstraintZero, - /// 2014 - A token mint constraint was violated - #[msg("A token mint constraint was violated")] - ConstraintTokenMint, - /// 2015 - A token owner constraint was violated - #[msg("A token owner constraint was violated")] - ConstraintTokenOwner, - /// The mint mint is intentional -> a mint authority for the mint. - /// - /// 2016 - A mint mint authority constraint was violated - #[msg("A mint mint authority constraint was violated")] - ConstraintMintMintAuthority, - /// 2017 - A mint freeze authority constraint was violated - #[msg("A mint freeze authority constraint was violated")] - ConstraintMintFreezeAuthority, - /// 2018 - A mint decimals constraint was violated - #[msg("A mint decimals constraint was violated")] - ConstraintMintDecimals, - /// 2019 - A space constraint was violated - #[msg("A space constraint was violated")] - ConstraintSpace, - - // Require - /// 2500 - A require expression was violated - #[msg("A require expression was violated")] - RequireViolated = 2500, - /// 2501 - A require_eq expression was violated - #[msg("A require_eq expression was violated")] - RequireEqViolated, - /// 2502 - A require_keys_eq expression was violated - #[msg("A require_keys_eq expression was violated")] - RequireKeysEqViolated, - /// 2503 - A require_neq expression was violated - #[msg("A require_neq expression was violated")] - RequireNeqViolated, - /// 2504 - A require_keys_neq expression was violated - #[msg("A require_keys_neq expression was violated")] - RequireKeysNeqViolated, - /// 2505 - A require_gt expression was violated - #[msg("A require_gt expression was violated")] - RequireGtViolated, - /// 2506 - A require_gte expression was violated - #[msg("A require_gte expression was violated")] - RequireGteViolated, - - // Accounts. - /// 3000 - The account discriminator was already set on this account - #[msg("The account discriminator was already set on this account")] - AccountDiscriminatorAlreadySet = 3000, - /// 3001 - No 8 byte discriminator was found on the account - #[msg("No 8 byte discriminator was found on the account")] - AccountDiscriminatorNotFound, - /// 3002 - 8 byte discriminator did not match what was expected - #[msg("8 byte discriminator did not match what was expected")] - AccountDiscriminatorMismatch, - /// 3003 - Failed to deserialize the account - #[msg("Failed to deserialize the account")] - AccountDidNotDeserialize, - /// 3004 - Failed to serialize the account - #[msg("Failed to serialize the account")] - AccountDidNotSerialize, - /// 3005 - Not enough account keys given to the instruction - #[msg("Not enough account keys given to the instruction")] - AccountNotEnoughKeys, - /// 3006 - The given account is not mutable - #[msg("The given account is not mutable")] - AccountNotMutable, - /// 3007 - The given account is owned by a different program than expected - #[msg("The given account is owned by a different program than expected")] - AccountOwnedByWrongProgram, - /// 3008 - Program ID was not as expected - #[msg("Program ID was not as expected")] - InvalidProgramId, - /// 3009 - Program account is not executable - #[msg("Program account is not executable")] - InvalidProgramExecutable, - /// 3010 - The given account did not sign - #[msg("The given account did not sign")] - AccountNotSigner, - /// 3011 - The given account is not owned by the system program - #[msg("The given account is not owned by the system program")] - AccountNotSystemOwned, - /// 3012 - The program expected this account to be already initialized - #[msg("The program expected this account to be already initialized")] - AccountNotInitialized, - /// 3013 - The given account is not a program data account - #[msg("The given account is not a program data account")] - AccountNotProgramData, - /// 3014 - The given account is not the associated token account - #[msg("The given account is not the associated token account")] - AccountNotAssociatedTokenAccount, - /// 3015 - The given public key does not match the required sysvar - #[msg("The given public key does not match the required sysvar")] - AccountSysvarMismatch, - - // State. - /// 4000 - The given state account does not have the correct address - #[msg("The given state account does not have the correct address")] - StateInvalidAddress = 4000, - - // Miscellaneous - /// 4100 - The declared program id does not match actual program id - #[msg("The declared program id does not match the actual program id")] - DeclaredProgramIdMismatch = 4100, - - // Deprecated - /// 5000 - The API being used is deprecated and should no longer be used - #[msg("The API being used is deprecated and should no longer be used")] - Deprecated = 5000, -} - -#[derive(Debug)] -pub enum Error { - AnchorError(AnchorError), - ProgramError(ProgramErrorWithOrigin), -} - -impl std::error::Error for Error {} - -impl Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Error::AnchorError(ae) => Display::fmt(&ae, f), - Error::ProgramError(pe) => Display::fmt(&pe, f), - } - } -} - -impl From for Error { - fn from(ae: AnchorError) -> Self { - Self::AnchorError(ae) - } -} - -impl From for Error { - fn from(program_error: ProgramError) -> Self { - Self::ProgramError(program_error.into()) - } -} -impl From for Error { - fn from(error: BorshIoError) -> Self { - Error::ProgramError(ProgramError::from(error).into()) - } -} - -impl From for Error { - fn from(pe: ProgramErrorWithOrigin) -> Self { - Self::ProgramError(pe) - } -} - -impl Error { - pub fn log(&self) { - match self { - Error::ProgramError(program_error) => program_error.log(), - Error::AnchorError(anchor_error) => anchor_error.log(), - } - } - - pub fn with_account_name(mut self, account_name: impl ToString) -> Self { - match &mut self { - Error::AnchorError(ae) => { - ae.error_origin = Some(ErrorOrigin::AccountName(account_name.to_string())); - } - Error::ProgramError(pe) => { - pe.error_origin = Some(ErrorOrigin::AccountName(account_name.to_string())); - } - }; - self - } - - pub fn with_source(mut self, source: Source) -> Self { - match &mut self { - Error::AnchorError(ae) => { - ae.error_origin = Some(ErrorOrigin::Source(source)); - } - Error::ProgramError(pe) => { - pe.error_origin = Some(ErrorOrigin::Source(source)); - } - }; - self - } - - pub fn with_pubkeys(mut self, pubkeys: (Pubkey, Pubkey)) -> Self { - let pubkeys = Some(ComparedValues::Pubkeys((pubkeys.0, pubkeys.1))); - match &mut self { - Error::AnchorError(ae) => ae.compared_values = pubkeys, - Error::ProgramError(pe) => pe.compared_values = pubkeys, - }; - self - } - - pub fn with_values(mut self, values: (impl ToString, impl ToString)) -> Self { - match &mut self { - Error::AnchorError(ae) => { - ae.compared_values = Some(ComparedValues::Values(( - values.0.to_string(), - values.1.to_string(), - ))) - } - Error::ProgramError(pe) => { - pe.compared_values = Some(ComparedValues::Values(( - values.0.to_string(), - values.1.to_string(), - ))) - } - }; - self - } -} - -#[derive(Debug)] -pub struct ProgramErrorWithOrigin { - pub program_error: ProgramError, - pub error_origin: Option, - pub compared_values: Option, -} - -impl Display for ProgramErrorWithOrigin { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - Display::fmt(&self.program_error, f) - } -} - -impl ProgramErrorWithOrigin { - pub fn log(&self) { - match &self.error_origin { - None => { - anchor_lang::solana_program::msg!( - "ProgramError occurred. Error Code: {:?}. Error Number: {}. Error Message: {}.", - self.program_error, - u64::from(self.program_error.clone()), - self.program_error - ); - } - Some(ErrorOrigin::Source(source)) => { - anchor_lang::solana_program::msg!( - "ProgramError thrown in {}:{}. Error Code: {:?}. Error Number: {}. Error Message: {}.", - source.filename, - source.line, - self.program_error, - u64::from(self.program_error.clone()), - self.program_error - ); - } - Some(ErrorOrigin::AccountName(account_name)) => { - // using sol_log because msg! wrongly interprets 5 inputs as u64 - anchor_lang::solana_program::log::sol_log(&format!( - "ProgramError caused by account: {}. Error Code: {:?}. Error Number: {}. Error Message: {}.", - account_name, - self.program_error, - u64::from(self.program_error.clone()), - self.program_error - )); - } - } - match &self.compared_values { - Some(ComparedValues::Pubkeys((left, right))) => { - anchor_lang::solana_program::msg!("Left:"); - left.log(); - anchor_lang::solana_program::msg!("Right:"); - right.log(); - } - Some(ComparedValues::Values((left, right))) => { - anchor_lang::solana_program::msg!("Left: {}", left); - anchor_lang::solana_program::msg!("Right: {}", right); - } - None => (), - } - } - - pub fn with_source(mut self, source: Source) -> Self { - self.error_origin = Some(ErrorOrigin::Source(source)); - self - } - - pub fn with_account_name(mut self, account_name: impl ToString) -> Self { - self.error_origin = Some(ErrorOrigin::AccountName(account_name.to_string())); - self - } -} - -impl From for ProgramErrorWithOrigin { - fn from(program_error: ProgramError) -> Self { - Self { - program_error, - error_origin: None, - compared_values: None, - } - } -} - -#[derive(Debug)] -pub enum ComparedValues { - Values((String, String)), - Pubkeys((Pubkey, Pubkey)), -} - -#[derive(Debug)] -pub enum ErrorOrigin { - Source(Source), - AccountName(String), -} - -#[derive(Debug)] -pub struct AnchorError { - pub error_name: String, - pub error_code_number: u32, - pub error_msg: String, - pub error_origin: Option, - pub compared_values: Option, -} - -impl AnchorError { - pub fn log(&self) { - match &self.error_origin { - None => { - anchor_lang::solana_program::log::sol_log(&format!( - "AnchorError occurred. Error Code: {}. Error Number: {}. Error Message: {}.", - self.error_name, self.error_code_number, self.error_msg - )); - } - Some(ErrorOrigin::Source(source)) => { - anchor_lang::solana_program::msg!( - "AnchorError thrown in {}:{}. Error Code: {}. Error Number: {}. Error Message: {}.", - source.filename, - source.line, - self.error_name, - self.error_code_number, - self.error_msg - ); - } - Some(ErrorOrigin::AccountName(account_name)) => { - anchor_lang::solana_program::log::sol_log(&format!( - "AnchorError caused by account: {}. Error Code: {}. Error Number: {}. Error Message: {}.", - account_name, - self.error_name, - self.error_code_number, - self.error_msg - )); - } - } - match &self.compared_values { - Some(ComparedValues::Pubkeys((left, right))) => { - anchor_lang::solana_program::msg!("Left:"); - left.log(); - anchor_lang::solana_program::msg!("Right:"); - right.log(); - } - Some(ComparedValues::Values((left, right))) => { - anchor_lang::solana_program::msg!("Left: {}", left); - anchor_lang::solana_program::msg!("Right: {}", right); - } - None => (), - } - } - - pub fn with_source(mut self, source: Source) -> Self { - self.error_origin = Some(ErrorOrigin::Source(source)); - self - } - - pub fn with_account_name(mut self, account_name: impl ToString) -> Self { - self.error_origin = Some(ErrorOrigin::AccountName(account_name.to_string())); - self - } -} - -impl Display for AnchorError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - Debug::fmt(&self, f) - } -} - -impl std::convert::From for anchor_lang::solana_program::program_error::ProgramError { - fn from(e: Error) -> anchor_lang::solana_program::program_error::ProgramError { - match e { - Error::AnchorError(AnchorError { - error_code_number, .. - }) => { - anchor_lang::solana_program::program_error::ProgramError::Custom(error_code_number) - } - Error::ProgramError(program_error) => program_error.program_error, - } - } -} - -#[derive(Debug)] -pub struct Source { - pub filename: &'static str, - pub line: u32, -} diff --git a/lang/src/idl.rs b/lang/src/idl.rs deleted file mode 100644 index 8bbd8931..00000000 --- a/lang/src/idl.rs +++ /dev/null @@ -1,111 +0,0 @@ -//! Defines the instructions and account state used to store a program's -//! IDL on-chain at a canonical account address, which can be derived as a -//! function of nothing other than the program's ID. -//! -//! It can be upgraded in a way similar to a BPF upgradeable program. That is, -//! one may invoke the `IdlInstruction::CreateBuffer` instruction to create -//! a buffer, `IdlInstruction::Write` to write a new IDL into it, and then -//! `IdlInstruction::SetBuffer` to copy the IDL into the program's canonical -//! IDL account. In order to perform this upgrade, the buffer's `authority` -//! must match the canonical IDL account's authority. -//! -//! Because the IDL can be larger than the max transaction size, the transaction -//! must be broken up into several pieces and stored into the IDL account with -//! multiple transactions via the `Write` instruction to continuously append to -//! the account's IDL data buffer. -//! -//! Note that IDL account instructions are automatically inserted into all -//! Anchor programs. To remove them, one can use the `no-idl` feature. - -#[allow(deprecated)] -use crate::accounts::program_account::ProgramAccount; -use crate::prelude::*; -use solana_program::pubkey::Pubkey; - -// The first 8 bytes of an instruction to create or modify the IDL account. This -// instruction is defined outside the main program's instruction enum, so that -// the enum variant tags can align with function source order. -// -// Sha256(anchor:idl)[..8]; -pub const IDL_IX_TAG: u64 = 0x0a69e9a778bcf440; - -// The Pubkey that is stored as the 'authority' on the IdlAccount when the authority -// is "erased". -pub const ERASED_AUTHORITY: Pubkey = Pubkey::new_from_array([0u8; 32]); - -#[derive(AnchorSerialize, AnchorDeserialize)] -pub enum IdlInstruction { - // One time initializer for creating the program's idl account. - Create { data_len: u64 }, - // Creates a new IDL account buffer. Can be called several times. - CreateBuffer, - // Appends the given data to the end of the idl account buffer. - Write { data: Vec }, - // Sets a new data buffer for the IdlAccount. - SetBuffer, - // Sets a new authority on the IdlAccount. - SetAuthority { new_authority: Pubkey }, -} - -// Accounts for the Create instruction. -pub type IdlCreateAccounts<'info> = crate::ctor::Ctor<'info>; - -// Accounts for Idl instructions. -#[derive(Accounts)] -pub struct IdlAccounts<'info> { - #[account(mut, has_one = authority)] - #[allow(deprecated)] - pub idl: ProgramAccount<'info, IdlAccount>, - #[account(constraint = authority.key != &ERASED_AUTHORITY)] - pub authority: Signer<'info>, -} - -// Accounts for creating an idl buffer. -#[derive(Accounts)] -pub struct IdlCreateBuffer<'info> { - #[account(zero)] - #[allow(deprecated)] - pub buffer: ProgramAccount<'info, IdlAccount>, - #[account(constraint = authority.key != &ERASED_AUTHORITY)] - pub authority: Signer<'info>, -} - -// Accounts for upgrading the canonical IdlAccount with the buffer. -#[derive(Accounts)] -pub struct IdlSetBuffer<'info> { - // The buffer with the new idl data. - #[account(mut, constraint = buffer.authority == idl.authority)] - #[allow(deprecated)] - pub buffer: ProgramAccount<'info, IdlAccount>, - // The idl account to be updated with the buffer's data. - #[account(mut, has_one = authority)] - #[allow(deprecated)] - pub idl: ProgramAccount<'info, IdlAccount>, - #[account(constraint = authority.key != &ERASED_AUTHORITY)] - pub authority: Signer<'info>, -} - -// The account holding a program's IDL. This is stored on chain so that clients -// can fetch it and generate a client with nothing but a program's ID. -// -// Note: we use the same account for the "write buffer", similar to the -// bpf upgradeable loader's mechanism. -#[account("internal")] -#[derive(Debug)] -pub struct IdlAccount { - // Address that can modify the IDL. - pub authority: Pubkey, - // Compressed idl bytes. - pub data: Vec, -} - -impl IdlAccount { - pub fn address(program_id: &Pubkey) -> Pubkey { - let program_signer = Pubkey::find_program_address(&[], program_id).0; - Pubkey::create_with_seed(&program_signer, IdlAccount::seed(), program_id) - .expect("Seed is always valid") - } - pub fn seed() -> &'static str { - "anchor:idl" - } -} diff --git a/lang/src/lib.rs b/lang/src/lib.rs deleted file mode 100644 index 50661c5b..00000000 --- a/lang/src/lib.rs +++ /dev/null @@ -1,580 +0,0 @@ -//! Anchor ⚓ is a framework for Solana's Sealevel runtime providing several -//! convenient developer tools. -//! -//! - Rust eDSL for writing safe, secure, and high level Solana programs -//! - [IDL](https://en.wikipedia.org/wiki/Interface_description_language) specification -//! - TypeScript package for generating clients from IDL -//! - CLI and workspace management for developing complete applications -//! -//! If you're familiar with developing in Ethereum's -//! [Solidity](https://docs.soliditylang.org/en/v0.7.4/), -//! [Truffle](https://www.trufflesuite.com/), -//! [web3.js](https://github.com/ethereum/web3.js) or Parity's -//! [Ink!](https://github.com/paritytech/ink), then the experience will be -//! familiar. Although the syntax and semantics are targeted at Solana, the high -//! level workflow of writing RPC request handlers, emitting an IDL, and -//! generating clients from IDL is the same. -//! -//! For detailed tutorials and examples on how to use Anchor, see the guided -//! [tutorials](https://project-serum.github.io/anchor) or examples in the GitHub -//! [repository](https://github.com/project-serum/anchor). -//! -//! Presented here are the Rust primitives for building on Solana. - -extern crate self as anchor_lang; - -use bytemuck::{Pod, Zeroable}; -use solana_program::account_info::AccountInfo; -use solana_program::instruction::AccountMeta; -use solana_program::pubkey::Pubkey; -use std::collections::BTreeMap; -use std::io::Write; - -mod account_meta; -pub mod accounts; -mod bpf_upgradeable_state; -mod bpf_writer; -mod common; -pub mod context; -mod ctor; -pub mod error; -#[doc(hidden)] -pub mod idl; -pub mod system_program; - -mod vec; -pub use crate::bpf_upgradeable_state::*; -pub use anchor_attribute_access_control::access_control; -pub use anchor_attribute_account::{account, declare_id, zero_copy}; -pub use anchor_attribute_constant::constant; -pub use anchor_attribute_error::*; -pub use anchor_attribute_event::{emit, event}; -pub use anchor_attribute_interface::interface; -pub use anchor_attribute_program::program; -pub use anchor_attribute_state::state; -pub use anchor_derive_accounts::Accounts; -/// Borsh is the default serialization format for instructions and accounts. -pub use borsh::{BorshDeserialize as AnchorDeserialize, BorshSerialize as AnchorSerialize}; -pub use solana_program; - -pub type Result = std::result::Result; - -/// A data structure of validated accounts that can be deserialized from the -/// input to a Solana program. Implementations of this trait should perform any -/// and all requisite constraint checks on accounts to ensure the accounts -/// maintain any invariants required for the program to run securely. In most -/// cases, it's recommended to use the [`Accounts`](./derive.Accounts.html) -/// derive macro to implement this trait. -pub trait Accounts<'info>: ToAccountMetas + ToAccountInfos<'info> + Sized { - /// Returns the validated accounts struct. What constitutes "valid" is - /// program dependent. However, users of these types should never have to - /// worry about account substitution attacks. For example, if a program - /// expects a `Mint` account from the SPL token program in a particular - /// field, then it should be impossible for this method to return `Ok` if - /// any other account type is given--from the SPL token program or elsewhere. - /// - /// `program_id` is the currently executing program. `accounts` is the - /// set of accounts to construct the type from. For every account used, - /// the implementation should mutate the slice, consuming the used entry - /// so that it cannot be used again. - fn try_accounts( - program_id: &Pubkey, - accounts: &mut &[AccountInfo<'info>], - ix_data: &[u8], - bumps: &mut BTreeMap, - ) -> Result; -} - -/// The exit procedure for an account. Any cleanup or persistence to storage -/// should be done here. -pub trait AccountsExit<'info>: ToAccountMetas + ToAccountInfos<'info> { - /// `program_id` is the currently executing program. - fn exit(&self, _program_id: &Pubkey) -> Result<()> { - // no-op - Ok(()) - } -} - -/// The close procedure to initiate garabage collection of an account, allowing -/// one to retrieve the rent exemption. -pub trait AccountsClose<'info>: ToAccountInfos<'info> { - fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()>; -} - -/// Transformation to -/// [`AccountMeta`](../solana_program/instruction/struct.AccountMeta.html) -/// structs. -pub trait ToAccountMetas { - /// `is_signer` is given as an optional override for the signer meta field. - /// This covers the edge case when a program-derived-address needs to relay - /// a transaction from a client to another program but sign the transaction - /// before the relay. The client cannot mark the field as a signer, and so - /// we have to override the is_signer meta field given by the client. - fn to_account_metas(&self, is_signer: Option) -> Vec; -} - -/// Transformation to -/// [`AccountInfo`](../solana_program/account_info/struct.AccountInfo.html) -/// structs. -pub trait ToAccountInfos<'info> { - fn to_account_infos(&self) -> Vec>; -} - -/// Transformation to an `AccountInfo` struct. -pub trait ToAccountInfo<'info> { - fn to_account_info(&self) -> AccountInfo<'info>; -} - -impl<'info, T> ToAccountInfo<'info> for T -where - T: AsRef>, -{ - fn to_account_info(&self) -> AccountInfo<'info> { - self.as_ref().clone() - } -} - -/// A data structure that can be serialized and stored into account storage, -/// i.e. an -/// [`AccountInfo`](../solana_program/account_info/struct.AccountInfo.html#structfield.data)'s -/// mutable data slice. -/// -/// Implementors of this trait should ensure that any subsequent usage of the -/// `AccountDeserialize` trait succeeds if and only if the account is of the -/// correct type. -/// -/// In most cases, one can use the default implementation provided by the -/// [`#[account]`](./attr.account.html) attribute. -pub trait AccountSerialize { - /// Serializes the account data into `writer`. - fn try_serialize(&self, _writer: &mut W) -> Result<()> { - Ok(()) - } -} - -/// A data structure that can be deserialized and stored into account storage, -/// i.e. an -/// [`AccountInfo`](../solana_program/account_info/struct.AccountInfo.html#structfield.data)'s -/// mutable data slice. -pub trait AccountDeserialize: Sized { - /// Deserializes previously initialized account data. Should fail for all - /// uninitialized accounts, where the bytes are zeroed. Implementations - /// should be unique to a particular account type so that one can never - /// successfully deserialize the data of one account type into another. - /// For example, if the SPL token program were to implement this trait, - /// it should be impossible to deserialize a `Mint` account into a token - /// `Account`. - fn try_deserialize(buf: &mut &[u8]) -> Result { - Self::try_deserialize_unchecked(buf) - } - - /// Deserializes account data without checking the account discriminator. - /// This should only be used on account initialization, when the bytes of - /// the account are zeroed. - fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result; -} - -/// An account data structure capable of zero copy deserialization. -pub trait ZeroCopy: Discriminator + Copy + Clone + Zeroable + Pod {} - -/// Calculates the data for an instruction invocation, where the data is -/// `Sha256(::)[..8] || BorshSerialize(args)`. -/// `args` is a borsh serialized struct of named fields for each argument given -/// to an instruction. -pub trait InstructionData: AnchorSerialize { - fn data(&self) -> Vec; -} - -/// An event that can be emitted via a Solana log. See [`emit!`](crate::prelude::emit) for an example. -pub trait Event: AnchorSerialize + AnchorDeserialize + Discriminator { - fn data(&self) -> Vec; -} - -// The serialized event data to be emitted via a Solana log. -// TODO: remove this on the next major version upgrade. -#[doc(hidden)] -#[deprecated(since = "0.4.2", note = "Please use Event instead")] -pub trait EventData: AnchorSerialize + Discriminator { - fn data(&self) -> Vec; -} - -/// 8 byte unique identifier for a type. -pub trait Discriminator { - fn discriminator() -> [u8; 8]; -} - -/// Bump seed for program derived addresses. -pub trait Bump { - fn seed(&self) -> u8; -} - -/// Defines an address expected to own an account. -pub trait Owner { - fn owner() -> Pubkey; -} - -/// Defines the id of a program. -pub trait Id { - fn id() -> Pubkey; -} - -/// Defines the Pubkey of an account. -pub trait Key { - fn key(&self) -> Pubkey; -} - -impl Key for Pubkey { - fn key(&self) -> Pubkey { - *self - } -} - -/// The prelude contains all commonly used components of the crate. -/// All programs should include it via `anchor_lang::prelude::*;`. -pub mod prelude { - pub use super::{ - access_control, account, accounts::account::Account, - accounts::account_loader::AccountLoader, accounts::program::Program, - accounts::signer::Signer, accounts::system_account::SystemAccount, - accounts::sysvar::Sysvar, accounts::unchecked_account::UncheckedAccount, constant, - context::Context, context::CpiContext, declare_id, emit, err, error, event, interface, - program, require, require_eq, require_gt, require_gte, require_keys_eq, require_keys_neq, - require_neq, solana_program::bpf_loader_upgradeable::UpgradeableLoaderState, source, state, - system_program::System, zero_copy, AccountDeserialize, AccountSerialize, Accounts, - AccountsExit, AnchorDeserialize, AnchorSerialize, Id, Key, Owner, ProgramData, Result, - ToAccountInfo, ToAccountInfos, ToAccountMetas, - }; - pub use anchor_attribute_error::*; - pub use borsh; - pub use error::*; - pub use solana_program::account_info::{next_account_info, AccountInfo}; - pub use solana_program::instruction::AccountMeta; - pub use solana_program::msg; - pub use solana_program::program_error::ProgramError; - pub use solana_program::pubkey::Pubkey; - pub use solana_program::sysvar::clock::Clock; - pub use solana_program::sysvar::epoch_schedule::EpochSchedule; - pub use solana_program::sysvar::instructions::Instructions; - pub use solana_program::sysvar::rent::Rent; - pub use solana_program::sysvar::rewards::Rewards; - pub use solana_program::sysvar::slot_hashes::SlotHashes; - pub use solana_program::sysvar::slot_history::SlotHistory; - pub use solana_program::sysvar::stake_history::StakeHistory; - pub use solana_program::sysvar::Sysvar as SolanaSysvar; - pub use thiserror; -} - -/// Internal module used by macros and unstable apis. -#[doc(hidden)] -pub mod __private { - use super::Result; - /// The discriminator anchor uses to mark an account as closed. - pub const CLOSED_ACCOUNT_DISCRIMINATOR: [u8; 8] = [255, 255, 255, 255, 255, 255, 255, 255]; - - pub use crate::ctor::Ctor; - - pub use anchor_attribute_account::ZeroCopyAccessor; - - pub use anchor_attribute_event::EventIndex; - - pub use base64; - - pub use bytemuck; - - use solana_program::pubkey::Pubkey; - - pub mod state { - pub use crate::accounts::state::*; - } - - // Calculates the size of an account, which may be larger than the deserialized - // data in it. This trait is currently only used for `#[state]` accounts. - #[doc(hidden)] - pub trait AccountSize { - fn size(&self) -> Result; - } - - // Very experimental trait. - #[doc(hidden)] - pub trait ZeroCopyAccessor { - fn get(&self) -> Ty; - fn set(input: &Ty) -> Self; - } - - #[doc(hidden)] - impl ZeroCopyAccessor for [u8; 32] { - fn get(&self) -> Pubkey { - Pubkey::new(self) - } - fn set(input: &Pubkey) -> [u8; 32] { - input.to_bytes() - } - } - - #[doc(hidden)] - pub use crate::accounts::state::PROGRAM_STATE_SEED; -} - -/// Ensures a condition is true, otherwise returns with the given error. -/// Use this with or without a custom error type. -/// -/// # Example -/// ```ignore -/// // Instruction function -/// pub fn set_data(ctx: Context, data: u64) -> Result<()> { -/// require!(ctx.accounts.data.mutation_allowed, MyError::MutationForbidden); -/// ctx.accounts.data.data = data; -/// Ok(()) -/// } -/// -/// // An enum for custom error codes -/// #[error_code] -/// pub enum MyError { -/// MutationForbidden -/// } -/// -/// // An account definition -/// #[account] -/// #[derive(Default)] -/// pub struct MyData { -/// mutation_allowed: bool, -/// data: u64 -/// } -/// -/// // An account validation struct -/// #[derive(Accounts)] -/// pub struct SetData<'info> { -/// #[account(mut)] -/// pub data: Account<'info, MyData> -/// } -/// ``` -#[macro_export] -macro_rules! require { - ($invariant:expr, $error:tt $(,)?) => { - if !($invariant) { - return Err(anchor_lang::error!(crate::ErrorCode::$error)); - } - }; - ($invariant:expr, $error:expr $(,)?) => { - if !($invariant) { - return Err(anchor_lang::error!($error)); - } - }; -} - -/// Ensures two NON-PUBKEY values are equal. -/// -/// Use [require_keys_eq](crate::prelude::require_keys_eq) -/// to compare two pubkeys. -/// -/// Can be used with or without a custom error code. -/// -/// # Example -/// ```rust,ignore -/// pub fn set_data(ctx: Context, data: u64) -> Result<()> { -/// require_eq!(ctx.accounts.data.data, 0); -/// ctx.accounts.data.data = data; -/// Ok(()) -/// } -/// ``` -#[macro_export] -macro_rules! require_eq { - ($value1: expr, $value2: expr, $error_code:expr $(,)?) => { - if $value1 != $value2 { - return Err(error!($error_code).with_values(($value1, $value2))); - } - }; - ($value1: expr, $value2: expr $(,)?) => { - if $value1 != $value2 { - return Err(error!(anchor_lang::error::ErrorCode::RequireEqViolated) - .with_values(($value1, $value2))); - } - }; -} - -/// Ensures two NON-PUBKEY values are not equal. -/// -/// Use [require_keys_neq](crate::prelude::require_keys_neq) -/// to compare two pubkeys. -/// -/// Can be used with or without a custom error code. -/// -/// # Example -/// ```rust,ignore -/// pub fn set_data(ctx: Context, data: u64) -> Result<()> { -/// require_neq!(ctx.accounts.data.data, 0); -/// ctx.accounts.data.data = data; -/// Ok(()); -/// } -/// ``` -#[macro_export] -macro_rules! require_neq { - ($value1: expr, $value2: expr, $error_code: expr $(,)?) => { - if $value1 == $value2 { - return Err(error!($error_code).with_values(($value1, $value2))); - } - }; - ($value1: expr, $value2: expr $(,)?) => { - if $value1 == $value2 { - return Err(error!(anchor_lang::error::ErrorCode::RequireNeqViolated) - .with_values(($value1, $value2))); - } - }; -} - -/// Ensures two pubkeys values are equal. -/// -/// Use [require_eq](crate::prelude::require_eq) -/// to compare two non-pubkey values. -/// -/// Can be used with or without a custom error code. -/// -/// # Example -/// ```rust,ignore -/// pub fn set_data(ctx: Context, data: u64) -> Result<()> { -/// require_keys_eq!(ctx.accounts.data.authority.key(), ctx.accounts.authority.key()); -/// ctx.accounts.data.data = data; -/// Ok(()) -/// } -/// ``` -#[macro_export] -macro_rules! require_keys_eq { - ($value1: expr, $value2: expr, $error_code:expr $(,)?) => { - if $value1 != $value2 { - return Err(error!($error_code).with_pubkeys(($value1, $value2))); - } - }; - ($value1: expr, $value2: expr $(,)?) => { - if $value1 != $value2 { - return Err(error!(anchor_lang::error::ErrorCode::RequireKeysEqViolated) - .with_pubkeys(($value1, $value2))); - } - }; -} - -/// Ensures two pubkeys are not equal. -/// -/// Use [require_neq](crate::prelude::require_neq) -/// to compare two non-pubkey values. -/// -/// Can be used with or without a custom error code. -/// -/// # Example -/// ```rust,ignore -/// pub fn set_data(ctx: Context, data: u64) -> Result<()> { -/// require_keys_neq!(ctx.accounts.data.authority.key(), ctx.accounts.other.key()); -/// ctx.accounts.data.data = data; -/// Ok(()) -/// } -/// ``` -#[macro_export] -macro_rules! require_keys_neq { - ($value1: expr, $value2: expr, $error_code: expr $(,)?) => { - if $value1 == $value2 { - return Err(error!($error_code).with_pubkeys(($value1, $value2))); - } - }; - ($value1: expr, $value2: expr $(,)?) => { - if $value1 == $value2 { - return Err( - error!(anchor_lang::error::ErrorCode::RequireKeysNeqViolated) - .with_pubkeys(($value1, $value2)), - ); - } - }; -} - -/// Ensures the first NON-PUBKEY value is greater than the second -/// NON-PUBKEY value. -/// -/// To include an equality check, use [require_gte](crate::require_gte). -/// -/// Can be used with or without a custom error code. -/// -/// # Example -/// ```rust,ignore -/// pub fn set_data(ctx: Context, data: u64) -> Result<()> { -/// require_gt!(ctx.accounts.data.data, 0); -/// ctx.accounts.data.data = data; -/// Ok(()); -/// } -/// ``` -#[macro_export] -macro_rules! require_gt { - ($value1: expr, $value2: expr, $error_code: expr $(,)?) => { - if $value1 <= $value2 { - return Err(error!($error_code).with_values(($value1, $value2))); - } - }; - ($value1: expr, $value2: expr $(,)?) => { - if $value1 <= $value2 { - return Err(error!(anchor_lang::error::ErrorCode::RequireGtViolated) - .with_values(($value1, $value2))); - } - }; -} - -/// Ensures the first NON-PUBKEY value is greater than or equal -/// to the second NON-PUBKEY value. -/// -/// Can be used with or without a custom error code. -/// -/// # Example -/// ```rust,ignore -/// pub fn set_data(ctx: Context, data: u64) -> Result<()> { -/// require_gte!(ctx.accounts.data.data, 1); -/// ctx.accounts.data.data = data; -/// Ok(()); -/// } -/// ``` -#[macro_export] -macro_rules! require_gte { - ($value1: expr, $value2: expr, $error_code: expr $(,)?) => { - if $value1 < $value2 { - return Err(error!($error_code).with_values(($value1, $value2))); - } - }; - ($value1: expr, $value2: expr $(,)?) => { - if $value1 < $value2 { - return Err(error!(anchor_lang::error::ErrorCode::RequireGteViolated) - .with_values(($value1, $value2))); - } - }; -} - -/// Returns with the given error. -/// Use this with a custom error type. -/// -/// # Example -/// ```ignore -/// // Instruction function -/// pub fn example(ctx: Context) -> Result<()> { -/// err!(MyError::SomeError) -/// } -/// -/// // An enum for custom error codes -/// #[error_code] -/// pub enum MyError { -/// SomeError -/// } -/// ``` -#[macro_export] -macro_rules! err { - ($error:tt $(,)?) => { - Err(anchor_lang::error!(crate::ErrorCode::$error)) - }; - ($error:expr $(,)?) => { - Err(anchor_lang::error!($error)) - }; -} - -/// Creates a [`Source`](crate::error::Source) -#[macro_export] -macro_rules! source { - () => { - anchor_lang::error::Source { - filename: file!(), - line: line!(), - } - }; -} diff --git a/lang/src/system_program.rs b/lang/src/system_program.rs deleted file mode 100644 index 4072baa1..00000000 --- a/lang/src/system_program.rs +++ /dev/null @@ -1,381 +0,0 @@ -use crate::prelude::*; -use solana_program::pubkey::Pubkey; - -pub use solana_program::system_program::ID; - -#[derive(Debug, Clone)] -pub struct System; - -impl anchor_lang::Id for System { - fn id() -> Pubkey { - ID - } -} - -pub fn advance_nonce_account<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, AdvanceNonceAccount<'info>>, -) -> Result<()> { - let ix = crate::solana_program::system_instruction::advance_nonce_account( - ctx.accounts.nonce.key, - ctx.accounts.authorized.key, - ); - crate::solana_program::program::invoke_signed( - &ix, - &[ - ctx.accounts.nonce, - ctx.accounts.recent_blockhashes, - ctx.accounts.authorized, - ], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -#[derive(Accounts)] -pub struct AdvanceNonceAccount<'info> { - pub nonce: AccountInfo<'info>, - pub authorized: AccountInfo<'info>, - pub recent_blockhashes: AccountInfo<'info>, -} - -pub fn allocate<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, Allocate<'info>>, - space: u64, -) -> Result<()> { - let ix = crate::solana_program::system_instruction::allocate( - ctx.accounts.account_to_allocate.key, - space, - ); - crate::solana_program::program::invoke_signed( - &ix, - &[ctx.accounts.account_to_allocate], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -#[derive(Accounts)] -pub struct Allocate<'info> { - pub account_to_allocate: AccountInfo<'info>, -} - -pub fn allocate_with_seed<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, AllocateWithSeed<'info>>, - seed: &str, - space: u64, - owner: &Pubkey, -) -> Result<()> { - let ix = crate::solana_program::system_instruction::allocate_with_seed( - ctx.accounts.account_to_allocate.key, - ctx.accounts.base.key, - seed, - space, - owner, - ); - crate::solana_program::program::invoke_signed( - &ix, - &[ctx.accounts.account_to_allocate, ctx.accounts.base], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -#[derive(Accounts)] -pub struct AllocateWithSeed<'info> { - pub account_to_allocate: AccountInfo<'info>, - pub base: AccountInfo<'info>, -} - -pub fn assign<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, Assign<'info>>, - owner: &Pubkey, -) -> Result<()> { - let ix = crate::solana_program::system_instruction::assign( - ctx.accounts.account_to_assign.key, - owner, - ); - crate::solana_program::program::invoke_signed( - &ix, - &[ctx.accounts.account_to_assign], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -#[derive(Accounts)] -pub struct Assign<'info> { - pub account_to_assign: AccountInfo<'info>, -} - -pub fn assign_with_seed<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, AssignWithSeed<'info>>, - seed: &str, - owner: &Pubkey, -) -> Result<()> { - let ix = crate::solana_program::system_instruction::assign_with_seed( - ctx.accounts.account_to_assign.key, - ctx.accounts.base.key, - seed, - owner, - ); - crate::solana_program::program::invoke_signed( - &ix, - &[ctx.accounts.account_to_assign, ctx.accounts.base], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -#[derive(Accounts)] -pub struct AssignWithSeed<'info> { - pub account_to_assign: AccountInfo<'info>, - pub base: AccountInfo<'info>, -} - -pub fn authorize_nonce_account<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, AuthorizeNonceAccount<'info>>, - new_authority: &Pubkey, -) -> Result<()> { - let ix = crate::solana_program::system_instruction::authorize_nonce_account( - ctx.accounts.nonce.key, - ctx.accounts.authorized.key, - new_authority, - ); - crate::solana_program::program::invoke_signed( - &ix, - &[ctx.accounts.nonce, ctx.accounts.authorized], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -#[derive(Accounts)] -pub struct AuthorizeNonceAccount<'info> { - pub nonce: AccountInfo<'info>, - pub authorized: AccountInfo<'info>, -} - -pub fn create_account<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, CreateAccount<'info>>, - lamports: u64, - space: u64, - owner: &Pubkey, -) -> Result<()> { - let ix = crate::solana_program::system_instruction::create_account( - ctx.accounts.from.key, - ctx.accounts.to.key, - lamports, - space, - owner, - ); - crate::solana_program::program::invoke_signed( - &ix, - &[ctx.accounts.from, ctx.accounts.to], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -#[derive(Accounts)] -pub struct CreateAccount<'info> { - pub from: AccountInfo<'info>, - pub to: AccountInfo<'info>, -} - -pub fn create_account_with_seed<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, CreateAccountWithSeed<'info>>, - seed: &str, - lamports: u64, - space: u64, - owner: &Pubkey, -) -> Result<()> { - let ix = crate::solana_program::system_instruction::create_account_with_seed( - ctx.accounts.from.key, - ctx.accounts.to.key, - ctx.accounts.base.key, - seed, - lamports, - space, - owner, - ); - crate::solana_program::program::invoke_signed( - &ix, - &[ctx.accounts.from, ctx.accounts.to, ctx.accounts.base], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -#[derive(Accounts)] -pub struct CreateAccountWithSeed<'info> { - pub from: AccountInfo<'info>, - pub to: AccountInfo<'info>, - pub base: AccountInfo<'info>, -} - -pub fn create_nonce_account<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, CreateNonceAccount<'info>>, - lamports: u64, - authority: &Pubkey, -) -> Result<()> { - let ixs = crate::solana_program::system_instruction::create_nonce_account( - ctx.accounts.from.key, - ctx.accounts.nonce.key, - authority, - lamports, - ); - crate::solana_program::program::invoke_signed( - &ixs[0], - &[ctx.accounts.from, ctx.accounts.nonce.clone()], - ctx.signer_seeds, - )?; - - crate::solana_program::program::invoke_signed( - &ixs[1], - &[ - ctx.accounts.nonce, - ctx.accounts.recent_blockhashes, - ctx.accounts.rent, - ], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -#[derive(Accounts)] -pub struct CreateNonceAccount<'info> { - pub from: AccountInfo<'info>, - pub nonce: AccountInfo<'info>, - pub recent_blockhashes: AccountInfo<'info>, - pub rent: AccountInfo<'info>, -} - -pub fn create_nonce_account_with_seed<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, CreateNonceAccountWithSeed<'info>>, - lamports: u64, - seed: &str, - authority: &Pubkey, -) -> Result<()> { - let ixs = crate::solana_program::system_instruction::create_nonce_account_with_seed( - ctx.accounts.from.key, - ctx.accounts.nonce.key, - ctx.accounts.base.key, - seed, - authority, - lamports, - ); - crate::solana_program::program::invoke_signed( - &ixs[0], - &[ - ctx.accounts.from, - ctx.accounts.nonce.clone(), - ctx.accounts.base, - ], - ctx.signer_seeds, - )?; - - crate::solana_program::program::invoke_signed( - &ixs[1], - &[ - ctx.accounts.nonce, - ctx.accounts.recent_blockhashes, - ctx.accounts.rent, - ], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -#[derive(Accounts)] -pub struct CreateNonceAccountWithSeed<'info> { - pub from: AccountInfo<'info>, - pub nonce: AccountInfo<'info>, - pub base: AccountInfo<'info>, - pub recent_blockhashes: AccountInfo<'info>, - pub rent: AccountInfo<'info>, -} - -pub fn transfer<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, Transfer<'info>>, - lamports: u64, -) -> Result<()> { - let ix = crate::solana_program::system_instruction::transfer( - ctx.accounts.from.key, - ctx.accounts.to.key, - lamports, - ); - crate::solana_program::program::invoke_signed( - &ix, - &[ctx.accounts.from, ctx.accounts.to], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -#[derive(Accounts)] -pub struct Transfer<'info> { - pub from: AccountInfo<'info>, - pub to: AccountInfo<'info>, -} - -pub fn transfer_with_seed<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, TransferWithSeed<'info>>, - from_seed: String, - from_owner: &Pubkey, - lamports: u64, -) -> Result<()> { - let ix = crate::solana_program::system_instruction::transfer_with_seed( - ctx.accounts.from.key, - ctx.accounts.base.key, - from_seed, - from_owner, - ctx.accounts.to.key, - lamports, - ); - crate::solana_program::program::invoke_signed( - &ix, - &[ctx.accounts.from, ctx.accounts.base, ctx.accounts.to], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -#[derive(Accounts)] -pub struct TransferWithSeed<'info> { - pub from: AccountInfo<'info>, - pub base: AccountInfo<'info>, - pub to: AccountInfo<'info>, -} - -pub fn withdraw_nonce_account<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, WithdrawNonceAccount<'info>>, - lamports: u64, -) -> Result<()> { - let ix = crate::solana_program::system_instruction::withdraw_nonce_account( - ctx.accounts.nonce.key, - ctx.accounts.authorized.key, - ctx.accounts.to.key, - lamports, - ); - crate::solana_program::program::invoke_signed( - &ix, - &[ - ctx.accounts.nonce, - ctx.accounts.to, - ctx.accounts.recent_blockhashes, - ctx.accounts.rent, - ctx.accounts.authorized, - ], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -#[derive(Accounts)] -pub struct WithdrawNonceAccount<'info> { - pub nonce: AccountInfo<'info>, - pub to: AccountInfo<'info>, - pub recent_blockhashes: AccountInfo<'info>, - pub rent: AccountInfo<'info>, - pub authorized: AccountInfo<'info>, -} diff --git a/lang/src/vec.rs b/lang/src/vec.rs deleted file mode 100644 index 1036f674..00000000 --- a/lang/src/vec.rs +++ /dev/null @@ -1,96 +0,0 @@ -use crate::{Accounts, Result, ToAccountInfos, ToAccountMetas}; -use solana_program::account_info::AccountInfo; -use solana_program::instruction::AccountMeta; -use solana_program::pubkey::Pubkey; -use std::collections::BTreeMap; - -impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Vec { - fn to_account_infos(&self) -> Vec> { - self.iter() - .flat_map(|item| item.to_account_infos()) - .collect() - } -} - -impl ToAccountMetas for Vec { - fn to_account_metas(&self, is_signer: Option) -> Vec { - self.iter() - .flat_map(|item| (*item).to_account_metas(is_signer)) - .collect() - } -} - -impl<'info, T: Accounts<'info>> Accounts<'info> for Vec { - fn try_accounts( - program_id: &Pubkey, - accounts: &mut &[AccountInfo<'info>], - ix_data: &[u8], - bumps: &mut BTreeMap, - ) -> Result { - let mut vec: Vec = Vec::new(); - T::try_accounts(program_id, accounts, ix_data, bumps).map(|item| vec.push(item))?; - Ok(vec) - } -} - -#[cfg(test)] -mod tests { - use solana_program::clock::Epoch; - use solana_program::pubkey::Pubkey; - - use super::*; - - #[derive(Accounts)] - pub struct Test<'info> { - #[account(signer)] - test: AccountInfo<'info>, - } - - #[test] - fn test_accounts_trait_for_vec() { - let program_id = Pubkey::default(); - - let key = Pubkey::default(); - let mut lamports1 = 0; - let mut data1 = vec![0; 10]; - let owner = Pubkey::default(); - let account1 = AccountInfo::new( - &key, - true, - true, - &mut lamports1, - &mut data1, - &owner, - false, - Epoch::default(), - ); - - let mut lamports2 = 0; - let mut data2 = vec![0; 10]; - let account2 = AccountInfo::new( - &key, - true, - true, - &mut lamports2, - &mut data2, - &owner, - false, - Epoch::default(), - ); - let mut bumps = std::collections::BTreeMap::new(); - let mut accounts = &[account1, account2][..]; - let parsed_accounts = - Vec::::try_accounts(&program_id, &mut accounts, &[], &mut bumps).unwrap(); - - assert_eq!(accounts.len(), parsed_accounts.len()); - } - - #[test] - #[should_panic] - fn test_accounts_trait_for_vec_empty() { - let program_id = Pubkey::default(); - let mut bumps = std::collections::BTreeMap::new(); - let mut accounts = &[][..]; - Vec::::try_accounts(&program_id, &mut accounts, &[], &mut bumps).unwrap(); - } -} diff --git a/lang/syn/Cargo.toml b/lang/syn/Cargo.toml deleted file mode 100644 index 90d55cf6..00000000 --- a/lang/syn/Cargo.toml +++ /dev/null @@ -1,30 +0,0 @@ -[package] -name = "anchor-syn" -version = "0.24.2" -authors = ["Serum Foundation "] -repository = "https://github.com/project-serum/anchor" -license = "Apache-2.0" -description = "Anchor syntax parsing and code generation tools" -rust-version = "1.56" -edition = "2021" - -[features] -init-if-needed = [] -idl = [] -hash = [] -default = [] -anchor-debug = [] -seeds = [] - -[dependencies] -proc-macro2 = { version = "1.0", features=["span-locations"]} -proc-macro2-diagnostics = "0.9" -quote = "1.0" -syn = { version = "1.0.60", features = ["full", "extra-traits", "parsing"] } -anyhow = "1.0.32" -heck = "0.3.1" -serde = { version = "1.0.122", features = ["derive"] } -serde_json = "1.0" -sha2 = "0.9.2" -thiserror = "1.0" -bs58 = "0.3.1" diff --git a/lang/syn/src/codegen/accounts/__client_accounts.rs b/lang/syn/src/codegen/accounts/__client_accounts.rs deleted file mode 100644 index 343239d3..00000000 --- a/lang/syn/src/codegen/accounts/__client_accounts.rs +++ /dev/null @@ -1,157 +0,0 @@ -use crate::{AccountField, AccountsStruct, Ty}; -use heck::SnakeCase; -use quote::quote; -use std::str::FromStr; - -// Generates the private `__client_accounts` mod implementation, containing -// a generated struct mapping 1-1 to the `Accounts` struct, except with -// `Pubkey`s as the types. This is generated for Rust *clients*. -pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { - let name = &accs.ident; - let account_mod_name: proc_macro2::TokenStream = format!( - "__client_accounts_{}", - accs.ident.to_string().to_snake_case() - ) - .parse() - .unwrap(); - - let account_struct_fields: Vec = accs - .fields - .iter() - .map(|f: &AccountField| match f { - AccountField::CompositeField(s) => { - let name = &s.ident; - let docs = if !s.docs.is_empty() { - proc_macro2::TokenStream::from_str(&format!("#[doc = r#\"{}\"#]", s.docs)) - .unwrap() - } else { - quote!() - }; - let symbol: proc_macro2::TokenStream = format!( - "__client_accounts_{0}::{1}", - s.symbol.to_snake_case(), - s.symbol, - ) - .parse() - .unwrap(); - quote! { - #docs - pub #name: #symbol - } - } - AccountField::Field(f) => { - let name = &f.ident; - let docs = if !f.docs.is_empty() { - proc_macro2::TokenStream::from_str(&format!("#[doc = r#\"{}\"#]", f.docs)) - .unwrap() - } else { - quote!() - }; - quote! { - #docs - pub #name: anchor_lang::solana_program::pubkey::Pubkey - } - } - }) - .collect(); - - let account_struct_metas: Vec = accs - .fields - .iter() - .map(|f: &AccountField| match f { - AccountField::CompositeField(s) => { - let name = &s.ident; - quote! { - account_metas.extend(self.#name.to_account_metas(None)); - } - } - AccountField::Field(f) => { - let is_signer = match f.ty { - Ty::Signer => true, - _ => f.constraints.is_signer(), - }; - let is_signer = match is_signer { - false => quote! {false}, - true => quote! {true}, - }; - let meta = match f.constraints.is_mutable() { - false => quote! { anchor_lang::solana_program::instruction::AccountMeta::new_readonly }, - true => quote! { anchor_lang::solana_program::instruction::AccountMeta::new }, - }; - let name = &f.ident; - quote! { - account_metas.push(#meta(self.#name, #is_signer)); - } - } - }) - .collect(); - // Re-export all composite account structs (i.e. other structs deriving - // accounts embedded into this struct. Required because, these embedded - // structs are *not* visible from the #[program] macro, which is responsible - // for generating the `accounts` mod, which aggregates all the the generated - // accounts used for structs. - let re_exports: Vec = { - // First, dedup the exports. - let mut re_exports = std::collections::HashSet::new(); - for f in accs.fields.iter().filter_map(|f: &AccountField| match f { - AccountField::CompositeField(s) => Some(s), - AccountField::Field(_) => None, - }) { - re_exports.insert(format!( - "__client_accounts_{0}::{1}", - f.symbol.to_snake_case(), - f.symbol, - )); - } - - re_exports - .iter() - .map(|symbol: &String| { - let symbol: proc_macro2::TokenStream = symbol.parse().unwrap(); - quote! { - pub use #symbol; - } - }) - .collect() - }; - - let struct_doc = proc_macro2::TokenStream::from_str(&format!( - "#[doc = \" Generated client accounts for [`{}`].\"]", - name - )) - .unwrap(); - - quote! { - /// An internal, Anchor generated module. This is used (as an - /// implementation detail), to generate a struct for a given - /// `#[derive(Accounts)]` implementation, where each field is a Pubkey, - /// instead of an `AccountInfo`. This is useful for clients that want - /// to generate a list of accounts, without explicitly knowing the - /// order all the fields should be in. - /// - /// To access the struct in this module, one should use the sibling - /// `accounts` module (also generated), which re-exports this. - pub(crate) mod #account_mod_name { - use super::*; - use anchor_lang::prelude::borsh; - #(#re_exports)* - - #struct_doc - #[derive(anchor_lang::AnchorSerialize)] - pub struct #name { - #(#account_struct_fields),* - } - - #[automatically_derived] - impl anchor_lang::ToAccountMetas for #name { - fn to_account_metas(&self, is_signer: Option) -> Vec { - let mut account_metas = vec![]; - - #(#account_struct_metas)* - - account_metas - } - } - } - } -} diff --git a/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs b/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs deleted file mode 100644 index e2080a4f..00000000 --- a/lang/syn/src/codegen/accounts/__cpi_client_accounts.rs +++ /dev/null @@ -1,185 +0,0 @@ -use std::str::FromStr; - -use crate::{AccountField, AccountsStruct, Ty}; -use heck::SnakeCase; -use quote::quote; - -// Generates the private `__cpi_client_accounts` mod implementation, containing -// a generated struct mapping 1-1 to the `Accounts` struct, except with -// `AccountInfo`s as the types. This is generated for CPI clients. -pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { - let name = &accs.ident; - let account_mod_name: proc_macro2::TokenStream = format!( - "__cpi_client_accounts_{}", - accs.ident.to_string().to_snake_case() - ) - .parse() - .unwrap(); - - let account_struct_fields: Vec = accs - .fields - .iter() - .map(|f: &AccountField| match f { - AccountField::CompositeField(s) => { - let name = &s.ident; - let docs = if !s.docs.is_empty() { - proc_macro2::TokenStream::from_str(&format!("#[doc = r#\"{}\"#]", s.docs)) - .unwrap() - } else { - quote!() - }; - let symbol: proc_macro2::TokenStream = format!( - "__cpi_client_accounts_{0}::{1}", - s.symbol.to_snake_case(), - s.symbol, - ) - .parse() - .unwrap(); - quote! { - #docs - pub #name: #symbol<'info> - } - } - AccountField::Field(f) => { - let name = &f.ident; - let docs = if !f.docs.is_empty() { - proc_macro2::TokenStream::from_str(&format!("#[doc = r#\"{}\"#]", f.docs)) - .unwrap() - } else { - quote!() - }; - quote! { - #docs - pub #name: anchor_lang::solana_program::account_info::AccountInfo<'info> - } - } - }) - .collect(); - - let account_struct_metas: Vec = accs - .fields - .iter() - .map(|f: &AccountField| match f { - AccountField::CompositeField(s) => { - let name = &s.ident; - quote! { - account_metas.extend(self.#name.to_account_metas(None)); - } - } - AccountField::Field(f) => { - let is_signer = match f.ty { - Ty::Signer => true, - _ => f.constraints.is_signer(), - }; - let is_signer = match is_signer { - false => quote! {false}, - true => quote! {true}, - }; - let meta = match f.constraints.is_mutable() { - false => quote! { anchor_lang::solana_program::instruction::AccountMeta::new_readonly }, - true => quote! { anchor_lang::solana_program::instruction::AccountMeta::new }, - }; - let name = &f.ident; - quote! { - account_metas.push(#meta(anchor_lang::Key::key(&self.#name), #is_signer)); - } - } - }) - .collect(); - - let account_struct_infos: Vec = accs - .fields - .iter() - .map(|f: &AccountField| match f { - AccountField::CompositeField(s) => { - let name = &s.ident; - quote! { - account_infos.extend(anchor_lang::ToAccountInfos::to_account_infos(&self.#name)); - } - } - AccountField::Field(f) => { - let name = &f.ident; - quote! { - account_infos.push(anchor_lang::ToAccountInfo::to_account_info(&self.#name)); - } - } - }) - .collect(); - - // Re-export all composite account structs (i.e. other structs deriving - // accounts embedded into this struct. Required because, these embedded - // structs are *not* visible from the #[program] macro, which is responsible - // for generating the `accounts` mod, which aggregates all the the generated - // accounts used for structs. - let re_exports: Vec = { - // First, dedup the exports. - let mut re_exports = std::collections::HashSet::new(); - for f in accs.fields.iter().filter_map(|f: &AccountField| match f { - AccountField::CompositeField(s) => Some(s), - AccountField::Field(_) => None, - }) { - re_exports.insert(format!( - "__cpi_client_accounts_{0}::{1}", - f.symbol.to_snake_case(), - f.symbol, - )); - } - - re_exports - .iter() - .map(|symbol: &String| { - let symbol: proc_macro2::TokenStream = symbol.parse().unwrap(); - quote! { - pub use #symbol; - } - }) - .collect() - }; - let generics = if account_struct_fields.is_empty() { - quote! {} - } else { - quote! {<'info>} - }; - let struct_doc = proc_macro2::TokenStream::from_str(&format!( - "#[doc = \" Generated CPI struct of the accounts for [`{}`].\"]", - name - )) - .unwrap(); - quote! { - /// An internal, Anchor generated module. This is used (as an - /// implementation detail), to generate a CPI struct for a given - /// `#[derive(Accounts)]` implementation, where each field is an - /// AccountInfo. - /// - /// To access the struct in this module, one should use the sibling - /// [`cpi::accounts`] module (also generated), which re-exports this. - pub(crate) mod #account_mod_name { - use super::*; - - #(#re_exports)* - - #struct_doc - pub struct #name #generics { - #(#account_struct_fields),* - } - - #[automatically_derived] - impl #generics anchor_lang::ToAccountMetas for #name #generics { - fn to_account_metas(&self, is_signer: Option) -> Vec { - let mut account_metas = vec![]; - #(#account_struct_metas)* - account_metas - } - } - - #[automatically_derived] - impl<'info> anchor_lang::ToAccountInfos<'info> for #name #generics { - fn to_account_infos(&self) -> Vec> { - let mut account_infos = vec![]; - #(#account_struct_infos)* - account_infos - } - } - } - } -} diff --git a/lang/syn/src/codegen/accounts/constraints.rs b/lang/syn/src/codegen/accounts/constraints.rs deleted file mode 100644 index 75f4a00f..00000000 --- a/lang/syn/src/codegen/accounts/constraints.rs +++ /dev/null @@ -1,871 +0,0 @@ -use crate::*; -use proc_macro2_diagnostics::SpanDiagnosticExt; -use quote::quote; -use syn::Expr; - -pub fn generate(f: &Field) -> proc_macro2::TokenStream { - let constraints = linearize(&f.constraints); - - let rent = constraints - .iter() - .any(|c| matches!(c, Constraint::RentExempt(ConstraintRentExempt::Enforce))) - .then(|| quote! { let __anchor_rent = Rent::get()?; }) - .unwrap_or_else(|| quote! {}); - - let checks: Vec = constraints - .iter() - .map(|c| generate_constraint(f, c)) - .collect(); - - quote! { - #rent - #(#checks)* - } -} - -pub fn generate_composite(f: &CompositeField) -> proc_macro2::TokenStream { - let checks: Vec = linearize(&f.constraints) - .iter() - .filter_map(|c| match c { - Constraint::Raw(_) => Some(c), - Constraint::Literal(_) => Some(c), - _ => panic!("Invariant violation: composite constraints can only be raw or literals"), - }) - .map(|c| generate_constraint_composite(f, c)) - .collect(); - quote! { - #(#checks)* - } -} - -// Linearizes the constraint group so that constraints with dependencies -// run after those without. -pub fn linearize(c_group: &ConstraintGroup) -> Vec { - let ConstraintGroup { - init, - zeroed, - mutable, - signer, - has_one, - literal, - raw, - owner, - rent_exempt, - seeds, - executable, - state, - close, - address, - associated_token, - token_account, - mint, - } = c_group.clone(); - - let mut constraints = Vec::new(); - - if let Some(c) = zeroed { - constraints.push(Constraint::Zeroed(c)); - } - if let Some(c) = init { - constraints.push(Constraint::Init(c)); - } - if let Some(c) = seeds { - constraints.push(Constraint::Seeds(c)); - } - if let Some(c) = associated_token { - constraints.push(Constraint::AssociatedToken(c)); - } - if let Some(c) = mutable { - constraints.push(Constraint::Mut(c)); - } - if let Some(c) = signer { - constraints.push(Constraint::Signer(c)); - } - constraints.append(&mut has_one.into_iter().map(Constraint::HasOne).collect()); - constraints.append(&mut literal.into_iter().map(Constraint::Literal).collect()); - constraints.append(&mut raw.into_iter().map(Constraint::Raw).collect()); - if let Some(c) = owner { - constraints.push(Constraint::Owner(c)); - } - if let Some(c) = rent_exempt { - constraints.push(Constraint::RentExempt(c)); - } - if let Some(c) = executable { - constraints.push(Constraint::Executable(c)); - } - if let Some(c) = state { - constraints.push(Constraint::State(c)); - } - if let Some(c) = close { - constraints.push(Constraint::Close(c)); - } - if let Some(c) = address { - constraints.push(Constraint::Address(c)); - } - if let Some(c) = token_account { - constraints.push(Constraint::TokenAccount(c)); - } - if let Some(c) = mint { - constraints.push(Constraint::Mint(c)); - } - constraints -} - -fn generate_constraint(f: &Field, c: &Constraint) -> proc_macro2::TokenStream { - match c { - Constraint::Init(c) => generate_constraint_init(f, c), - Constraint::Zeroed(c) => generate_constraint_zeroed(f, c), - Constraint::Mut(c) => generate_constraint_mut(f, c), - Constraint::HasOne(c) => generate_constraint_has_one(f, c), - Constraint::Signer(c) => generate_constraint_signer(f, c), - Constraint::Literal(c) => generate_constraint_literal(&f.ident, c), - Constraint::Raw(c) => generate_constraint_raw(&f.ident, c), - Constraint::Owner(c) => generate_constraint_owner(f, c), - Constraint::RentExempt(c) => generate_constraint_rent_exempt(f, c), - Constraint::Seeds(c) => generate_constraint_seeds(f, c), - Constraint::Executable(c) => generate_constraint_executable(f, c), - Constraint::State(c) => generate_constraint_state(f, c), - Constraint::Close(c) => generate_constraint_close(f, c), - Constraint::Address(c) => generate_constraint_address(f, c), - Constraint::AssociatedToken(c) => generate_constraint_associated_token(f, c), - Constraint::TokenAccount(c) => generate_constraint_token_account(f, c), - Constraint::Mint(c) => generate_constraint_mint(f, c), - } -} - -fn generate_constraint_composite(f: &CompositeField, c: &Constraint) -> proc_macro2::TokenStream { - match c { - Constraint::Raw(c) => generate_constraint_raw(&f.ident, c), - Constraint::Literal(c) => generate_constraint_literal(&f.ident, c), - _ => panic!("Invariant violation"), - } -} - -fn generate_constraint_address(f: &Field, c: &ConstraintAddress) -> proc_macro2::TokenStream { - let field = &f.ident; - let addr = &c.address; - let error = generate_custom_error( - field, - &c.error, - quote! { ConstraintAddress }, - &Some(&(quote! { actual }, quote! { expected })), - ); - quote! { - { - let actual = #field.key(); - let expected = #addr; - if actual != expected { - return #error; - } - } - } -} - -pub fn generate_constraint_init(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::TokenStream { - generate_constraint_init_group(f, c) -} - -pub fn generate_constraint_zeroed(f: &Field, _c: &ConstraintZeroed) -> proc_macro2::TokenStream { - let field = &f.ident; - let name_str = field.to_string(); - let ty_decl = f.ty_decl(); - let from_account_info = f.from_account_info(None, false); - quote! { - let #field: #ty_decl = { - let mut __data: &[u8] = &#field.try_borrow_data()?; - let mut __disc_bytes = [0u8; 8]; - __disc_bytes.copy_from_slice(&__data[..8]); - let __discriminator = u64::from_le_bytes(__disc_bytes); - if __discriminator != 0 { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintZero).with_account_name(#name_str)); - } - #from_account_info - }; - } -} - -pub fn generate_constraint_close(f: &Field, c: &ConstraintClose) -> proc_macro2::TokenStream { - let field = &f.ident; - let name_str = field.to_string(); - let target = &c.sol_dest; - quote! { - if #field.key() == #target.key() { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintClose).with_account_name(#name_str)); - } - } -} - -pub fn generate_constraint_mut(f: &Field, c: &ConstraintMut) -> proc_macro2::TokenStream { - let ident = &f.ident; - let error = generate_custom_error(ident, &c.error, quote! { ConstraintMut }, &None); - quote! { - if !#ident.to_account_info().is_writable { - return #error; - } - } -} - -pub fn generate_constraint_has_one(f: &Field, c: &ConstraintHasOne) -> proc_macro2::TokenStream { - let target = c.join_target.clone(); - let ident = &f.ident; - let field = match &f.ty { - Ty::Loader(_) => quote! {#ident.load()?}, - Ty::AccountLoader(_) => quote! {#ident.load()?}, - _ => quote! {#ident}, - }; - let error = generate_custom_error( - ident, - &c.error, - quote! { ConstraintHasOne }, - &Some(&(quote! { my_key }, quote! { target_key })), - ); - quote! { - { - let my_key = #field.#target; - let target_key = #target.key(); - if my_key != target_key { - return #error; - } - } - } -} - -pub fn generate_constraint_signer(f: &Field, c: &ConstraintSigner) -> proc_macro2::TokenStream { - let ident = &f.ident; - let info = match f.ty { - Ty::AccountInfo => quote! { #ident }, - Ty::ProgramAccount(_) => quote! { #ident.to_account_info() }, - Ty::Account(_) => quote! { #ident.to_account_info() }, - Ty::Loader(_) => quote! { #ident.to_account_info() }, - Ty::AccountLoader(_) => quote! { #ident.to_account_info() }, - Ty::CpiAccount(_) => quote! { #ident.to_account_info() }, - _ => panic!("Invalid syntax: signer cannot be specified."), - }; - let error = generate_custom_error(ident, &c.error, quote! { ConstraintSigner }, &None); - quote! { - if !#info.is_signer { - return #error; - } - } -} - -pub fn generate_constraint_literal( - ident: &Ident, - c: &ConstraintLiteral, -) -> proc_macro2::TokenStream { - let name_str = ident.to_string(); - let lit: proc_macro2::TokenStream = { - let lit = &c.lit; - let constraint = lit.value().replace('\"', ""); - let message = format!( - "Deprecated. Should be used with constraint: #[account(constraint = {})]", - constraint, - ); - lit.span().warning(message).emit_as_item_tokens(); - constraint.parse().unwrap() - }; - quote! { - if !(#lit) { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::Deprecated).with_account_name(#name_str)); - } - } -} - -pub fn generate_constraint_raw(ident: &Ident, c: &ConstraintRaw) -> proc_macro2::TokenStream { - let raw = &c.raw; - let error = generate_custom_error(ident, &c.error, quote! { ConstraintRaw }, &None); - quote! { - if !(#raw) { - return #error; - } - } -} - -pub fn generate_constraint_owner(f: &Field, c: &ConstraintOwner) -> proc_macro2::TokenStream { - let ident = &f.ident; - let owner_address = &c.owner_address; - let error = generate_custom_error( - ident, - &c.error, - quote! { ConstraintOwner }, - &Some(&(quote! { *my_owner }, quote! { owner_address })), - ); - quote! { - { - let my_owner = AsRef::::as_ref(&#ident).owner; - let owner_address = #owner_address; - if my_owner != &owner_address { - return #error; - } - } - } -} - -pub fn generate_constraint_rent_exempt( - f: &Field, - c: &ConstraintRentExempt, -) -> proc_macro2::TokenStream { - let ident = &f.ident; - let name_str = ident.to_string(); - let info = quote! { - #ident.to_account_info() - }; - match c { - ConstraintRentExempt::Skip => quote! {}, - ConstraintRentExempt::Enforce => quote! { - if !__anchor_rent.is_exempt(#info.lamports(), #info.try_data_len()?) { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintRentExempt).with_account_name(#name_str)); - } - }, - } -} - -fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::TokenStream { - let field = &f.ident; - let name_str = f.ident.to_string(); - let ty_decl = f.ty_decl(); - let if_needed = if c.if_needed { - quote! {true} - } else { - quote! {false} - }; - let space = &c.space; - - // Payer for rent exemption. - let payer = { - let p = &c.payer; - quote! { - let payer = #p.to_account_info(); - } - }; - - // Convert from account info to account context wrapper type. - let from_account_info = f.from_account_info(Some(&c.kind), true); - let from_account_info_unchecked = f.from_account_info(Some(&c.kind), false); - - // PDA bump seeds. - let (find_pda, seeds_with_bump) = match &c.seeds { - None => (quote! {}, quote! {}), - Some(c) => { - let seeds = &mut c.seeds.clone(); - - // If the seeds came with a trailing comma, we need to chop it off - // before we interpolate them below. - if let Some(pair) = seeds.pop() { - seeds.push_value(pair.into_value()); - } - - let maybe_seeds_plus_comma = (!seeds.is_empty()).then(|| { - quote! { #seeds, } - }); - - ( - quote! { - let (__pda_address, __bump) = Pubkey::find_program_address( - &[#maybe_seeds_plus_comma], - program_id, - ); - __bumps.insert(#name_str.to_string(), __bump); - }, - quote! { - &[ - #maybe_seeds_plus_comma - &[__bump][..] - ][..] - }, - ) - } - }; - - match &c.kind { - InitKind::Token { owner, mint } => { - let create_account = generate_create_account( - field, - quote! {anchor_spl::token::TokenAccount::LEN}, - quote! {&token_program.key()}, - seeds_with_bump, - ); - quote! { - // Define the bump and pda variable. - #find_pda - - let #field: #ty_decl = { - if !#if_needed || AsRef::::as_ref(&#field).owner == &anchor_lang::solana_program::system_program::ID { - // Define payer variable. - #payer - - // Create the account with the system program. - #create_account - - // Initialize the token account. - let cpi_program = token_program.to_account_info(); - let accounts = anchor_spl::token::InitializeAccount { - account: #field.to_account_info(), - mint: #mint.to_account_info(), - authority: #owner.to_account_info(), - rent: rent.to_account_info(), - }; - let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, accounts); - anchor_spl::token::initialize_account(cpi_ctx)?; - } - - let pa: #ty_decl = #from_account_info_unchecked; - if #if_needed { - if pa.mint != #mint.key() { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenMint).with_account_name(#name_str).with_pubkeys((pa.mint, #mint.key()))); - } - if pa.owner != #owner.key() { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenOwner).with_account_name(#name_str).with_pubkeys((pa.owner, #owner.key()))); - } - } - pa - }; - } - } - InitKind::AssociatedToken { owner, mint } => { - quote! { - // Define the bump and pda variable. - #find_pda - - let #field: #ty_decl = { - if !#if_needed || AsRef::::as_ref(&#field).owner == &anchor_lang::solana_program::system_program::ID { - #payer - - let cpi_program = associated_token_program.to_account_info(); - let cpi_accounts = anchor_spl::associated_token::Create { - payer: payer.to_account_info(), - associated_token: #field.to_account_info(), - authority: #owner.to_account_info(), - mint: #mint.to_account_info(), - system_program: system_program.to_account_info(), - token_program: token_program.to_account_info(), - rent: rent.to_account_info(), - }; - let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, cpi_accounts); - anchor_spl::associated_token::create(cpi_ctx)?; - } - let pa: #ty_decl = #from_account_info_unchecked; - if #if_needed { - if pa.mint != #mint.key() { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenMint).with_account_name(#name_str).with_pubkeys((pa.mint, #mint.key()))); - } - if pa.owner != #owner.key() { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenOwner).with_account_name(#name_str).with_pubkeys((pa.owner, #owner.key()))); - } - - if pa.key() != anchor_spl::associated_token::get_associated_token_address(&#owner.key(), &#mint.key()) { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::AccountNotAssociatedTokenAccount).with_account_name(#name_str)); - } - } - pa - }; - } - } - InitKind::Mint { - owner, - decimals, - freeze_authority, - } => { - let create_account = generate_create_account( - field, - quote! {anchor_spl::token::Mint::LEN}, - quote! {&token_program.key()}, - seeds_with_bump, - ); - let freeze_authority = match freeze_authority { - Some(fa) => quote! { Option::<&anchor_lang::prelude::Pubkey>::Some(&#fa.key()) }, - None => quote! { Option::<&anchor_lang::prelude::Pubkey>::None }, - }; - quote! { - // Define the bump and pda variable. - #find_pda - - let #field: #ty_decl = { - if !#if_needed || AsRef::::as_ref(&#field).owner == &anchor_lang::solana_program::system_program::ID { - // Define payer variable. - #payer - - // Create the account with the system program. - #create_account - - // Initialize the mint account. - let cpi_program = token_program.to_account_info(); - let accounts = anchor_spl::token::InitializeMint { - mint: #field.to_account_info(), - rent: rent.to_account_info(), - }; - let cpi_ctx = anchor_lang::context::CpiContext::new(cpi_program, accounts); - anchor_spl::token::initialize_mint(cpi_ctx, #decimals, &#owner.key(), #freeze_authority)?; - } - let pa: #ty_decl = #from_account_info_unchecked; - if #if_needed { - if pa.mint_authority != anchor_lang::solana_program::program_option::COption::Some(#owner.key()) { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintMintMintAuthority).with_account_name(#name_str)); - } - if pa.freeze_authority - .as_ref() - .map(|fa| #freeze_authority.as_ref().map(|expected_fa| fa != *expected_fa).unwrap_or(true)) - .unwrap_or(#freeze_authority.is_some()) { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintMintFreezeAuthority).with_account_name(#name_str)); - } - if pa.decimals != #decimals { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintMintDecimals).with_account_name(#name_str).with_values((pa.decimals, #decimals))); - } - } - pa - }; - } - } - InitKind::Program { owner } => { - // Define the space variable. - let space = quote! {let space = #space;}; - - // Define the owner of the account being created. If not specified, - // default to the currently executing program. - let owner = match owner { - None => quote! { - program_id - }, - Some(o) => quote! { - &#o - }, - }; - - // CPI to the system program to create the account. - let create_account = - generate_create_account(field, quote! {space}, owner.clone(), seeds_with_bump); - - // Put it all together. - quote! { - // Define the bump variable. - #find_pda - - let #field = { - let actual_field = #field.to_account_info(); - let actual_owner = actual_field.owner; - - // Define the account space variable. - #space - - // Create the account. Always do this in the event - // if needed is not specified or the system program is the owner. - let pa: #ty_decl = if !#if_needed || actual_owner == &anchor_lang::solana_program::system_program::ID { - // Define the payer variable. - #payer - - // CPI to the system program to create. - #create_account - - // Convert from account info to account context wrapper type. - #from_account_info_unchecked - } else { - // Convert from account info to account context wrapper type. - #from_account_info - }; - - // Assert the account was created correctly. - if #if_needed { - if space != actual_field.data_len() { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSpace).with_account_name(#name_str).with_values((space, actual_field.data_len()))); - } - - if actual_owner != #owner { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintOwner).with_account_name(#name_str).with_pubkeys((*actual_owner, *#owner))); - } - - { - let required_lamports = __anchor_rent.minimum_balance(space); - if pa.to_account_info().lamports() < required_lamports { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintRentExempt).with_account_name(#name_str)); - } - } - } - - // Done. - pa - }; - } - } - } -} - -fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2::TokenStream { - let name = &f.ident; - let name_str = name.to_string(); - - let s = &mut c.seeds.clone(); - - let deriving_program_id = c - .program_seed - .clone() - // If they specified a seeds::program to use when deriving the PDA, use it. - .map(|program_id| quote! { #program_id.key() }) - // Otherwise fall back to the current program's program_id. - .unwrap_or(quote! { program_id }); - - // If the seeds came with a trailing comma, we need to chop it off - // before we interpolate them below. - if let Some(pair) = s.pop() { - s.push_value(pair.into_value()); - } - - // If the bump is provided with init *and target*, then force it to be the - // canonical bump. - // - // Note that for `#[account(init, seeds)]`, find_program_address has already - // been run in the init constraint. - if c.is_init && c.bump.is_some() { - let b = c.bump.as_ref().unwrap(); - quote! { - if #name.key() != __pda_address { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_pubkeys((#name.key(), __pda_address))); - } - if __bump != #b { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_values((__bump, #b))); - } - } - } - // Init seeds but no bump. We already used the canonical to create bump so - // just check the address. - // - // Note that for `#[account(init, seeds)]`, find_program_address has already - // been run in the init constraint. - else if c.is_init { - quote! { - if #name.key() != __pda_address { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_pubkeys((#name.key(), __pda_address))); - } - } - } - // No init. So we just check the address. - else { - let maybe_seeds_plus_comma = (!s.is_empty()).then(|| { - quote! { #s, } - }); - - let define_pda = match c.bump.as_ref() { - // Bump target not given. Find it. - None => quote! { - let (__pda_address, __bump) = Pubkey::find_program_address( - &[#maybe_seeds_plus_comma], - &#deriving_program_id, - ); - __bumps.insert(#name_str.to_string(), __bump); - }, - // Bump target given. Use it. - Some(b) => quote! { - let __pda_address = Pubkey::create_program_address( - &[#maybe_seeds_plus_comma &[#b][..]], - &#deriving_program_id, - ).map_err(|_| anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str))?; - }, - }; - quote! { - // Define the PDA. - #define_pda - - // Check it. - if #name.key() != __pda_address { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_pubkeys((#name.key(), __pda_address))); - } - } - } -} - -fn generate_constraint_associated_token( - f: &Field, - c: &ConstraintAssociatedToken, -) -> proc_macro2::TokenStream { - let name = &f.ident; - let name_str = name.to_string(); - let wallet_address = &c.wallet; - let spl_token_mint_address = &c.mint; - quote! { - { - let my_owner = #name.owner; - let wallet_address = #wallet_address.key(); - if my_owner != wallet_address { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintTokenOwner).with_account_name(#name_str).with_pubkeys((my_owner, wallet_address))); - } - let __associated_token_address = anchor_spl::associated_token::get_associated_token_address(&wallet_address, &#spl_token_mint_address.key()); - let my_key = #name.key(); - if my_key != __associated_token_address { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintAssociated).with_account_name(#name_str).with_pubkeys((my_key, __associated_token_address))); - } - } - } -} - -fn generate_constraint_token_account( - f: &Field, - c: &ConstraintTokenAccountGroup, -) -> proc_macro2::TokenStream { - let name = &f.ident; - let authority_check = match &c.authority { - Some(authority) => { - quote! { if #name.owner != #authority.key() { return Err(anchor_lang::error::ErrorCode::ConstraintTokenOwner.into()); } } - } - None => quote! {}, - }; - let mint_check = match &c.mint { - Some(mint) => { - quote! { if #name.mint != #mint.key() { return Err(anchor_lang::error::ErrorCode::ConstraintTokenMint.into()); } } - } - None => quote! {}, - }; - quote! { - #authority_check - #mint_check - } -} - -fn generate_constraint_mint(f: &Field, c: &ConstraintTokenMintGroup) -> proc_macro2::TokenStream { - let name = &f.ident; - - let decimal_check = match &c.decimals { - Some(decimals) => quote! { - if #name.decimals != #decimals { - return Err(anchor_lang::error::ErrorCode::ConstraintMintDecimals.into()); - } - }, - None => quote! {}, - }; - let mint_authority_check = match &c.mint_authority { - Some(mint_authority) => quote! { - if #name.mint_authority != anchor_lang::solana_program::program_option::COption::Some(anchor_lang::Key::key(&#mint_authority)) { - return Err(anchor_lang::error::ErrorCode::ConstraintMintMintAuthority.into()); - } - }, - None => quote! {}, - }; - let freeze_authority_check = match &c.freeze_authority { - Some(freeze_authority) => quote! { - if #name.freeze_authority != anchor_lang::solana_program::program_option::COption::Some(anchor_lang::Key::key(&#freeze_authority)) { - return Err(anchor_lang::error::ErrorCode::ConstraintMintFreezeAuthority.into()); - } - }, - None => quote! {}, - }; - quote! { - #decimal_check - #mint_authority_check - #freeze_authority_check - } -} - -// Generated code to create an account with with system program with the -// given `space` amount of data, owned by `owner`. -// -// `seeds_with_nonce` should be given for creating PDAs. Otherwise it's an -// empty stream. -pub fn generate_create_account( - field: &Ident, - space: proc_macro2::TokenStream, - owner: proc_macro2::TokenStream, - seeds_with_nonce: proc_macro2::TokenStream, -) -> proc_macro2::TokenStream { - quote! { - // If the account being initialized already has lamports, then - // return them all back to the payer so that the account has - // zero lamports when the system program's create instruction - // is eventually called. - let __current_lamports = #field.lamports(); - if __current_lamports == 0 { - // Create the token account with right amount of lamports and space, and the correct owner. - let lamports = __anchor_rent.minimum_balance(#space); - let cpi_accounts = anchor_lang::system_program::CreateAccount { - from: payer.to_account_info(), - to: #field.to_account_info() - }; - let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts); - anchor_lang::system_program::create_account(cpi_context.with_signer(&[#seeds_with_nonce]), lamports, #space as u64, #owner)?; - } else { - // Fund the account for rent exemption. - let required_lamports = __anchor_rent - .minimum_balance(#space) - .max(1) - .saturating_sub(__current_lamports); - if required_lamports > 0 { - let cpi_accounts = anchor_lang::system_program::Transfer { - from: payer.to_account_info(), - to: #field.to_account_info(), - }; - let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts); - anchor_lang::system_program::transfer(cpi_context, required_lamports)?; - } - // Allocate space. - let cpi_accounts = anchor_lang::system_program::Allocate { - account_to_allocate: #field.to_account_info() - }; - let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts); - anchor_lang::system_program::allocate(cpi_context.with_signer(&[#seeds_with_nonce]), #space as u64)?; - // Assign to the spl token program. - let cpi_accounts = anchor_lang::system_program::Assign { - account_to_assign: #field.to_account_info() - }; - let cpi_context = anchor_lang::context::CpiContext::new(system_program.to_account_info(), cpi_accounts); - anchor_lang::system_program::assign(cpi_context.with_signer(&[#seeds_with_nonce]), #owner)?; - } - } -} - -pub fn generate_constraint_executable( - f: &Field, - _c: &ConstraintExecutable, -) -> proc_macro2::TokenStream { - let name = &f.ident; - let name_str = name.to_string(); - quote! { - if !#name.to_account_info().executable { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintExecutable).with_account_name(#name_str)); - } - } -} - -pub fn generate_constraint_state(f: &Field, c: &ConstraintState) -> proc_macro2::TokenStream { - let program_target = c.program_target.clone(); - let ident = &f.ident; - let name_str = ident.to_string(); - let account_ty = match &f.ty { - Ty::CpiState(ty) => &ty.account_type_path, - _ => panic!("Invalid state constraint"), - }; - quote! { - // Checks the given state account is the canonical state account for - // the target program. - if #ident.key() != anchor_lang::accounts::cpi_state::CpiState::<#account_ty>::address(&#program_target.key()) { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintState).with_account_name(#name_str)); - } - if AsRef::::as_ref(&#ident).owner != &#program_target.key() { - return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintState).with_account_name(#name_str)); - } - } -} - -fn generate_custom_error( - account_name: &Ident, - custom_error: &Option, - error: proc_macro2::TokenStream, - compared_values: &Option<&(proc_macro2::TokenStream, proc_macro2::TokenStream)>, -) -> proc_macro2::TokenStream { - let account_name = account_name.to_string(); - let mut error = match custom_error { - Some(error) => { - quote! { anchor_lang::error::Error::from(#error).with_account_name(#account_name) } - } - None => { - quote! { anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::#error).with_account_name(#account_name) } - } - }; - - let compared_values = match compared_values { - Some((left, right)) => quote! { .with_pubkeys((#left, #right)) }, - None => quote! {}, - }; - - error.extend(compared_values); - - quote! { - Err(#error) - } -} diff --git a/lang/syn/src/codegen/accounts/exit.rs b/lang/syn/src/codegen/accounts/exit.rs deleted file mode 100644 index 735f2256..00000000 --- a/lang/syn/src/codegen/accounts/exit.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::codegen::accounts::{generics, ParsedGenerics}; -use crate::{AccountField, AccountsStruct}; -use quote::quote; - -// Generates the `Exit` trait implementation. -pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { - let name = &accs.ident; - let ParsedGenerics { - combined_generics, - trait_generics, - struct_generics, - where_clause, - } = generics(accs); - - let on_save: Vec = accs - .fields - .iter() - .map(|af: &AccountField| match af { - AccountField::CompositeField(s) => { - let name = &s.ident; - let name_str = name.to_string(); - quote! { - anchor_lang::AccountsExit::exit(&self.#name, program_id) - .map_err(|e| e.with_account_name(#name_str))?; - } - } - AccountField::Field(f) => { - let ident = &f.ident; - let name_str = ident.to_string(); - if f.constraints.is_close() { - let close_target = &f.constraints.close.as_ref().unwrap().sol_dest; - quote! { - anchor_lang::AccountsClose::close( - &self.#ident, - self.#close_target.to_account_info(), - ).map_err(|e| e.with_account_name(#name_str))?; - } - } else { - match f.constraints.is_mutable() { - false => quote! {}, - true => quote! { - anchor_lang::AccountsExit::exit(&self.#ident, program_id) - .map_err(|e| e.with_account_name(#name_str))?; - }, - } - } - } - }) - .collect(); - quote! { - #[automatically_derived] - impl<#combined_generics> anchor_lang::AccountsExit<#trait_generics> for #name<#struct_generics> #where_clause{ - fn exit(&self, program_id: &anchor_lang::solana_program::pubkey::Pubkey) -> anchor_lang::Result<()> { - #(#on_save)* - Ok(()) - } - } - } -} diff --git a/lang/syn/src/codegen/accounts/mod.rs b/lang/syn/src/codegen/accounts/mod.rs deleted file mode 100644 index 3a239cbe..00000000 --- a/lang/syn/src/codegen/accounts/mod.rs +++ /dev/null @@ -1,102 +0,0 @@ -use crate::AccountsStruct; -use quote::quote; -use std::iter; -use syn::punctuated::Punctuated; -use syn::{ConstParam, LifetimeDef, Token, TypeParam}; -use syn::{GenericParam, PredicateLifetime, WhereClause, WherePredicate}; - -mod __client_accounts; -mod __cpi_client_accounts; -mod constraints; -mod exit; -mod to_account_infos; -mod to_account_metas; -mod try_accounts; - -pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { - let impl_try_accounts = try_accounts::generate(accs); - let impl_to_account_infos = to_account_infos::generate(accs); - let impl_to_account_metas = to_account_metas::generate(accs); - let impl_exit = exit::generate(accs); - - let __client_accounts_mod = __client_accounts::generate(accs); - let __cpi_client_accounts_mod = __cpi_client_accounts::generate(accs); - - quote! { - #impl_try_accounts - #impl_to_account_infos - #impl_to_account_metas - #impl_exit - - #__client_accounts_mod - #__cpi_client_accounts_mod - } -} - -fn generics(accs: &AccountsStruct) -> ParsedGenerics { - let trait_lifetime = accs - .generics - .lifetimes() - .next() - .cloned() - .unwrap_or_else(|| syn::parse_str("'info").expect("Could not parse lifetime")); - - let mut where_clause = accs.generics.where_clause.clone().unwrap_or(WhereClause { - where_token: Default::default(), - predicates: Default::default(), - }); - for lifetime in accs.generics.lifetimes().map(|def| &def.lifetime) { - where_clause - .predicates - .push(WherePredicate::Lifetime(PredicateLifetime { - lifetime: lifetime.clone(), - colon_token: Default::default(), - bounds: iter::once(trait_lifetime.lifetime.clone()).collect(), - })) - } - let trait_lifetime = GenericParam::Lifetime(trait_lifetime); - - ParsedGenerics { - combined_generics: if accs.generics.lifetimes().next().is_some() { - accs.generics.params.clone() - } else { - iter::once(trait_lifetime.clone()) - .chain(accs.generics.params.clone()) - .collect() - }, - trait_generics: iter::once(trait_lifetime).collect(), - struct_generics: accs - .generics - .params - .clone() - .into_iter() - .map(|param: GenericParam| match param { - GenericParam::Const(ConstParam { ident, .. }) - | GenericParam::Type(TypeParam { ident, .. }) => GenericParam::Type(TypeParam { - attrs: vec![], - ident, - colon_token: None, - bounds: Default::default(), - eq_token: None, - default: None, - }), - GenericParam::Lifetime(LifetimeDef { lifetime, .. }) => { - GenericParam::Lifetime(LifetimeDef { - attrs: vec![], - lifetime, - colon_token: None, - bounds: Default::default(), - }) - } - }) - .collect(), - where_clause, - } -} - -struct ParsedGenerics { - pub combined_generics: Punctuated, - pub trait_generics: Punctuated, - pub struct_generics: Punctuated, - pub where_clause: WhereClause, -} diff --git a/lang/syn/src/codegen/accounts/to_account_infos.rs b/lang/syn/src/codegen/accounts/to_account_infos.rs deleted file mode 100644 index 6ba14311..00000000 --- a/lang/syn/src/codegen/accounts/to_account_infos.rs +++ /dev/null @@ -1,40 +0,0 @@ -use crate::codegen::accounts::{generics, ParsedGenerics}; -use crate::{AccountField, AccountsStruct}; -use quote::quote; - -// Generates the `ToAccountInfos` trait implementation. -pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { - let name = &accs.ident; - let ParsedGenerics { - combined_generics, - trait_generics, - struct_generics, - where_clause, - } = generics(accs); - - let to_acc_infos: Vec = accs - .fields - .iter() - .map(|f: &AccountField| { - let name = match f { - AccountField::CompositeField(s) => &s.ident, - AccountField::Field(f) => &f.ident, - }; - quote! { - account_infos.extend(self.#name.to_account_infos()); - } - }) - .collect(); - quote! { - #[automatically_derived] - impl<#combined_generics> anchor_lang::ToAccountInfos<#trait_generics> for #name <#struct_generics> #where_clause{ - fn to_account_infos(&self) -> Vec> { - let mut account_infos = vec![]; - - #(#to_acc_infos)* - - account_infos - } - } - } -} diff --git a/lang/syn/src/codegen/accounts/to_account_metas.rs b/lang/syn/src/codegen/accounts/to_account_metas.rs deleted file mode 100644 index f3648383..00000000 --- a/lang/syn/src/codegen/accounts/to_account_metas.rs +++ /dev/null @@ -1,42 +0,0 @@ -use crate::{AccountField, AccountsStruct}; -use quote::quote; - -// Generates the `ToAccountMetas` trait implementation. -pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { - let name = &accs.ident; - - let to_acc_metas: Vec = accs - .fields - .iter() - .map(|f: &AccountField| { - let (name, is_signer) = match f { - AccountField::CompositeField(s) => (&s.ident, quote! {None}), - AccountField::Field(f) => { - let is_signer = match f.constraints.is_signer() { - false => quote! {None}, - true => quote! {Some(true)}, - }; - (&f.ident, is_signer) - } - }; - quote! { - account_metas.extend(self.#name.to_account_metas(#is_signer)); - } - }) - .collect(); - - let (impl_gen, ty_gen, where_clause) = accs.generics.split_for_impl(); - - quote! { - #[automatically_derived] - impl #impl_gen anchor_lang::ToAccountMetas for #name #ty_gen #where_clause{ - fn to_account_metas(&self, is_signer: Option) -> Vec { - let mut account_metas = vec![]; - - #(#to_acc_metas)* - - account_metas - } - } - } -} diff --git a/lang/syn/src/codegen/accounts/try_accounts.rs b/lang/syn/src/codegen/accounts/try_accounts.rs deleted file mode 100644 index b193dcba..00000000 --- a/lang/syn/src/codegen/accounts/try_accounts.rs +++ /dev/null @@ -1,178 +0,0 @@ -use crate::codegen::accounts::{constraints, generics, ParsedGenerics}; -use crate::{AccountField, AccountsStruct}; -use quote::quote; -use syn::Expr; - -// Generates the `Accounts` trait implementation. -pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream { - let name = &accs.ident; - let ParsedGenerics { - combined_generics, - trait_generics, - struct_generics, - where_clause, - } = generics(accs); - - // Deserialization for each field - let deser_fields: Vec = accs - .fields - .iter() - .map(|af: &AccountField| { - match af { - AccountField::CompositeField(s) => { - let name = &s.ident; - let ty = &s.raw_field.ty; - quote! { - #[cfg(feature = "anchor-debug")] - ::solana_program::log::sol_log(stringify!(#name)); - let #name: #ty = anchor_lang::Accounts::try_accounts(program_id, accounts, ix_data, __bumps)?; - } - } - AccountField::Field(f) => { - // `init` and `zero` acccounts are special cased as they are - // deserialized by constraints. Here, we just take out the - // AccountInfo for later use at constraint validation time. - if is_init(af) || f.constraints.zeroed.is_some() { - let name = &f.ident; - quote!{ - if accounts.is_empty() { - return Err(anchor_lang::error::ErrorCode::AccountNotEnoughKeys.into()); - } - let #name = &accounts[0]; - *accounts = &accounts[1..]; - } - } else { - let name = f.ident.to_string(); - let typed_name = f.typed_ident(); - quote! { - #[cfg(feature = "anchor-debug")] - ::solana_program::log::sol_log(stringify!(#typed_name)); - let #typed_name = anchor_lang::Accounts::try_accounts(program_id, accounts, ix_data, __bumps) - .map_err(|e| e.with_account_name(#name))?; - } - } - } - } - }) - .collect(); - - let constraints = generate_constraints(accs); - let accounts_instance = generate_accounts_instance(accs); - - let ix_de = match &accs.instruction_api { - None => quote! {}, - Some(ix_api) => { - let strct_inner = &ix_api; - let field_names: Vec = ix_api - .iter() - .map(|expr: &Expr| match expr { - Expr::Type(expr_type) => { - let field = &expr_type.expr; - quote! { - #field - } - } - _ => panic!("Invalid instruction declaration"), - }) - .collect(); - quote! { - let mut ix_data = ix_data; - #[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)] - struct __Args { - #strct_inner - } - let __Args { - #(#field_names),* - } = __Args::deserialize(&mut ix_data) - .map_err(|_| anchor_lang::error::ErrorCode::InstructionDidNotDeserialize)?; - } - } - }; - - quote! { - #[automatically_derived] - impl<#combined_generics> anchor_lang::Accounts<#trait_generics> for #name<#struct_generics> #where_clause { - #[inline(never)] - fn try_accounts( - program_id: &anchor_lang::solana_program::pubkey::Pubkey, - accounts: &mut &[anchor_lang::solana_program::account_info::AccountInfo<'info>], - ix_data: &[u8], - __bumps: &mut std::collections::BTreeMap, - ) -> anchor_lang::Result { - // Deserialize instruction, if declared. - #ix_de - // Deserialize each account. - #(#deser_fields)* - // Execute accounts constraints. - #constraints - // Success. Return the validated accounts. - Ok(#accounts_instance) - } - } - } -} - -pub fn generate_constraints(accs: &AccountsStruct) -> proc_macro2::TokenStream { - let non_init_fields: Vec<&AccountField> = - accs.fields.iter().filter(|af| !is_init(af)).collect(); - - // Deserialization for each pda init field. This must be after - // the inital extraction from the accounts slice and before access_checks. - let init_fields: Vec = accs - .fields - .iter() - .filter_map(|af| match af { - AccountField::CompositeField(_s) => None, - AccountField::Field(f) => match is_init(af) { - false => None, - true => Some(f), - }, - }) - .map(constraints::generate) - .collect(); - - // Constraint checks for each account fields. - let access_checks: Vec = non_init_fields - .iter() - .map(|af: &&AccountField| match af { - AccountField::Field(f) => constraints::generate(f), - AccountField::CompositeField(s) => constraints::generate_composite(s), - }) - .collect(); - - quote! { - #(#init_fields)* - #(#access_checks)* - } -} - -pub fn generate_accounts_instance(accs: &AccountsStruct) -> proc_macro2::TokenStream { - let name = &accs.ident; - // Each field in the final deserialized accounts struct. - let return_tys: Vec = accs - .fields - .iter() - .map(|f: &AccountField| { - let name = match f { - AccountField::CompositeField(s) => &s.ident, - AccountField::Field(f) => &f.ident, - }; - quote! { - #name - } - }) - .collect(); - - quote! { - #name { - #(#return_tys),* - } - } -} - -fn is_init(af: &AccountField) -> bool { - match af { - AccountField::CompositeField(_s) => false, - AccountField::Field(f) => f.constraints.init.is_some(), - } -} diff --git a/lang/syn/src/codegen/error.rs b/lang/syn/src/codegen/error.rs deleted file mode 100644 index 4dea4ea2..00000000 --- a/lang/syn/src/codegen/error.rs +++ /dev/null @@ -1,100 +0,0 @@ -use crate::Error; -use quote::quote; - -pub fn generate(error: Error) -> proc_macro2::TokenStream { - let error_enum = &error.raw_enum; - let enum_name = &error.ident; - // Each arm of the `match` statement for implementing `std::fmt::Display` - // on the user defined error code. - let display_variant_dispatch: Vec = error - .raw_enum - .variants - .iter() - .enumerate() - .map(|(idx, variant)| { - let ident = &variant.ident; - let error_code = &error.codes[idx]; - let display_msg = match &error_code.msg { - None => { - quote! { - ::fmt(self, fmt) - } - } - Some(msg) => { - quote! { - write!(fmt, #msg) - } - } - }; - quote! { - #enum_name::#ident => #display_msg - } - }) - .collect(); - - // Each arm of the `match` statement for implementing the `name` function - // on the user defined error code. - let name_variant_dispatch: Vec = error - .raw_enum - .variants - .iter() - .map(|variant| { - let ident = &variant.ident; - let ident_name = ident.to_string(); - quote! { - #enum_name::#ident => #ident_name.to_string() - } - }) - .collect(); - - let offset = match error.args { - None => quote! { anchor_lang::error::ERROR_CODE_OFFSET}, - Some(args) => { - let offset = &args.offset; - quote! { #offset } - } - }; - - quote! { - #[derive(std::fmt::Debug, Clone, Copy)] - #[repr(u32)] - #error_enum - - impl #enum_name { - /// Gets the name of this [#enum_name]. - pub fn name(&self) -> String { - match self { - #(#name_variant_dispatch),* - } - } - } - - impl From<#enum_name> for u32 { - fn from(e: #enum_name) -> u32 { - e as u32 + #offset - } - } - - impl From<#enum_name> for anchor_lang::error::Error { - fn from(error_code: #enum_name) -> anchor_lang::error::Error { - anchor_lang::error::Error::from( - anchor_lang::error::AnchorError { - error_name: error_code.name(), - error_code_number: error_code.into(), - error_msg: error_code.to_string(), - error_origin: None, - compared_values: None - } - ) - } - } - - impl std::fmt::Display for #enum_name { - fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { - match self { - #(#display_variant_dispatch),* - } - } - } - } -} diff --git a/lang/syn/src/codegen/mod.rs b/lang/syn/src/codegen/mod.rs deleted file mode 100644 index a8a20d9f..00000000 --- a/lang/syn/src/codegen/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod accounts; -pub mod error; -pub mod program; diff --git a/lang/syn/src/codegen/program/accounts.rs b/lang/syn/src/codegen/program/accounts.rs deleted file mode 100644 index 2dfebbd2..00000000 --- a/lang/syn/src/codegen/program/accounts.rs +++ /dev/null @@ -1,65 +0,0 @@ -use crate::Program; -use heck::SnakeCase; -use quote::quote; - -pub fn generate(program: &Program) -> proc_macro2::TokenStream { - let mut accounts = std::collections::HashSet::new(); - - // Go through state accounts. - if let Some(state) = &program.state { - // Ctor. - if let Some((_ctor, ctor_accounts)) = &state.ctor_and_anchor { - let macro_name = format!( - "__client_accounts_{}", - ctor_accounts.to_string().to_snake_case() - ); - accounts.insert(macro_name); - } - // Methods. - if let Some((_impl_block, methods)) = &state.impl_block_and_methods { - for ix in methods { - let anchor_ident = &ix.anchor_ident; - // TODO: move to fn and share with accounts.rs. - let macro_name = format!( - "__client_accounts_{}", - anchor_ident.to_string().to_snake_case() - ); - accounts.insert(macro_name); - } - } - } - - // Go through instruction accounts. - for ix in &program.ixs { - let anchor_ident = &ix.anchor_ident; - // TODO: move to fn and share with accounts.rs. - let macro_name = format!( - "__client_accounts_{}", - anchor_ident.to_string().to_snake_case() - ); - accounts.insert(macro_name); - } - - // Build the tokens from all accounts - let account_structs: Vec = accounts - .iter() - .map(|macro_name: &String| { - let macro_name: proc_macro2::TokenStream = macro_name.parse().unwrap(); - quote! { - pub use crate::#macro_name::*; - } - }) - .collect(); - - // TODO: calculate the account size and add it as a constant field to - // each struct here. This is convenient for Rust clients. - - quote! { - /// An Anchor generated module, providing a set of structs - /// mirroring the structs deriving `Accounts`, where each field is - /// a `Pubkey`. This is useful for specifying accounts for a client. - pub mod accounts { - #(#account_structs)* - } - } -} diff --git a/lang/syn/src/codegen/program/common.rs b/lang/syn/src/codegen/program/common.rs deleted file mode 100644 index c17379a1..00000000 --- a/lang/syn/src/codegen/program/common.rs +++ /dev/null @@ -1,83 +0,0 @@ -use crate::parser; -use crate::{IxArg, State}; -use heck::CamelCase; -use quote::quote; - -// Namespace for calculating state instruction sighash signatures. -pub const SIGHASH_STATE_NAMESPACE: &str = "state"; - -// Namespace for calculating instruction sighash signatures for any instruction -// not affecting program state. -pub const SIGHASH_GLOBAL_NAMESPACE: &str = "global"; - -// We don't technically use sighash, because the input arguments aren't given. -// Rust doesn't have method overloading so no need to use the arguments. -// However, we do namespace methods in the preeimage so that we can use -// different traits with the same method name. -pub fn sighash(namespace: &str, name: &str) -> [u8; 8] { - let preimage = format!("{}:{}", namespace, name); - - let mut sighash = [0u8; 8]; - sighash.copy_from_slice(&crate::hash::hash(preimage.as_bytes()).to_bytes()[..8]); - sighash -} - -pub fn sighash_ctor() -> [u8; 8] { - sighash(SIGHASH_STATE_NAMESPACE, "new") -} - -pub fn generate_ix_variant(name: String, args: &[IxArg]) -> proc_macro2::TokenStream { - let ix_arg_names: Vec<&syn::Ident> = args.iter().map(|arg| &arg.name).collect(); - let ix_name_camel: proc_macro2::TokenStream = { - let n = name.to_camel_case(); - n.parse().unwrap() - }; - - if args.is_empty() { - quote! { - #ix_name_camel - } - } else { - quote! { - #ix_name_camel { - #(#ix_arg_names),* - } - } - } -} - -pub fn generate_ctor_args(state: &State) -> Vec { - generate_ctor_typed_args(state) - .iter() - .map(|pat_ty| *pat_ty.pat.clone()) - .collect() -} - -pub fn generate_ctor_typed_args(state: &State) -> Vec { - state - .ctor_and_anchor - .as_ref() - .map(|(ctor, _anchor_ident)| { - ctor.sig - .inputs - .iter() - .filter_map(|arg: &syn::FnArg| match arg { - syn::FnArg::Typed(pat_ty) => { - let mut arg_str = parser::tts_to_string(&pat_ty.ty); - arg_str.retain(|c| !c.is_whitespace()); - if arg_str.starts_with("Context<") { - return None; - } - Some(pat_ty.clone()) - } - _ => { - if !state.is_zero_copy { - panic!("Cannot pass self as parameter") - } - None - } - }) - .collect() - }) - .unwrap_or_default() -} diff --git a/lang/syn/src/codegen/program/cpi.rs b/lang/syn/src/codegen/program/cpi.rs deleted file mode 100644 index aa2aec56..00000000 --- a/lang/syn/src/codegen/program/cpi.rs +++ /dev/null @@ -1,207 +0,0 @@ -use crate::codegen::program::common::{generate_ix_variant, sighash, SIGHASH_GLOBAL_NAMESPACE}; -use crate::Program; -use crate::StateIx; -use heck::SnakeCase; -use quote::{quote, ToTokens}; - -pub fn generate(program: &Program) -> proc_macro2::TokenStream { - // Generate cpi methods for the state struct. - // The Ctor is not exposed via CPI, since it is a one time use function. - let state_cpi_methods: Vec = program - .state - .as_ref() - .map(|state| { - state - .impl_block_and_methods - .as_ref() - .map(|(_, methods)| { - methods - .iter() - .map(|method: &StateIx| { - let accounts_ident = &method.anchor_ident; - let ix_variant = generate_ix_variant( - method.raw_method.sig.ident.to_string(), - &method.args, - ); - let method_name = &method.ident; - let args: Vec<&syn::PatType> = - method.args.iter().map(|arg| &arg.raw_arg).collect(); - - quote! { - pub fn #method_name<'a, 'b, 'c, 'info>( - ctx: anchor_lang::context::CpiStateContext<'a, 'b, 'c, 'info, #accounts_ident<'info>>, - #(#args),* - ) -> anchor_lang::Result<()> { - let ix = { - let ix = instruction::state::#ix_variant; - let data = anchor_lang::InstructionData::data(&ix); - let accounts = ctx.to_account_metas(None); - anchor_lang::solana_program::instruction::Instruction { - program_id: crate::ID, - accounts, - data, - } - }; - let mut acc_infos = ctx.to_account_infos(); - anchor_lang::solana_program::program::invoke_signed( - &ix, - &acc_infos, - ctx.signer_seeds(), - ).map_err(Into::into) - } - } - }) - .collect() - }) - .unwrap_or_else(Vec::new) - }) - .unwrap_or_else(Vec::new); - // Generate cpi methods for global methods. - let global_cpi_methods: Vec = program - .ixs - .iter() - .map(|ix| { - let accounts_ident: proc_macro2::TokenStream = format!("crate::cpi::accounts::{}", &ix.anchor_ident.to_string()).parse().unwrap(); - let cpi_method = { - let ix_variant = generate_ix_variant(ix.raw_method.sig.ident.to_string(), &ix.args); - let method_name = &ix.ident; - let args: Vec<&syn::PatType> = ix.args.iter().map(|arg| &arg.raw_arg).collect(); - let name = &ix.raw_method.sig.ident.to_string(); - let sighash_arr = sighash(SIGHASH_GLOBAL_NAMESPACE, name); - let sighash_tts: proc_macro2::TokenStream = - format!("{:?}", sighash_arr).parse().unwrap(); - let ret_type = &ix.returns.ty.to_token_stream(); - let (method_ret, maybe_return) = match ret_type.to_string().as_str() { - "()" => (quote! {anchor_lang::Result<()> }, quote! { Ok(()) }), - _ => ( - quote! { anchor_lang::Result> }, - quote! { Ok(crate::cpi::Return::<#ret_type> { phantom: crate::cpi::PhantomData }) } - ) - }; - - quote! { - pub fn #method_name<'a, 'b, 'c, 'info>( - ctx: anchor_lang::context::CpiContext<'a, 'b, 'c, 'info, #accounts_ident<'info>>, - #(#args),* - ) -> #method_ret { - let ix = { - let ix = instruction::#ix_variant; - let mut ix_data = AnchorSerialize::try_to_vec(&ix) - .map_err(|_| anchor_lang::error::ErrorCode::InstructionDidNotSerialize)?; - let mut data = #sighash_tts.to_vec(); - data.append(&mut ix_data); - let accounts = ctx.to_account_metas(None); - anchor_lang::solana_program::instruction::Instruction { - program_id: crate::ID, - accounts, - data, - } - }; - let mut acc_infos = ctx.to_account_infos(); - anchor_lang::solana_program::program::invoke_signed( - &ix, - &acc_infos, - ctx.signer_seeds, - ).map_or_else( - |e| Err(Into::into(e)), - // Maybe handle Solana return data. - |_| { #maybe_return } - ) - } - } - }; - - cpi_method - }) - .collect(); - - let accounts = generate_accounts(program); - - quote! { - #[cfg(feature = "cpi")] - pub mod cpi { - use super::*; - use std::marker::PhantomData; - - pub mod state { - use super::*; - - #(#state_cpi_methods)* - } - - pub struct Return { - phantom: std::marker::PhantomData - } - - impl Return { - pub fn get(&self) -> T { - let (_key, data) = anchor_lang::solana_program::program::get_return_data().unwrap(); - T::try_from_slice(&data).unwrap() - } - } - - #(#global_cpi_methods)* - - #accounts - } - } -} - -pub fn generate_accounts(program: &Program) -> proc_macro2::TokenStream { - let mut accounts = std::collections::HashSet::new(); - - // Go through state accounts. - if let Some(state) = &program.state { - // Ctor. - if let Some((_ctor, ctor_accounts)) = &state.ctor_and_anchor { - let macro_name = format!( - "__cpi_client_accounts_{}", - ctor_accounts.to_string().to_snake_case() - ); - accounts.insert(macro_name); - } - // Methods. - if let Some((_impl_block, methods)) = &state.impl_block_and_methods { - for ix in methods { - let anchor_ident = &ix.anchor_ident; - // TODO: move to fn and share with accounts.rs. - let macro_name = format!( - "__cpi_client_accounts_{}", - anchor_ident.to_string().to_snake_case() - ); - accounts.insert(macro_name); - } - } - } - - // Go through instruction accounts. - for ix in &program.ixs { - let anchor_ident = &ix.anchor_ident; - // TODO: move to fn and share with accounts.rs. - let macro_name = format!( - "__cpi_client_accounts_{}", - anchor_ident.to_string().to_snake_case() - ); - accounts.insert(macro_name); - } - - // Build the tokens from all accounts - let account_structs: Vec = accounts - .iter() - .map(|macro_name: &String| { - let macro_name: proc_macro2::TokenStream = macro_name.parse().unwrap(); - quote! { - pub use crate::#macro_name::*; - } - }) - .collect(); - - quote! { - /// An Anchor generated module, providing a set of structs - /// mirroring the structs deriving `Accounts`, where each field is - /// an `AccountInfo`. This is useful for CPI. - pub mod accounts { - #(#account_structs)* - } - } -} diff --git a/lang/syn/src/codegen/program/dispatch.rs b/lang/syn/src/codegen/program/dispatch.rs deleted file mode 100644 index d8023266..00000000 --- a/lang/syn/src/codegen/program/dispatch.rs +++ /dev/null @@ -1,187 +0,0 @@ -use crate::codegen::program::common::*; -use crate::Program; -use quote::quote; - -pub fn generate(program: &Program) -> proc_macro2::TokenStream { - // Dispatch the state constructor. - let ctor_state_dispatch_arm = match &program.state { - None => quote! { /* no-op */ }, - Some(state) => match state.ctor_and_anchor.is_some() { - false => quote! {}, - true => { - let sighash_arr = sighash_ctor(); - let sighash_tts: proc_macro2::TokenStream = - format!("{:?}", sighash_arr).parse().unwrap(); - quote! { - #sighash_tts => { - __private::__state::__ctor( - program_id, - accounts, - ix_data, - ) - } - } - } - }, - }; - - // Dispatch the state impl instructions. - let state_dispatch_arms: Vec = match &program.state { - None => vec![], - Some(s) => s - .impl_block_and_methods - .as_ref() - .map(|(_impl_block, methods)| { - methods - .iter() - .map(|ix: &crate::StateIx| { - let name = &ix.raw_method.sig.ident.to_string(); - let ix_method_name: proc_macro2::TokenStream = - { format!("__{}", name).parse().unwrap() }; - let sighash_arr = sighash(SIGHASH_STATE_NAMESPACE, name); - let sighash_tts: proc_macro2::TokenStream = - format!("{:?}", sighash_arr).parse().unwrap(); - quote! { - #sighash_tts => { - __private::__state::#ix_method_name( - program_id, - accounts, - ix_data, - ) - } - } - }) - .collect() - }) - .unwrap_or_default(), - }; - - // Dispatch all trait interface implementations. - let trait_dispatch_arms: Vec = match &program.state { - None => vec![], - Some(s) => s - .interfaces - .as_ref() - .map(|interfaces| { - interfaces - .iter() - .flat_map(|iface: &crate::StateInterface| { - iface - .methods - .iter() - .map(|m: &crate::StateIx| { - let sighash_arr = sighash(&iface.trait_name, &m.ident.to_string()); - let sighash_tts: proc_macro2::TokenStream = - format!("{:?}", sighash_arr).parse().unwrap(); - let name = &m.raw_method.sig.ident.to_string(); - let ix_method_name: proc_macro2::TokenStream = - format!("__{}_{}", iface.trait_name, name).parse().unwrap(); - quote! { - #sighash_tts => { - __private::__interface::#ix_method_name( - program_id, - accounts, - ix_data, - ) - } - } - }) - .collect::>() - }) - .collect() - }) - .unwrap_or_default(), - }; - - // Dispatch all global instructions. - let global_dispatch_arms: Vec = program - .ixs - .iter() - .map(|ix| { - let ix_method_name = &ix.raw_method.sig.ident; - let sighash_arr = sighash(SIGHASH_GLOBAL_NAMESPACE, &ix_method_name.to_string()); - let sighash_tts: proc_macro2::TokenStream = - format!("{:?}", sighash_arr).parse().unwrap(); - quote! { - #sighash_tts => { - __private::__global::#ix_method_name( - program_id, - accounts, - ix_data, - ) - } - } - }) - .collect(); - let fallback_fn = gen_fallback(program).unwrap_or(quote! { - Err(anchor_lang::error::ErrorCode::InstructionFallbackNotFound.into()) - }); - quote! { - /// Performs method dispatch. - /// - /// Each method in an anchor program is uniquely defined by a namespace - /// and a rust identifier (i.e., the name given to the method). These - /// two pieces can be combined to creater a method identifier, - /// specifically, Anchor uses - /// - /// Sha256("::")[..8], - /// - /// where the namespace can be one of three types. 1) "global" for a - /// regular instruction, 2) "state" for a state struct instruction - /// handler and 3) a trait namespace (used in combination with the - /// `#[interface]` attribute), which is defined by the trait name, e.. - /// `MyTrait`. - /// - /// With this 8 byte identifier, Anchor performs method dispatch, - /// matching the given 8 byte identifier to the associated method - /// handler, which leads to user defined code being eventually invoked. - fn dispatch( - program_id: &Pubkey, - accounts: &[AccountInfo], - data: &[u8], - ) -> anchor_lang::Result<()> { - // Split the instruction data into the first 8 byte method - // identifier (sighash) and the serialized instruction data. - let mut ix_data: &[u8] = data; - let sighash: [u8; 8] = { - let mut sighash: [u8; 8] = [0; 8]; - sighash.copy_from_slice(&ix_data[..8]); - ix_data = &ix_data[8..]; - sighash - }; - - // If the method identifier is the IDL tag, then execute an IDL - // instruction, injected into all Anchor programs. - if cfg!(not(feature = "no-idl")) { - if sighash == anchor_lang::idl::IDL_IX_TAG.to_le_bytes() { - return __private::__idl::__idl_dispatch( - program_id, - accounts, - &ix_data, - ); - } - } - - match sighash { - #ctor_state_dispatch_arm - #(#state_dispatch_arms)* - #(#trait_dispatch_arms)* - #(#global_dispatch_arms)* - _ => { - #fallback_fn - } - } - } - } -} - -pub fn gen_fallback(program: &Program) -> Option { - program.fallback_fn.as_ref().map(|fallback_fn| { - let program_name = &program.name; - let method = &fallback_fn.raw_method; - let fn_name = &method.sig.ident; - quote! { - #program_name::#fn_name(program_id, accounts, data) - } - }) -} diff --git a/lang/syn/src/codegen/program/entry.rs b/lang/syn/src/codegen/program/entry.rs deleted file mode 100644 index 4efa97d9..00000000 --- a/lang/syn/src/codegen/program/entry.rs +++ /dev/null @@ -1,91 +0,0 @@ -use crate::program_codegen::dispatch; -use crate::Program; -use heck::CamelCase; -use quote::quote; - -pub fn generate(program: &Program) -> proc_macro2::TokenStream { - let name: proc_macro2::TokenStream = program.name.to_string().to_camel_case().parse().unwrap(); - let fallback_maybe = dispatch::gen_fallback(program).unwrap_or(quote! { - Err(anchor_lang::error::ErrorCode::InstructionMissing.into()) - }); - quote! { - #[cfg(not(feature = "no-entrypoint"))] - anchor_lang::solana_program::entrypoint!(entry); - /// The Anchor codegen exposes a programming model where a user defines - /// a set of methods inside of a `#[program]` module in a way similar - /// to writing RPC request handlers. The macro then generates a bunch of - /// code wrapping these user defined methods into something that can be - /// executed on Solana. - /// - /// These methods fall into one of three categories, each of which - /// can be considered a different "namespace" of the program. - /// - /// 1) Global methods - regular methods inside of the `#[program]`. - /// 2) State methods - associated methods inside a `#[state]` struct. - /// 3) Interface methods - methods inside a strait struct's - /// implementation of an `#[interface]` trait. - /// - /// Care must be taken by the codegen to prevent collisions between - /// methods in these different namespaces. For this reason, Anchor uses - /// a variant of sighash to perform method dispatch, rather than - /// something like a simple enum variant discriminator. - /// - /// The execution flow of the generated code can be roughly outlined: - /// - /// * Start program via the entrypoint. - /// * Strip method identifier off the first 8 bytes of the instruction - /// data and invoke the identified method. The method identifier - /// is a variant of sighash. See docs.rs for `anchor_lang` for details. - /// * If the method identifier is an IDL identifier, execute the IDL - /// instructions, which are a special set of hardcoded instructions - /// baked into every Anchor program. Then exit. - /// * Otherwise, the method identifier is for a user defined - /// instruction, i.e., one of the methods in the user defined - /// `#[program]` module. Perform method dispatch, i.e., execute the - /// big match statement mapping method identifier to method handler - /// wrapper. - /// * Run the method handler wrapper. This wraps the code the user - /// actually wrote, deserializing the accounts, constructing the - /// context, invoking the user's code, and finally running the exit - /// routine, which typically persists account changes. - /// - /// The `entry` function here, defines the standard entry to a Solana - /// program, where execution begins. - pub fn entry(program_id: &Pubkey, accounts: &[AccountInfo], data: &[u8]) -> anchor_lang::solana_program::entrypoint::ProgramResult { - try_entry(program_id, accounts, data).map_err(|e| { - e.log(); - e.into() - }) - } - - fn try_entry(program_id: &Pubkey, accounts: &[AccountInfo], data: &[u8]) -> anchor_lang::Result<()> { - #[cfg(feature = "anchor-debug")] - { - msg!("anchor-debug is active"); - } - if *program_id != ID { - return Err(anchor_lang::error::ErrorCode::DeclaredProgramIdMismatch.into()); - } - if data.len() < 8 { - return #fallback_maybe; - } - - dispatch(program_id, accounts, data) - } - - /// Module representing the program. - pub mod program { - use super::*; - - /// Type representing the program. - #[derive(Clone)] - pub struct #name; - - impl anchor_lang::Id for #name { - fn id() -> Pubkey { - ID - } - } - } - } -} diff --git a/lang/syn/src/codegen/program/handlers.rs b/lang/syn/src/codegen/program/handlers.rs deleted file mode 100644 index 543e14af..00000000 --- a/lang/syn/src/codegen/program/handlers.rs +++ /dev/null @@ -1,814 +0,0 @@ -use crate::codegen::program::common::*; -use crate::{Program, State}; -use heck::CamelCase; -use quote::{quote, ToTokens}; - -// Generate non-inlined wrappers for each instruction handler, since Solana's -// BPF max stack size can't handle reasonable sized dispatch trees without doing -// so. -pub fn generate(program: &Program) -> proc_macro2::TokenStream { - let program_name = &program.name; - let non_inlined_idl: proc_macro2::TokenStream = { - quote! { - // Entry for all IDL related instructions. Use the "no-idl" feature - // to eliminate this code, for example, if one wants to make the - // IDL no longer mutable or if one doesn't want to store the IDL - // on chain. - #[inline(never)] - #[cfg(not(feature = "no-idl"))] - pub fn __idl_dispatch(program_id: &Pubkey, accounts: &[AccountInfo], idl_ix_data: &[u8]) -> anchor_lang::Result<()> { - let mut accounts = accounts; - let mut data: &[u8] = idl_ix_data; - - let ix = anchor_lang::idl::IdlInstruction::deserialize(&mut data) - .map_err(|_| anchor_lang::error::ErrorCode::InstructionDidNotDeserialize)?; - - match ix { - anchor_lang::idl::IdlInstruction::Create { data_len } => { - let mut bumps = std::collections::BTreeMap::new(); - let mut accounts = - anchor_lang::idl::IdlCreateAccounts::try_accounts(program_id, &mut accounts, &[], &mut bumps)?; - __idl_create_account(program_id, &mut accounts, data_len)?; - accounts.exit(program_id)?; - }, - anchor_lang::idl::IdlInstruction::CreateBuffer => { - let mut bumps = std::collections::BTreeMap::new(); - let mut accounts = - anchor_lang::idl::IdlCreateBuffer::try_accounts(program_id, &mut accounts, &[], &mut bumps)?; - __idl_create_buffer(program_id, &mut accounts)?; - accounts.exit(program_id)?; - }, - anchor_lang::idl::IdlInstruction::Write { data } => { - let mut bumps = std::collections::BTreeMap::new(); - let mut accounts = - anchor_lang::idl::IdlAccounts::try_accounts(program_id, &mut accounts, &[], &mut bumps)?; - __idl_write(program_id, &mut accounts, data)?; - accounts.exit(program_id)?; - }, - anchor_lang::idl::IdlInstruction::SetAuthority { new_authority } => { - let mut bumps = std::collections::BTreeMap::new(); - let mut accounts = - anchor_lang::idl::IdlAccounts::try_accounts(program_id, &mut accounts, &[], &mut bumps)?; - __idl_set_authority(program_id, &mut accounts, new_authority)?; - accounts.exit(program_id)?; - }, - anchor_lang::idl::IdlInstruction::SetBuffer => { - let mut bumps = std::collections::BTreeMap::new(); - let mut accounts = - anchor_lang::idl::IdlSetBuffer::try_accounts(program_id, &mut accounts, &[], &mut bumps)?; - __idl_set_buffer(program_id, &mut accounts)?; - accounts.exit(program_id)?; - }, - } - Ok(()) - } - - #[inline(never)] - #[cfg(feature = "no-idl")] - pub fn __idl_dispatch(program_id: &Pubkey, accounts: &[AccountInfo], idl_ix_data: &[u8]) -> anchor_lang::Result<()> { - Err(anchor_lang::error::ErrorCode::IdlInstructionStub.into()) - } - - // One time IDL account initializer. Will faill on subsequent - // invocations. - #[inline(never)] - pub fn __idl_create_account( - program_id: &Pubkey, - accounts: &mut anchor_lang::idl::IdlCreateAccounts, - data_len: u64, - ) -> anchor_lang::Result<()> { - #[cfg(not(feature = "no-log-ix-name"))] - anchor_lang::prelude::msg!("Instruction: IdlCreateAccount"); - - if program_id != accounts.program.key { - return Err(anchor_lang::error::ErrorCode::IdlInstructionInvalidProgram.into()); - } - // Create the IDL's account. - let from = accounts.from.key; - let (base, nonce) = Pubkey::find_program_address(&[], program_id); - let seed = anchor_lang::idl::IdlAccount::seed(); - let owner = accounts.program.key; - let to = Pubkey::create_with_seed(&base, seed, owner).unwrap(); - // Space: account discriminator || authority pubkey || vec len || vec data - let space = 8 + 32 + 4 + data_len as usize; - let rent = Rent::get()?; - let lamports = rent.minimum_balance(space); - let seeds = &[&[nonce][..]]; - let ix = anchor_lang::solana_program::system_instruction::create_account_with_seed( - from, - &to, - &base, - seed, - lamports, - space as u64, - owner, - ); - anchor_lang::solana_program::program::invoke_signed( - &ix, - &[ - accounts.from.clone(), - accounts.to.clone(), - accounts.base.clone(), - accounts.system_program.clone(), - ], - &[seeds], - )?; - - // Deserialize the newly created account. - let mut idl_account = { - let mut account_data = accounts.to.try_borrow_data()?; - let mut account_data_slice: &[u8] = &account_data; - anchor_lang::idl::IdlAccount::try_deserialize_unchecked( - &mut account_data_slice, - )? - }; - - // Set the authority. - idl_account.authority = *accounts.from.key; - - // Store the new account data. - let mut data = accounts.to.try_borrow_mut_data()?; - let dst: &mut [u8] = &mut data; - let mut cursor = std::io::Cursor::new(dst); - idl_account.try_serialize(&mut cursor)?; - - Ok(()) - } - - #[inline(never)] - pub fn __idl_create_buffer( - program_id: &Pubkey, - accounts: &mut anchor_lang::idl::IdlCreateBuffer, - ) -> anchor_lang::Result<()> { - #[cfg(not(feature = "no-log-ix-name"))] - anchor_lang::prelude::msg!("Instruction: IdlCreateBuffer"); - - let mut buffer = &mut accounts.buffer; - buffer.authority = *accounts.authority.key; - Ok(()) - } - - #[inline(never)] - pub fn __idl_write( - program_id: &Pubkey, - accounts: &mut anchor_lang::idl::IdlAccounts, - idl_data: Vec, - ) -> anchor_lang::Result<()> { - #[cfg(not(feature = "no-log-ix-name"))] - anchor_lang::prelude::msg!("Instruction: IdlWrite"); - - let mut idl = &mut accounts.idl; - idl.data.extend(idl_data); - Ok(()) - } - - #[inline(never)] - pub fn __idl_set_authority( - program_id: &Pubkey, - accounts: &mut anchor_lang::idl::IdlAccounts, - new_authority: Pubkey, - ) -> anchor_lang::Result<()> { - #[cfg(not(feature = "no-log-ix-name"))] - anchor_lang::prelude::msg!("Instruction: IdlSetAuthority"); - - accounts.idl.authority = new_authority; - Ok(()) - } - - #[inline(never)] - pub fn __idl_set_buffer( - program_id: &Pubkey, - accounts: &mut anchor_lang::idl::IdlSetBuffer, - ) -> anchor_lang::Result<()> { - #[cfg(not(feature = "no-log-ix-name"))] - anchor_lang::prelude::msg!("Instruction: IdlSetBuffer"); - - accounts.idl.data = accounts.buffer.data.clone(); - Ok(()) - } - } - }; - // Constructor handler. - let non_inlined_ctor: proc_macro2::TokenStream = match &program.state { - None => quote! {}, - Some(state) => match state.ctor_and_anchor.as_ref() { - None => quote! {}, - Some((_ctor, anchor_ident)) => { - let ctor_untyped_args = generate_ctor_args(state); - let name = &state.strct.ident; - let mod_name = &program.name; - let variant_arm = generate_ctor_variant(state); - let ix_name: proc_macro2::TokenStream = - generate_ctor_variant_name().parse().unwrap(); - let ix_name_log = format!("Instruction: {}", ix_name); - if state.is_zero_copy { - quote! { - // One time state account initializer. Will faill on subsequent - // invocations. - #[inline(never)] - pub fn __ctor(program_id: &Pubkey, accounts: &[AccountInfo], ix_data: &[u8]) -> anchor_lang::Result<()> { - #[cfg(not(feature = "no-log-ix-name"))] - anchor_lang::prelude::msg!(#ix_name_log); - - // Deserialize instruction data. - let ix = instruction::state::#ix_name::deserialize(&mut &ix_data[..]) - .map_err(|_| anchor_lang::error::ErrorCode::InstructionDidNotDeserialize)?; - let instruction::state::#variant_arm = ix; - - let mut __bumps = std::collections::BTreeMap::new(); - - // Deserialize accounts. - let mut remaining_accounts: &[AccountInfo] = accounts; - let ctor_accounts = - anchor_lang::__private::Ctor::try_accounts(program_id, &mut remaining_accounts, &[], &mut __bumps)?; - let mut ctor_user_def_accounts = - #anchor_ident::try_accounts(program_id, &mut remaining_accounts, ix_data, &mut __bumps)?; - - // Create the solana account for the ctor data. - let from = ctor_accounts.from.key; - let (base, nonce) = Pubkey::find_program_address(&[], ctor_accounts.program.key); - let seed = anchor_lang::__private::PROGRAM_STATE_SEED; - let owner = ctor_accounts.program.key; - let to = Pubkey::create_with_seed(&base, seed, owner).unwrap(); - let space = 8 + std::mem::size_of::<#name>(); - let rent = Rent::get()?; - let lamports = rent.minimum_balance(std::convert::TryInto::try_into(space).unwrap()); - let seeds = &[&[nonce][..]]; - let ix = anchor_lang::solana_program::system_instruction::create_account_with_seed( - from, - &to, - &base, - seed, - lamports, - space as u64, - owner, - ); - anchor_lang::solana_program::program::invoke_signed( - &ix, - &[ - ctor_accounts.from.clone(), - ctor_accounts.to.clone(), - ctor_accounts.base.clone(), - ctor_accounts.system_program.clone(), - ], - &[seeds], - )?; - - // Zero copy deserialize. - let loader: anchor_lang::accounts::loader::Loader<#mod_name::#name> = anchor_lang::accounts::loader::Loader::try_from_unchecked(program_id, &ctor_accounts.to)?; - - // Invoke the ctor in a new lexical scope so that - // the zero-copy RefMut gets dropped. Required - // so that we can subsequently run the exit routine. - { - let mut instance = loader.load_init()?; - instance.new( - anchor_lang::context::Context::new( - program_id, - &mut ctor_user_def_accounts, - remaining_accounts, - __bumps, - ), - #(#ctor_untyped_args),* - )?; - } - - // Exit routines. - ctor_user_def_accounts.exit(program_id)?; - loader.exit(program_id)?; - - Ok(()) - } - } - } else { - quote! { - // One time state account initializer. Will faill on subsequent - // invocations. - #[inline(never)] - pub fn __ctor(program_id: &Pubkey, accounts: &[AccountInfo], ix_data: &[u8]) -> anchor_lang::Result<()> { - #[cfg(not(feature = "no-log-ix-name"))] - anchor_lang::prelude::msg!(#ix_name_log); - - // Deserialize instruction data. - let ix = instruction::state::#ix_name::deserialize(&mut &ix_data[..]) - .map_err(|_| anchor_lang::error::ErrorCode::InstructionDidNotDeserialize)?; - let instruction::state::#variant_arm = ix; - - let mut __bumps = std::collections::BTreeMap::new(); - - // Deserialize accounts. - let mut remaining_accounts: &[AccountInfo] = accounts; - let ctor_accounts = - anchor_lang::__private::Ctor::try_accounts(program_id, &mut remaining_accounts, &[], &mut __bumps)?; - let mut ctor_user_def_accounts = - #anchor_ident::try_accounts(program_id, &mut remaining_accounts, ix_data, &mut __bumps)?; - - // Invoke the ctor. - let instance = #mod_name::#name::new( - anchor_lang::context::Context::new( - program_id, - &mut ctor_user_def_accounts, - remaining_accounts, - __bumps, - ), - #(#ctor_untyped_args),* - )?; - - // Create the solana account for the ctor data. - let from = ctor_accounts.from.key; - let (base, nonce) = Pubkey::find_program_address(&[], ctor_accounts.program.key); - let seed = anchor_lang::accounts::state::ProgramState::<#name>::seed(); - let owner = ctor_accounts.program.key; - let to = Pubkey::create_with_seed(&base, seed, owner).unwrap(); - let space = anchor_lang::__private::AccountSize::size(&instance)?; - let rent = Rent::get()?; - let lamports = rent.minimum_balance(std::convert::TryInto::try_into(space).unwrap()); - let seeds = &[&[nonce][..]]; - let ix = anchor_lang::solana_program::system_instruction::create_account_with_seed( - from, - &to, - &base, - seed, - lamports, - space, - owner, - ); - anchor_lang::solana_program::program::invoke_signed( - &ix, - &[ - ctor_accounts.from.clone(), - ctor_accounts.to.clone(), - ctor_accounts.base.clone(), - ctor_accounts.system_program.clone(), - ], - &[seeds], - )?; - - // Serialize the state and save it to storage. - ctor_user_def_accounts.exit(program_id)?; - let mut data = ctor_accounts.to.try_borrow_mut_data()?; - let dst: &mut [u8] = &mut data; - let mut cursor = std::io::Cursor::new(dst); - instance.try_serialize(&mut cursor)?; - - Ok(()) - } - } - } - } - }, - }; - - // State method handlers. - let non_inlined_state_handlers: Vec = match &program.state { - None => vec![], - Some(state) => state - .impl_block_and_methods - .as_ref() - .map(|(_impl_block, methods)| { - methods - .iter() - .map(|ix| { - let ix_arg_names: Vec<&syn::Ident> = - ix.args.iter().map(|arg| &arg.name).collect(); - let private_ix_method_name: proc_macro2::TokenStream = { - let n = format!("__{}", &ix.raw_method.sig.ident.to_string()); - n.parse().unwrap() - }; - let ix_method_name = &ix.raw_method.sig.ident; - let state_ty: proc_macro2::TokenStream = state.name.parse().unwrap(); - let anchor_ident = &ix.anchor_ident; - let name = &state.strct.ident; - let mod_name = &program.name; - - let variant_arm = - generate_ix_variant(ix.raw_method.sig.ident.to_string(), &ix.args); - let ix_name = generate_ix_variant_name(ix.raw_method.sig.ident.to_string()); - let ix_name_log = format!("Instruction: {}", ix_name); - - if state.is_zero_copy { - quote! { - #[inline(never)] - pub fn #private_ix_method_name( - program_id: &Pubkey, - accounts: &[AccountInfo], - ix_data: &[u8], - ) -> anchor_lang::Result<()> { - #[cfg(not(feature = "no-log-ix-name"))] - anchor_lang::prelude::msg!(#ix_name_log); - - // Deserialize instruction. - let ix = instruction::state::#ix_name::deserialize(&mut &ix_data[..]) - .map_err(|_| anchor_lang::error::ErrorCode::InstructionDidNotDeserialize)?; - let instruction::state::#variant_arm = ix; - - // Bump collector. - let mut __bumps = std::collections::BTreeMap::new(); - - // Load state. - let mut remaining_accounts: &[AccountInfo] = accounts; - if remaining_accounts.is_empty() { - return Err(anchor_lang::error::ErrorCode::AccountNotEnoughKeys.into()); - } - let loader: anchor_lang::accounts::loader::Loader<#mod_name::#name> = anchor_lang::accounts::loader::Loader::try_accounts(program_id, &mut remaining_accounts, &[], &mut __bumps)?; - - // Deserialize accounts. - let mut accounts = #anchor_ident::try_accounts( - program_id, - &mut remaining_accounts, - ix_data, - &mut __bumps, - )?; - let ctx = - anchor_lang::context::Context::new( - program_id, - &mut accounts, - remaining_accounts, - __bumps, - ); - - // Execute user defined function. - { - let mut state = loader.load_mut()?; - state.#ix_method_name( - ctx, - #(#ix_arg_names),* - )?; - } - // Serialize the state and save it to storage. - accounts.exit(program_id)?; - loader.exit(program_id)?; - - Ok(()) - } - } - } else { - quote! { - #[inline(never)] - pub fn #private_ix_method_name( - program_id: &Pubkey, - accounts: &[AccountInfo], - ix_data: &[u8], - ) -> anchor_lang::Result<()> { - #[cfg(not(feature = "no-log-ix-name"))] - anchor_lang::prelude::msg!(#ix_name_log); - - // Deserialize instruction. - let ix = instruction::state::#ix_name::deserialize(&mut &ix_data[..]) - .map_err(|_| anchor_lang::error::ErrorCode::InstructionDidNotDeserialize)?; - let instruction::state::#variant_arm = ix; - - // Bump collector. - let mut __bumps = std::collections::BTreeMap::new(); - - // Load state. - let mut remaining_accounts: &[AccountInfo] = accounts; - if remaining_accounts.is_empty() { - return Err(anchor_lang::error::ErrorCode::AccountNotEnoughKeys.into()); - } - let mut state: anchor_lang::accounts::state::ProgramState<#state_ty> = anchor_lang::accounts::state::ProgramState::try_accounts( - program_id, - &mut remaining_accounts, - &[], - &mut __bumps, - )?; - - // Deserialize accounts. - let mut accounts = #anchor_ident::try_accounts( - program_id, - &mut remaining_accounts, - ix_data, - &mut __bumps, - )?; - let ctx = - anchor_lang::context::Context::new( - program_id, - &mut accounts, - remaining_accounts, - __bumps - ); - - // Execute user defined function. - state.#ix_method_name( - ctx, - #(#ix_arg_names),* - )?; - - // Serialize the state and save it to storage. - accounts.exit(program_id)?; - let acc_info = state.to_account_info(); - let mut data = acc_info.try_borrow_mut_data()?; - let dst: &mut [u8] = &mut data; - let mut cursor = std::io::Cursor::new(dst); - state.try_serialize(&mut cursor)?; - - Ok(()) - } - } - } - }) - .collect() - }) - .unwrap_or_default(), - }; - - // State trait handlers. - let non_inlined_state_trait_handlers: Vec = match &program.state { - None => Vec::new(), - Some(state) => state - .interfaces - .as_ref() - .map(|interfaces| { - interfaces - .iter() - .flat_map(|iface: &crate::StateInterface| { - iface - .methods - .iter() - .map(|ix| { - // Easy to implement. Just need to write a test. - // Feel free to open a PR. - assert!(!state.is_zero_copy, "Trait implementations not yet implemented for zero copy state structs. Please file an issue."); - - let ix_arg_names: Vec<&syn::Ident> = - ix.args.iter().map(|arg| &arg.name).collect(); - let private_ix_method_name: proc_macro2::TokenStream = { - let n = format!("__{}_{}", iface.trait_name, &ix.raw_method.sig.ident.to_string()); - n.parse().unwrap() - }; - let ix_method_name = &ix.raw_method.sig.ident; - let state_ty: proc_macro2::TokenStream = state.name.parse().unwrap(); - let anchor_ident = &ix.anchor_ident; - let ix_name = generate_ix_variant_name(ix.raw_method.sig.ident.to_string()); - let ix_name_log = format!("Instruction: {}", ix_name); - - let raw_args: Vec<&syn::PatType> = ix - .args - .iter() - .map(|arg: &crate::IxArg| &arg.raw_arg) - .collect(); - let args_struct = { - if ix.args.is_empty() { - quote! { - #[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)] - struct Args; - } - } else { - quote! { - #[derive(anchor_lang::AnchorSerialize, anchor_lang::AnchorDeserialize)] - struct Args { - #(#raw_args),* - } - } - } - }; - - let deserialize_instruction = quote! { - #args_struct - let ix = Args::deserialize(&mut &ix_data[..]) - .map_err(|_| anchor_lang::error::ErrorCode::InstructionDidNotDeserialize)?; - let Args { - #(#ix_arg_names),* - } = ix; - }; - - if ix.has_receiver { - quote! { - #[inline(never)] - pub fn #private_ix_method_name( - program_id: &Pubkey, - accounts: &[AccountInfo], - ix_data: &[u8], - ) -> anchor_lang::Result<()> { - #[cfg(not(feature = "no-log-ix-name"))] - anchor_lang::prelude::msg!(#ix_name_log); - - // Deserialize instruction. - #deserialize_instruction - - // Bump collector. - let mut __bumps = std::collections::BTreeMap::new(); - - // Deserialize the program state account. - let mut remaining_accounts: &[AccountInfo] = accounts; - if remaining_accounts.is_empty() { - return Err(anchor_lang::error::ErrorCode::AccountNotEnoughKeys.into()); - } - let mut state: anchor_lang::accounts::state::ProgramState<#state_ty> = anchor_lang::accounts::state::ProgramState::try_accounts( - program_id, - &mut remaining_accounts, - &[], - &mut __bumps, - )?; - - // Deserialize accounts. - let mut accounts = #anchor_ident::try_accounts( - program_id, - &mut remaining_accounts, - ix_data, - &mut __bumps, - )?; - let ctx = - anchor_lang::context::Context::new( - program_id, - &mut accounts, - remaining_accounts, - __bumps, - ); - - // Execute user defined function. - state.#ix_method_name( - ctx, - #(#ix_arg_names),* - )?; - - // Exit procedures. - accounts.exit(program_id)?; - let acc_info = state.to_account_info(); - let mut data = acc_info.try_borrow_mut_data()?; - let dst: &mut [u8] = &mut data; - let mut cursor = std::io::Cursor::new(dst); - state.try_serialize(&mut cursor)?; - - Ok(()) - } - } - } else { - let state_name: proc_macro2::TokenStream = state.name.parse().unwrap(); - quote! { - #[inline(never)] - pub fn #private_ix_method_name( - program_id: &Pubkey, - accounts: &[AccountInfo], - ix_data: &[u8], - ) -> anchor_lang::Result<()> { - #[cfg(not(feature = "no-log-ix-name"))] - anchor_lang::prelude::msg!(#ix_name_log); - - // Deserialize instruction. - #deserialize_instruction - - // Bump collector. - let mut __bumps = std::collections::BTreeMap::new(); - - // Deserialize accounts. - let mut remaining_accounts: &[AccountInfo] = accounts; - let mut accounts = #anchor_ident::try_accounts( - program_id, - &mut remaining_accounts, - ix_data, - &mut __bumps, - )?; - - // Execute user defined function. - #state_name::#ix_method_name( - anchor_lang::context::Context::new( - program_id, - &mut accounts, - remaining_accounts, - __bumps - ), - #(#ix_arg_names),* - )?; - - // Exit procedure. - accounts.exit(program_id) - } - } - } - }) - .collect::>() - }) - .collect() - }) - .unwrap_or_default(), - }; - - let non_inlined_handlers: Vec = program - .ixs - .iter() - .map(|ix| { - let ix_arg_names: Vec<&syn::Ident> = ix.args.iter().map(|arg| &arg.name).collect(); - let ix_name = generate_ix_variant_name(ix.raw_method.sig.ident.to_string()); - let ix_method_name = &ix.raw_method.sig.ident; - let anchor = &ix.anchor_ident; - let variant_arm = generate_ix_variant(ix.raw_method.sig.ident.to_string(), &ix.args); - let ix_name_log = format!("Instruction: {}", ix_name); - let ret_type = &ix.returns.ty.to_token_stream(); - let maybe_set_return_data = match ret_type.to_string().as_str() { - "()" => quote! {}, - _ => quote! { - anchor_lang::solana_program::program::set_return_data(&result.try_to_vec().unwrap()); - }, - }; - quote! { - #[inline(never)] - pub fn #ix_method_name( - program_id: &Pubkey, - accounts: &[AccountInfo], - ix_data: &[u8], - ) -> anchor_lang::Result<()> { - #[cfg(not(feature = "no-log-ix-name"))] - anchor_lang::prelude::msg!(#ix_name_log); - - // Deserialize data. - let ix = instruction::#ix_name::deserialize(&mut &ix_data[..]) - .map_err(|_| anchor_lang::error::ErrorCode::InstructionDidNotDeserialize)?; - let instruction::#variant_arm = ix; - - // Bump collector. - let mut __bumps = std::collections::BTreeMap::new(); - - // Deserialize accounts. - let mut remaining_accounts: &[AccountInfo] = accounts; - let mut accounts = #anchor::try_accounts( - program_id, - &mut remaining_accounts, - ix_data, - &mut __bumps, - )?; - - // Invoke user defined handler. - let result = #program_name::#ix_method_name( - anchor_lang::context::Context::new( - program_id, - &mut accounts, - remaining_accounts, - __bumps, - ), - #(#ix_arg_names),* - )?; - - // Maybe set Solana return data. - #maybe_set_return_data - - // Exit routine. - accounts.exit(program_id) - } - } - }) - .collect(); - - quote! { - /// Create a private module to not clutter the program's namespace. - /// Defines an entrypoint for each individual instruction handler - /// wrapper. - mod __private { - use super::*; - /// __idl mod defines handlers for injected Anchor IDL instructions. - pub mod __idl { - use super::*; - - #non_inlined_idl - } - - /// __state mod defines wrapped handlers for state instructions. - pub mod __state { - use super::*; - - #non_inlined_ctor - #(#non_inlined_state_handlers)* - } - - /// __interface mod defines wrapped handlers for `#[interface]` trait - /// implementations. - pub mod __interface { - use super::*; - - #(#non_inlined_state_trait_handlers)* - } - - /// __global mod defines wrapped handlers for global instructions. - pub mod __global { - use super::*; - - #(#non_inlined_handlers)* - } - } - } -} - -fn generate_ix_variant_name(name: String) -> proc_macro2::TokenStream { - let n = name.to_camel_case(); - n.parse().unwrap() -} - -fn generate_ctor_variant_name() -> String { - "New".to_string() -} - -fn generate_ctor_variant(state: &State) -> proc_macro2::TokenStream { - let ctor_args = generate_ctor_args(state); - let ctor_variant_name: proc_macro2::TokenStream = generate_ctor_variant_name().parse().unwrap(); - if ctor_args.is_empty() { - quote! { - #ctor_variant_name - } - } else { - quote! { - #ctor_variant_name { - #(#ctor_args),* - } - } - } -} diff --git a/lang/syn/src/codegen/program/instruction.rs b/lang/syn/src/codegen/program/instruction.rs deleted file mode 100644 index 9191d05d..00000000 --- a/lang/syn/src/codegen/program/instruction.rs +++ /dev/null @@ -1,194 +0,0 @@ -use crate::codegen::program::common::*; -use crate::parser; -use crate::Program; -use heck::CamelCase; -use quote::quote; - -pub fn generate(program: &Program) -> proc_macro2::TokenStream { - let ctor_variant = match &program.state { - None => quote! {}, - Some(state) => { - let ctor_args: Vec = generate_ctor_typed_args(state) - .iter() - .map(|arg| { - format!("pub {}", parser::tts_to_string(&arg)) - .parse() - .unwrap() - }) - .collect(); - let strct = { - if ctor_args.is_empty() { - quote! { - #[derive(AnchorSerialize, AnchorDeserialize)] - pub struct New; - } - } else { - quote! { - #[derive(AnchorSerialize, AnchorDeserialize)] - pub struct New { - #(#ctor_args),* - } - } - } - }; - let sighash_arr = sighash_ctor(); - let sighash_tts: proc_macro2::TokenStream = - format!("{:?}", sighash_arr).parse().unwrap(); - quote! { - /// Instruction arguments to the `#[state]`'s `new` - /// constructor. - #strct - - impl anchor_lang::InstructionData for New { - fn data(&self) -> Vec { - let mut d = #sighash_tts.to_vec(); - d.append(&mut self.try_to_vec().expect("Should always serialize")); - d - } - } - } - } - }; - let state_method_variants: Vec = match &program.state { - None => vec![], - Some(state) => state - .impl_block_and_methods - .as_ref() - .map(|(_impl_block, methods)| { - methods - .iter() - .map(|method| { - let ix_name_camel: proc_macro2::TokenStream = method - .raw_method - .sig - .ident - .to_string() - .to_camel_case() - .parse() - .unwrap(); - let raw_args: Vec = method - .args - .iter() - .map(|arg| { - format!("pub {}", parser::tts_to_string(&arg.raw_arg)) - .parse() - .unwrap() - }) - .collect(); - - let ix_data_trait = { - let name = method.raw_method.sig.ident.to_string(); - let sighash_arr = sighash(SIGHASH_STATE_NAMESPACE, &name); - let sighash_tts: proc_macro2::TokenStream = - format!("{:?}", sighash_arr).parse().unwrap(); - quote! { - impl anchor_lang::InstructionData for #ix_name_camel { - fn data(&self) -> Vec { - let mut d = #sighash_tts.to_vec(); - d.append(&mut self.try_to_vec().expect("Should always serialize")); - d - } - } - } - }; - - // If no args, output a "unit" variant instead of a struct variant. - if method.args.is_empty() { - quote! { - /// Anchor generated instruction. - #[derive(AnchorSerialize, AnchorDeserialize)] - pub struct #ix_name_camel; - - #ix_data_trait - } - } else { - quote! { - /// Anchor generated instruction. - #[derive(AnchorSerialize, AnchorDeserialize)] - pub struct #ix_name_camel { - #(#raw_args),* - } - - #ix_data_trait - } - } - }) - .collect() - }) - .unwrap_or_default(), - }; - let variants: Vec = program - .ixs - .iter() - .map(|ix| { - let name = &ix.raw_method.sig.ident.to_string(); - let ix_name_camel = - proc_macro2::Ident::new(&name.to_camel_case(), ix.raw_method.sig.ident.span()); - let raw_args: Vec = ix - .args - .iter() - .map(|arg| { - format!("pub {}", parser::tts_to_string(&arg.raw_arg)) - .parse() - .unwrap() - }) - .collect(); - let ix_data_trait = { - let sighash_arr = sighash(SIGHASH_GLOBAL_NAMESPACE, name); - let sighash_tts: proc_macro2::TokenStream = - format!("{:?}", sighash_arr).parse().unwrap(); - quote! { - impl anchor_lang::InstructionData for #ix_name_camel { - fn data(&self) -> Vec { - let mut d = #sighash_tts.to_vec(); - d.append(&mut self.try_to_vec().expect("Should always serialize")); - d - } - } - } - }; - // If no args, output a "unit" variant instead of a struct variant. - if ix.args.is_empty() { - quote! { - /// Instruction. - #[derive(AnchorSerialize, AnchorDeserialize)] - pub struct #ix_name_camel; - - #ix_data_trait - } - } else { - quote! { - /// Instruction. - #[derive(AnchorSerialize, AnchorDeserialize)] - pub struct #ix_name_camel { - #(#raw_args),* - } - - #ix_data_trait - } - } - }) - .collect(); - - quote! { - /// An Anchor generated module containing the program's set of - /// instructions, where each method handler in the `#[program]` mod is - /// associated with a struct defining the input arguments to the - /// method. These should be used directly, when one wants to serialize - /// Anchor instruction data, for example, when speciying - /// instructions on a client. - pub mod instruction { - use super::*; - - /// Instruction struct definitions for `#[state]` methods. - pub mod state { - use super::*; - - #ctor_variant - #(#state_method_variants)* - } - - #(#variants)* - } - } -} diff --git a/lang/syn/src/codegen/program/mod.rs b/lang/syn/src/codegen/program/mod.rs deleted file mode 100644 index ea5aff0c..00000000 --- a/lang/syn/src/codegen/program/mod.rs +++ /dev/null @@ -1,35 +0,0 @@ -use crate::Program; -use quote::quote; - -mod accounts; -pub mod common; -mod cpi; -mod dispatch; -mod entry; -mod handlers; -mod instruction; - -pub fn generate(program: &Program) -> proc_macro2::TokenStream { - let mod_name = &program.name; - - let entry = entry::generate(program); - let dispatch = dispatch::generate(program); - let handlers = handlers::generate(program); - let user_defined_program = &program.program_mod; - let instruction = instruction::generate(program); - let cpi = cpi::generate(program); - let accounts = accounts::generate(program); - - quote! { - // TODO: remove once we allow segmented paths in `Accounts` structs. - use self::#mod_name::*; - - #entry - #dispatch - #handlers - #user_defined_program - #instruction - #cpi - #accounts - } -} diff --git a/lang/syn/src/hash.rs b/lang/syn/src/hash.rs deleted file mode 100644 index 8a826aa1..00000000 --- a/lang/syn/src/hash.rs +++ /dev/null @@ -1,117 +0,0 @@ -// Utility hashing module copied from `solana_program::program::hash`, since we -// can't import solana_program for compile time hashing for some reason. - -use serde::{Deserialize, Serialize}; -use sha2::{Digest, Sha256}; -use std::{convert::TryFrom, fmt, mem, str::FromStr}; -use thiserror::Error; - -pub const HASH_BYTES: usize = 32; -#[derive(Serialize, Deserialize, Clone, Copy, Default, Eq, PartialEq, Ord, PartialOrd, Hash)] -#[repr(transparent)] -pub struct Hash(pub [u8; HASH_BYTES]); - -#[derive(Clone, Default)] -pub struct Hasher { - hasher: Sha256, -} - -impl Hasher { - pub fn hash(&mut self, val: &[u8]) { - self.hasher.update(val); - } - pub fn hashv(&mut self, vals: &[&[u8]]) { - for val in vals { - self.hash(val); - } - } - pub fn result(self) -> Hash { - // At the time of this writing, the sha2 library is stuck on an old version - // of generic_array (0.9.0). Decouple ourselves with a clone to our version. - Hash(<[u8; HASH_BYTES]>::try_from(self.hasher.finalize().as_slice()).unwrap()) - } -} - -impl AsRef<[u8]> for Hash { - fn as_ref(&self) -> &[u8] { - &self.0[..] - } -} - -impl fmt::Debug for Hash { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", bs58::encode(self.0).into_string()) - } -} - -impl fmt::Display for Hash { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", bs58::encode(self.0).into_string()) - } -} - -#[derive(Debug, Clone, PartialEq, Eq, Error)] -pub enum ParseHashError { - #[error("string decoded to wrong size for hash")] - WrongSize, - #[error("failed to decoded string to hash")] - Invalid, -} - -impl FromStr for Hash { - type Err = ParseHashError; - - fn from_str(s: &str) -> Result { - let bytes = bs58::decode(s) - .into_vec() - .map_err(|_| ParseHashError::Invalid)?; - if bytes.len() != mem::size_of::() { - Err(ParseHashError::WrongSize) - } else { - Ok(Hash::new(&bytes)) - } - } -} - -impl Hash { - pub fn new(hash_slice: &[u8]) -> Self { - Hash(<[u8; HASH_BYTES]>::try_from(hash_slice).unwrap()) - } - - pub fn to_bytes(self) -> [u8; HASH_BYTES] { - self.0 - } -} - -/// Return a Sha256 hash for the given data. -pub fn hashv(vals: &[&[u8]]) -> Hash { - // Perform the calculation inline, calling this from within a program is - // not supported - #[cfg(not(target_arch = "bpf"))] - { - let mut hasher = Hasher::default(); - hasher.hashv(vals); - hasher.result() - } - // Call via a system call to perform the calculation - #[cfg(target_arch = "bpf")] - { - extern "C" { - fn sol_sha256(vals: *const u8, val_len: u64, hash_result: *mut u8) -> u64; - }; - let mut hash_result = [0; HASH_BYTES]; - unsafe { - sol_sha256( - vals as *const _ as *const u8, - vals.len() as u64, - &mut hash_result as *mut _ as *mut u8, - ); - } - Hash::new_from_array(hash_result) - } -} - -/// Return a Sha256 hash for the given data. -pub fn hash(val: &[u8]) -> Hash { - hashv(&[val]) -} diff --git a/lang/syn/src/idl/file.rs b/lang/syn/src/idl/file.rs deleted file mode 100644 index 9b5c2b3c..00000000 --- a/lang/syn/src/idl/file.rs +++ /dev/null @@ -1,570 +0,0 @@ -use crate::idl::*; -use crate::parser::context::CrateContext; -use crate::parser::{self, accounts, error, program}; -use crate::Ty; -use crate::{AccountField, AccountsStruct, StateIx}; -use anyhow::Result; -use heck::MixedCase; -use quote::ToTokens; -use std::collections::{HashMap, HashSet}; -use std::path::Path; - -const DERIVE_NAME: &str = "Accounts"; -// TODO: sharee this with `anchor_lang` crate. -const ERROR_CODE_OFFSET: u32 = 6000; - -// Parse an entire interface file. -pub fn parse( - filename: impl AsRef, - version: String, - seeds_feature: bool, - safety_checks: bool, -) -> Result> { - let ctx = CrateContext::parse(filename)?; - if safety_checks { - ctx.safety_checks()?; - } - - let program_mod = match parse_program_mod(&ctx) { - None => return Ok(None), - Some(m) => m, - }; - let p = program::parse(program_mod)?; - - let accs = parse_account_derives(&ctx); - - let state = match p.state { - None => None, - Some(state) => match state.ctor_and_anchor { - None => None, // State struct defined but no implementation - Some((ctor, anchor_ident)) => { - let mut methods = state - .impl_block_and_methods - .map(|(_impl_block, methods)| { - methods - .iter() - .map(|method: &StateIx| { - let name = method.ident.to_string().to_mixed_case(); - let args = method - .args - .iter() - .map(|arg| { - let mut tts = proc_macro2::TokenStream::new(); - arg.raw_arg.ty.to_tokens(&mut tts); - let ty = tts.to_string().parse().unwrap(); - IdlField { - name: arg.name.to_string().to_mixed_case(), - ty, - } - }) - .collect::>(); - let accounts_strct = - accs.get(&method.anchor_ident.to_string()).unwrap(); - let accounts = - idl_accounts(&ctx, accounts_strct, &accs, seeds_feature); - IdlInstruction { - name, - accounts, - args, - returns: None, - } - }) - .collect::>() - }) - .unwrap_or_default(); - let ctor = { - let name = "new".to_string(); - let args = ctor - .sig - .inputs - .iter() - .filter(|arg| match arg { - syn::FnArg::Typed(pat_ty) => { - // TODO: this filtering should be done in the parser. - let mut arg_str = parser::tts_to_string(&pat_ty.ty); - arg_str.retain(|c| !c.is_whitespace()); - !arg_str.starts_with("Context<") - } - _ => false, - }) - .map(|arg: &syn::FnArg| match arg { - syn::FnArg::Typed(arg_typed) => { - let mut tts = proc_macro2::TokenStream::new(); - arg_typed.ty.to_tokens(&mut tts); - let ty = tts.to_string().parse().unwrap(); - IdlField { - name: parser::tts_to_string(&arg_typed.pat).to_mixed_case(), - ty, - } - } - _ => panic!("Invalid syntax"), - }) - .collect(); - let accounts_strct = accs.get(&anchor_ident.to_string()).unwrap(); - let accounts = idl_accounts(&ctx, accounts_strct, &accs, seeds_feature); - IdlInstruction { - name, - accounts, - args, - returns: None, - } - }; - - methods.insert(0, ctor); - - let strct = { - let fields = match state.strct.fields { - syn::Fields::Named(f_named) => f_named - .named - .iter() - .map(|f: &syn::Field| { - let mut tts = proc_macro2::TokenStream::new(); - f.ty.to_tokens(&mut tts); - let ty = tts.to_string().parse().unwrap(); - IdlField { - name: f.ident.as_ref().unwrap().to_string().to_mixed_case(), - ty, - } - }) - .collect::>(), - _ => panic!("State must be a struct"), - }; - IdlTypeDefinition { - name: state.name, - ty: IdlTypeDefinitionTy::Struct { fields }, - } - }; - - Some(IdlState { strct, methods }) - } - }, - }; - let error = parse_error_enum(&ctx).map(|mut e| error::parse(&mut e, None)); - let error_codes = error.as_ref().map(|e| { - e.codes - .iter() - .map(|code| IdlErrorCode { - code: ERROR_CODE_OFFSET + code.id, - name: code.ident.to_string(), - msg: code.msg.clone(), - }) - .collect::>() - }); - - let instructions = p - .ixs - .iter() - .map(|ix| { - let args = ix - .args - .iter() - .map(|arg| IdlField { - name: arg.name.to_string().to_mixed_case(), - ty: to_idl_type(&ctx, &arg.raw_arg.ty), - }) - .collect::>(); - // todo: don't unwrap - let accounts_strct = accs.get(&ix.anchor_ident.to_string()).unwrap(); - let accounts = idl_accounts(&ctx, accounts_strct, &accs, seeds_feature); - let ret_type_str = ix.returns.ty.to_token_stream().to_string(); - let returns = match ret_type_str.as_str() { - "()" => None, - _ => Some(ret_type_str.parse().unwrap()), - }; - IdlInstruction { - name: ix.ident.to_string().to_mixed_case(), - accounts, - args, - returns, - } - }) - .collect::>(); - - let events = parse_events(&ctx) - .iter() - .map(|e: &&syn::ItemStruct| { - let fields = match &e.fields { - syn::Fields::Named(n) => n, - _ => panic!("Event fields must be named"), - }; - let fields = fields - .named - .iter() - .map(|f: &syn::Field| { - let index = match f.attrs.get(0) { - None => false, - Some(i) => parser::tts_to_string(&i.path) == "index", - }; - IdlEventField { - name: f.ident.clone().unwrap().to_string().to_mixed_case(), - ty: to_idl_type(&ctx, &f.ty), - index, - } - }) - .collect::>(); - - IdlEvent { - name: e.ident.to_string(), - fields, - } - }) - .collect::>(); - - // All user defined types. - let mut accounts = vec![]; - let mut types = vec![]; - let ty_defs = parse_ty_defs(&ctx)?; - - let account_structs = parse_accounts(&ctx); - let account_names: HashSet = account_structs - .iter() - .map(|a| a.ident.to_string()) - .collect::>(); - - let error_name = error.map(|e| e.name).unwrap_or_else(|| "".to_string()); - - // All types that aren't in the accounts section, are in the types section. - for ty_def in ty_defs { - // Don't add the error type to the types or accounts sections. - if ty_def.name != error_name { - if account_names.contains(&ty_def.name) { - accounts.push(ty_def); - } else if !events.iter().any(|e| e.name == ty_def.name) { - types.push(ty_def); - } - } - } - - let constants = parse_consts(&ctx) - .iter() - .map(|c: &&syn::ItemConst| IdlConst { - name: c.ident.to_string(), - ty: c.ty.to_token_stream().to_string().parse().unwrap(), - value: c.expr.to_token_stream().to_string().parse().unwrap(), - }) - .collect::>(); - - Ok(Some(Idl { - version, - name: p.name.to_string(), - state, - instructions, - types, - accounts, - events: if events.is_empty() { - None - } else { - Some(events) - }, - errors: error_codes, - metadata: None, - constants, - })) -} - -// Parse the main program mod. -fn parse_program_mod(ctx: &CrateContext) -> Option { - let root = ctx.root_module(); - let mods = root - .items() - .filter_map(|i| match i { - syn::Item::Mod(item_mod) => { - let mod_count = item_mod - .attrs - .iter() - .filter(|attr| attr.path.segments.last().unwrap().ident == "program") - .count(); - if mod_count != 1 { - return None; - } - Some(item_mod) - } - _ => None, - }) - .collect::>(); - if mods.len() != 1 { - return None; - } - Some(mods[0].clone()) -} - -fn parse_error_enum(ctx: &CrateContext) -> Option { - ctx.enums() - .filter_map(|item_enum| { - let attrs_count = item_enum - .attrs - .iter() - .filter(|attr| { - let segment = attr.path.segments.last().unwrap(); - segment.ident == "error_code" - }) - .count(); - match attrs_count { - 0 => None, - 1 => Some(item_enum), - _ => panic!("Invalid syntax: one error attribute allowed"), - } - }) - .next() - .cloned() -} - -fn parse_events(ctx: &CrateContext) -> Vec<&syn::ItemStruct> { - ctx.structs() - .filter_map(|item_strct| { - let attrs_count = item_strct - .attrs - .iter() - .filter(|attr| { - let segment = attr.path.segments.last().unwrap(); - segment.ident == "event" - }) - .count(); - match attrs_count { - 0 => None, - 1 => Some(item_strct), - _ => panic!("Invalid syntax: one event attribute allowed"), - } - }) - .collect() -} - -fn parse_accounts(ctx: &CrateContext) -> Vec<&syn::ItemStruct> { - ctx.structs() - .filter_map(|item_strct| { - let attrs_count = item_strct - .attrs - .iter() - .filter(|attr| { - let segment = attr.path.segments.last().unwrap(); - segment.ident == "account" || segment.ident == "associated" - }) - .count(); - match attrs_count { - 0 => None, - 1 => Some(item_strct), - _ => panic!("Invalid syntax: one event attribute allowed"), - } - }) - .collect() -} - -// Parse all structs implementing the `Accounts` trait. -fn parse_account_derives(ctx: &CrateContext) -> HashMap { - // TODO: parse manual implementations. Currently we only look - // for derives. - ctx.structs() - .filter_map(|i_strct| { - for attr in &i_strct.attrs { - if attr.path.is_ident("derive") && attr.tokens.to_string().contains(DERIVE_NAME) { - let strct = accounts::parse(i_strct).expect("Code not parseable"); - return Some((strct.ident.to_string(), strct)); - } - } - None - }) - .collect() -} - -fn parse_consts(ctx: &CrateContext) -> Vec<&syn::ItemConst> { - ctx.consts() - .filter(|item_strct| { - for attr in &item_strct.attrs { - if attr.path.segments.last().unwrap().ident == "constant" { - return true; - } - } - false - }) - .collect() -} - -// Parse all user defined types in the file. -fn parse_ty_defs(ctx: &CrateContext) -> Result> { - ctx.structs() - .filter_map(|item_strct| { - // Only take serializable types - let serializable = item_strct.attrs.iter().any(|attr| { - let attr_string = attr.tokens.to_string(); - let attr_name = attr.path.segments.last().unwrap().ident.to_string(); - let attr_serializable = ["account", "associated", "event", "zero_copy"]; - - let derived_serializable = attr_name == "derive" - && attr_string.contains("AnchorSerialize") - && attr_string.contains("AnchorDeserialize"); - - attr_serializable.iter().any(|a| *a == attr_name) || derived_serializable - }); - - if !serializable { - return None; - } - - // Only take public types - match &item_strct.vis { - syn::Visibility::Public(_) => (), - _ => return None, - } - - let name = item_strct.ident.to_string(); - let fields = match &item_strct.fields { - syn::Fields::Named(fields) => fields - .named - .iter() - .map(|f: &syn::Field| { - Ok(IdlField { - name: f.ident.as_ref().unwrap().to_string().to_mixed_case(), - ty: to_idl_type(ctx, &f.ty), - }) - }) - .collect::>>(), - syn::Fields::Unnamed(_) => return None, - _ => panic!("Empty structs are allowed."), - }; - - Some(fields.map(|fields| IdlTypeDefinition { - name, - ty: IdlTypeDefinitionTy::Struct { fields }, - })) - }) - .chain(ctx.enums().map(|enm| { - let name = enm.ident.to_string(); - let variants = enm - .variants - .iter() - .map(|variant: &syn::Variant| { - let name = variant.ident.to_string(); - let fields = match &variant.fields { - syn::Fields::Unit => None, - syn::Fields::Unnamed(fields) => { - let fields: Vec = fields - .unnamed - .iter() - .map(|f| to_idl_type(ctx, &f.ty)) - .collect(); - Some(EnumFields::Tuple(fields)) - } - syn::Fields::Named(fields) => { - let fields: Vec = fields - .named - .iter() - .map(|f: &syn::Field| { - let name = f.ident.as_ref().unwrap().to_string(); - let ty = to_idl_type(ctx, &f.ty); - IdlField { name, ty } - }) - .collect(); - Some(EnumFields::Named(fields)) - } - }; - IdlEnumVariant { name, fields } - }) - .collect::>(); - Ok(IdlTypeDefinition { - name, - ty: IdlTypeDefinitionTy::Enum { variants }, - }) - })) - .collect() -} - -// Replace variable array lengths with values -fn resolve_variable_array_lengths(ctx: &CrateContext, mut tts_string: String) -> String { - for constant in ctx.consts().filter(|c| match *c.ty { - // Filter to only those consts that are of type usize or could be cast to usize - syn::Type::Path(ref p) => { - let segment = p.path.segments.last().unwrap(); - matches!( - segment.ident.to_string().as_str(), - "usize" - | "u8" - | "u16" - | "u32" - | "u64" - | "u128" - | "isize" - | "i8" - | "i16" - | "i32" - | "i64" - | "i128" - ) - } - _ => false, - }) { - let mut check_string = tts_string.clone(); - // Strip whitespace to handle accidental double whitespaces - check_string.retain(|c| !c.is_whitespace()); - let size_string = format!("{}]", &constant.ident.to_string()); - let cast_size_string = format!("{}asusize]", &constant.ident.to_string()); - // Check for something to replace - let mut replacement_string = None; - if check_string.contains(cast_size_string.as_str()) { - replacement_string = Some(cast_size_string); - } else if check_string.contains(size_string.as_str()) { - replacement_string = Some(size_string); - } - if let Some(replacement_string) = replacement_string { - // Check for the existence of consts existing elsewhere in the - // crate which have the same name, are usize, and have a - // different value. We can't know which was intended for the - // array size from ctx. - if ctx.consts().any(|c| { - c != constant - && c.ident == constant.ident - && c.ty == constant.ty - && c.expr != constant.expr - }) { - panic!("Crate wide unique name required for array size const."); - } - // Replace the match, don't break because there might be multiple replacements to be - // made in the case of multidimensional arrays - tts_string = check_string.replace( - &replacement_string, - format!("{}]", &constant.expr.to_token_stream()).as_str(), - ); - } - } - tts_string -} - -fn to_idl_type(ctx: &CrateContext, ty: &syn::Type) -> IdlType { - let mut tts_string = parser::tts_to_string(&ty); - if tts_string.starts_with('[') { - tts_string = resolve_variable_array_lengths(ctx, tts_string); - } - tts_string.parse().unwrap() -} - -fn idl_accounts( - ctx: &CrateContext, - accounts: &AccountsStruct, - global_accs: &HashMap, - seeds_feature: bool, -) -> Vec { - accounts - .fields - .iter() - .map(|acc: &AccountField| match acc { - AccountField::CompositeField(comp_f) => { - let accs_strct = global_accs.get(&comp_f.symbol).unwrap_or_else(|| { - panic!("Could not resolve Accounts symbol {}", comp_f.symbol) - }); - let accounts = idl_accounts(ctx, accs_strct, global_accs, seeds_feature); - IdlAccountItem::IdlAccounts(IdlAccounts { - name: comp_f.ident.to_string().to_mixed_case(), - accounts, - }) - } - AccountField::Field(acc) => IdlAccountItem::IdlAccount(IdlAccount { - name: acc.ident.to_string().to_mixed_case(), - is_mut: acc.constraints.is_mutable(), - is_signer: match acc.ty { - Ty::Signer => true, - _ => acc.constraints.is_signer(), - }, - pda: pda::parse(ctx, accounts, acc, seeds_feature), - }), - }) - .collect::>() -} diff --git a/lang/syn/src/idl/mod.rs b/lang/syn/src/idl/mod.rs deleted file mode 100644 index 50bafb89..00000000 --- a/lang/syn/src/idl/mod.rs +++ /dev/null @@ -1,313 +0,0 @@ -use serde::{Deserialize, Serialize}; -use serde_json::Value as JsonValue; - -pub mod file; -pub mod pda; - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct Idl { - pub version: String, - pub name: String, - #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub constants: Vec, - pub instructions: Vec, - #[serde(skip_serializing_if = "Option::is_none", default)] - pub state: Option, - #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub accounts: Vec, - #[serde(skip_serializing_if = "Vec::is_empty", default)] - pub types: Vec, - #[serde(skip_serializing_if = "Option::is_none", default)] - pub events: Option>, - #[serde(skip_serializing_if = "Option::is_none", default)] - pub errors: Option>, - #[serde(skip_serializing_if = "Option::is_none", default)] - pub metadata: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct IdlConst { - pub name: String, - #[serde(rename = "type")] - pub ty: IdlType, - pub value: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct IdlState { - #[serde(rename = "struct")] - pub strct: IdlTypeDefinition, - pub methods: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct IdlInstruction { - pub name: String, - pub accounts: Vec, - pub args: Vec, - #[serde(skip_serializing_if = "Option::is_none")] - pub returns: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -#[serde(rename_all = "camelCase")] -pub struct IdlAccounts { - pub name: String, - pub accounts: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -#[serde(untagged)] -pub enum IdlAccountItem { - IdlAccount(IdlAccount), - IdlAccounts(IdlAccounts), -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -#[serde(rename_all = "camelCase")] -pub struct IdlAccount { - pub name: String, - pub is_mut: bool, - pub is_signer: bool, - #[serde(skip_serializing_if = "Option::is_none", default)] - pub pda: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -#[serde(rename_all = "camelCase")] -pub struct IdlPda { - pub seeds: Vec, - #[serde(skip_serializing_if = "Option::is_none", default)] - pub program_id: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -#[serde(rename_all = "camelCase", tag = "kind")] -pub enum IdlSeed { - Const(IdlSeedConst), - Arg(IdlSeedArg), - Account(IdlSeedAccount), -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -#[serde(rename_all = "camelCase")] -pub struct IdlSeedAccount { - #[serde(rename = "type")] - pub ty: IdlType, - // account_ty points to the entry in the "accounts" section. - // Some only if the `Account` type is used. - #[serde(skip_serializing_if = "Option::is_none")] - pub account: Option, - pub path: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -#[serde(rename_all = "camelCase")] -pub struct IdlSeedArg { - #[serde(rename = "type")] - pub ty: IdlType, - pub path: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -#[serde(rename_all = "camelCase")] -pub struct IdlSeedConst { - #[serde(rename = "type")] - pub ty: IdlType, - pub value: serde_json::Value, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct IdlField { - pub name: String, - #[serde(rename = "type")] - pub ty: IdlType, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct IdlEvent { - pub name: String, - pub fields: Vec, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct IdlEventField { - pub name: String, - #[serde(rename = "type")] - pub ty: IdlType, - pub index: bool, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct IdlTypeDefinition { - pub name: String, - #[serde(rename = "type")] - pub ty: IdlTypeDefinitionTy, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -#[serde(rename_all = "lowercase", tag = "kind")] -pub enum IdlTypeDefinitionTy { - Struct { fields: Vec }, - Enum { variants: Vec }, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct IdlEnumVariant { - pub name: String, - #[serde(skip_serializing_if = "Option::is_none", default)] - pub fields: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -#[serde(untagged)] -pub enum EnumFields { - Named(Vec), - Tuple(Vec), -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -#[serde(rename_all = "camelCase")] -pub enum IdlType { - Bool, - U8, - I8, - U16, - I16, - U32, - I32, - F32, - U64, - I64, - F64, - U128, - I128, - Bytes, - String, - PublicKey, - Defined(String), - Option(Box), - Vec(Box), - Array(Box, usize), -} - -impl std::str::FromStr for IdlType { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - let mut s = s.to_string(); - fn array_from_str(inner: &str) -> IdlType { - match inner.strip_suffix(']') { - None => { - let (raw_type, raw_length) = inner.rsplit_once(';').unwrap(); - let ty = IdlType::from_str(raw_type).unwrap(); - let len = raw_length.replace('_', "").parse::().unwrap(); - IdlType::Array(Box::new(ty), len) - } - Some(nested_inner) => array_from_str(&nested_inner[1..]), - } - } - s.retain(|c| !c.is_whitespace()); - - let r = match s.as_str() { - "bool" => IdlType::Bool, - "u8" => IdlType::U8, - "i8" => IdlType::I8, - "u16" => IdlType::U16, - "i16" => IdlType::I16, - "u32" => IdlType::U32, - "i32" => IdlType::I32, - "f32" => IdlType::F32, - "u64" => IdlType::U64, - "i64" => IdlType::I64, - "f64" => IdlType::F64, - "u128" => IdlType::U128, - "i128" => IdlType::I128, - "Vec" => IdlType::Bytes, - "String" | "&str" => IdlType::String, - "Pubkey" => IdlType::PublicKey, - _ => match s.to_string().strip_prefix("Option<") { - None => match s.to_string().strip_prefix("Vec<") { - None => { - if s.to_string().starts_with('[') { - array_from_str(&s) - } else { - IdlType::Defined(s.to_string()) - } - } - Some(inner) => { - let inner_ty = Self::from_str( - inner - .strip_suffix('>') - .ok_or_else(|| anyhow::anyhow!("Invalid option"))?, - )?; - IdlType::Vec(Box::new(inner_ty)) - } - }, - Some(inner) => { - let inner_ty = Self::from_str( - inner - .strip_suffix('>') - .ok_or_else(|| anyhow::anyhow!("Invalid option"))?, - )?; - IdlType::Option(Box::new(inner_ty)) - } - }, - }; - Ok(r) - } -} - -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct IdlErrorCode { - pub code: u32, - pub name: String, - #[serde(skip_serializing_if = "Option::is_none", default)] - pub msg: Option, -} - -#[cfg(test)] -mod tests { - use crate::idl::IdlType; - use std::str::FromStr; - - #[test] - fn multidimensional_array() { - assert_eq!( - IdlType::from_str("[[u8;16];32]").unwrap(), - IdlType::Array(Box::new(IdlType::Array(Box::new(IdlType::U8), 16)), 32) - ); - } - - #[test] - fn array() { - assert_eq!( - IdlType::from_str("[Pubkey;16]").unwrap(), - IdlType::Array(Box::new(IdlType::PublicKey), 16) - ); - } - - #[test] - fn array_with_underscored_length() { - assert_eq!( - IdlType::from_str("[u8;50_000]").unwrap(), - IdlType::Array(Box::new(IdlType::U8), 50000) - ); - } - - #[test] - fn option() { - assert_eq!( - IdlType::from_str("Option").unwrap(), - IdlType::Option(Box::new(IdlType::Bool)) - ) - } - - #[test] - fn vector() { - assert_eq!( - IdlType::from_str("Vec").unwrap(), - IdlType::Vec(Box::new(IdlType::Bool)) - ) - } -} diff --git a/lang/syn/src/idl/pda.rs b/lang/syn/src/idl/pda.rs deleted file mode 100644 index 537e58f5..00000000 --- a/lang/syn/src/idl/pda.rs +++ /dev/null @@ -1,322 +0,0 @@ -use crate::idl::*; -use crate::parser; -use crate::parser::context::CrateContext; -use crate::ConstraintSeedsGroup; -use crate::{AccountsStruct, Field}; -use std::collections::HashMap; -use std::str::FromStr; -use syn::Expr; - -// Parses a seeds constraint, extracting the IdlSeed types. -// -// Note: This implementation makes assumptions about the types that can be used -// (e.g., no program-defined function calls in seeds). -// -// This probably doesn't cover all cases. If you see a warning log, you -// can add a new case here. In the worst case, we miss a seed and -// the parser will treat the given seeds as empty and so clients will -// simply fail to automatically populate the PDA accounts. -// -// Seed Assumptions: Seeds must be of one of the following forms: -// -// - instruction argument. -// - account context field pubkey. -// - account data, where the account is defined in the current program. -// We make an exception for the SPL token program, since it is so common -// and sometimes convenient to use fields as a seed (e.g. Auction house -// program). In the case of nested structs/account data, all nested structs -// must be defined in the current program as well. -// - byte string literal (e.g. b"MY_SEED"). -// - byte string literal constant (e.g. `pub const MY_SEED: [u8; 2] = *b"hi";`). -// - array constants. -// -pub fn parse( - ctx: &CrateContext, - accounts: &AccountsStruct, - acc: &Field, - seeds_feature: bool, -) -> Option { - if !seeds_feature { - return None; - } - let pda_parser = PdaParser::new(ctx, accounts); - acc.constraints - .seeds - .as_ref() - .map(|s| pda_parser.parse(s)) - .unwrap_or(None) -} - -struct PdaParser<'a> { - ctx: &'a CrateContext, - // Accounts context. - accounts: &'a AccountsStruct, - // Maps var name to var type. These are the instruction arguments in a - // given accounts context. - ix_args: HashMap, - // Constants available in the crate. - const_names: Vec, - // All field names of the accounts in the accounts context. - account_field_names: Vec, -} - -impl<'a> PdaParser<'a> { - fn new(ctx: &'a CrateContext, accounts: &'a AccountsStruct) -> Self { - // All the available sources of seeds. - let ix_args = accounts.instruction_args().unwrap_or_default(); - let const_names: Vec = ctx.consts().map(|c| c.ident.to_string()).collect(); - let account_field_names = accounts.field_names(); - - Self { - ctx, - accounts, - ix_args, - const_names, - account_field_names, - } - } - - fn parse(&self, seeds_grp: &ConstraintSeedsGroup) -> Option { - // Extract the idl seed types from the constraints. - let seeds = seeds_grp - .seeds - .iter() - .map(|s| self.parse_seed(s)) - .collect::>>()?; - - // Parse the program id from the constraints. - let program_id = seeds_grp - .program_seed - .as_ref() - .map(|pid| self.parse_seed(pid)) - .unwrap_or_default(); - - // Done. - Some(IdlPda { seeds, program_id }) - } - - fn parse_seed(&self, seed: &Expr) -> Option { - match seed { - Expr::MethodCall(_) => { - let seed_path = parse_seed_path(seed)?; - - if self.is_instruction(&seed_path) { - self.parse_instruction(&seed_path) - } else if self.is_const(&seed_path) { - self.parse_const(&seed_path) - } else if self.is_account(&seed_path) { - self.parse_account(&seed_path) - } else if self.is_str_literal(&seed_path) { - self.parse_str_literal(&seed_path) - } else { - println!("WARNING: unexpected seed category for var: {:?}", seed_path); - None - } - } - Expr::Reference(expr_reference) => self.parse_seed(&expr_reference.expr), - Expr::Index(_) => { - println!("WARNING: auto pda derivation not currently supported for slice literals"); - None - } - // Unknown type. Please file an issue. - _ => { - println!("WARNING: unexpected seed: {:?}", seed); - None - } - } - } - - fn parse_instruction(&self, seed_path: &SeedPath) -> Option { - let idl_ty = IdlType::from_str(self.ix_args.get(&seed_path.name()).unwrap()).ok()?; - Some(IdlSeed::Arg(IdlSeedArg { - ty: idl_ty, - path: seed_path.path(), - })) - } - - fn parse_const(&self, seed_path: &SeedPath) -> Option { - // Pull in the constant value directly into the IDL. - assert!(seed_path.components().is_empty()); - let const_item = self - .ctx - .consts() - .find(|c| c.ident == seed_path.name()) - .unwrap(); - let idl_ty = IdlType::from_str(&parser::tts_to_string(&const_item.ty)).ok()?; - let mut idl_ty_value = parser::tts_to_string(&const_item.expr); - - if let IdlType::Array(_ty, _size) = &idl_ty { - // Convert str literal to array. - if idl_ty_value.contains("b\"") { - let components: Vec<&str> = idl_ty_value.split('b').collect(); - assert!(components.len() == 2); - let mut str_lit = components[1].to_string(); - str_lit.retain(|c| c != '"'); - idl_ty_value = format!("{:?}", str_lit.as_bytes()); - } - } - - Some(IdlSeed::Const(IdlSeedConst { - ty: idl_ty, - value: serde_json::from_str(&idl_ty_value).unwrap(), - })) - } - - fn parse_account(&self, seed_path: &SeedPath) -> Option { - // Get the anchor account field from the derive accounts struct. - let account_field = self - .accounts - .fields - .iter() - .find(|field| *field.ident() == seed_path.name()) - .unwrap(); - - // Follow the path to find the seed type. - let ty = { - let mut path = seed_path.components(); - match path.len() { - 0 => IdlType::PublicKey, - 1 => { - // Name of the account struct. - let account = account_field.ty_name()?; - if account == "TokenAccount" { - assert!(path.len() == 1); - match path[0].as_str() { - "mint" => IdlType::PublicKey, - "amount" => IdlType::U64, - "authority" => IdlType::PublicKey, - "delegated_amount" => IdlType::U64, - _ => { - println!("WARNING: token field isn't supported: {}", &path[0]); - return None; - } - } - } else { - // Get the rust representation of the field's struct. - let strct = self.ctx.structs().find(|s| s.ident == account).unwrap(); - parse_field_path(self.ctx, strct, &mut path) - } - } - _ => panic!("invariant violation"), - } - }; - - Some(IdlSeed::Account(IdlSeedAccount { - ty, - account: account_field.ty_name(), - path: seed_path.path(), - })) - } - - fn parse_str_literal(&self, seed_path: &SeedPath) -> Option { - let mut var_name = seed_path.name(); - // Remove the byte `b` prefix if the string is of the form `b"seed". - if var_name.starts_with("b\"") { - var_name.remove(0); - } - let value_string: String = var_name.chars().filter(|c| *c != '"').collect(); - Some(IdlSeed::Const(IdlSeedConst { - value: serde_json::Value::String(value_string), - ty: IdlType::String, - })) - } - - fn is_instruction(&self, seed_path: &SeedPath) -> bool { - self.ix_args.contains_key(&seed_path.name()) - } - - fn is_const(&self, seed_path: &SeedPath) -> bool { - self.const_names.contains(&seed_path.name()) - } - - fn is_account(&self, seed_path: &SeedPath) -> bool { - self.account_field_names.contains(&seed_path.name()) - } - - fn is_str_literal(&self, seed_path: &SeedPath) -> bool { - seed_path.components().is_empty() && seed_path.name().contains('"') - } -} - -// SeedPath represents the deconstructed syntax of a single pda seed, -// consisting of a variable name and a vec of all the sub fields accessed -// on that variable name. For example, if a seed is `my_field.my_data.as_ref()`, -// then the field name is `my_field` and the vec of sub fields is `[my_data]`. -#[derive(Debug)] -struct SeedPath(String, Vec); - -impl SeedPath { - fn name(&self) -> String { - self.0.clone() - } - - // Full path to the data this seed represents. - fn path(&self) -> String { - match self.1.len() { - 0 => self.0.clone(), - _ => format!("{}.{}", self.name(), self.components().join(".")), - } - } - - // All path components for the subfields accessed on this seed. - fn components(&self) -> &[String] { - &self.1 - } -} - -// Extracts the seed path from a single seed expression. -fn parse_seed_path(seed: &Expr) -> Option { - // Convert the seed into the raw string representation. - let seed_str = parser::tts_to_string(&seed); - - // Break up the seed into each sub field component. - let mut components: Vec<&str> = seed_str.split(" . ").collect(); - if components.len() <= 1 { - println!("WARNING: seeds are in an unexpected format: {:?}", seed); - return None; - } - - // The name of the variable (or field). - let name = components.remove(0).to_string(); - - // The path to the seed (only if the `name` type is a struct). - let mut path = Vec::new(); - while !components.is_empty() { - let c = components.remove(0); - if c.contains("()") { - break; - } - path.push(c.to_string()); - } - if path.len() == 1 && (path[0] == "key" || path[0] == "key()") { - path = Vec::new(); - } - - Some(SeedPath(name, path)) -} - -fn parse_field_path(ctx: &CrateContext, strct: &syn::ItemStruct, path: &mut &[String]) -> IdlType { - let field_name = &path[0]; - *path = &path[1..]; - - // Get the type name for the field. - let next_field = strct - .fields - .iter() - .find(|f| &f.ident.clone().unwrap().to_string() == field_name) - .unwrap(); - let next_field_ty_str = parser::tts_to_string(&next_field.ty); - - // The path is empty so this must be a primitive type. - if path.is_empty() { - return next_field_ty_str.parse().unwrap(); - } - - // Get the rust representation of hte field's struct. - let strct = ctx - .structs() - .find(|s| s.ident == next_field_ty_str) - .unwrap(); - - parse_field_path(ctx, strct, path) -} diff --git a/lang/syn/src/lib.rs b/lang/syn/src/lib.rs deleted file mode 100644 index 7bde237c..00000000 --- a/lang/syn/src/lib.rs +++ /dev/null @@ -1,925 +0,0 @@ -use codegen::accounts as accounts_codegen; -use codegen::program as program_codegen; -use parser::accounts as accounts_parser; -use parser::program as program_parser; -use proc_macro2::{Span, TokenStream}; -use quote::quote; -use quote::ToTokens; -use std::collections::HashMap; -use std::ops::Deref; -use syn::ext::IdentExt; -use syn::parse::{Error as ParseError, Parse, ParseStream, Result as ParseResult}; -use syn::punctuated::Punctuated; -use syn::spanned::Spanned; -use syn::token::Comma; -use syn::{ - Expr, Generics, Ident, ImplItemMethod, ItemEnum, ItemFn, ItemImpl, ItemMod, ItemStruct, LitInt, - LitStr, PatType, Token, Type, TypePath, -}; - -pub mod codegen; -#[cfg(feature = "hash")] -pub mod hash; -#[cfg(not(feature = "hash"))] -pub(crate) mod hash; -#[cfg(feature = "idl")] -pub mod idl; -pub mod parser; - -#[derive(Debug)] -pub struct Program { - pub state: Option, - pub ixs: Vec, - pub name: Ident, - pub program_mod: ItemMod, - pub fallback_fn: Option, -} - -impl Parse for Program { - fn parse(input: ParseStream) -> ParseResult { - let program_mod = ::parse(input)?; - program_parser::parse(program_mod) - } -} - -impl From<&Program> for TokenStream { - fn from(program: &Program) -> Self { - program_codegen::generate(program) - } -} - -impl ToTokens for Program { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend::(self.into()); - } -} - -#[derive(Debug)] -pub struct State { - pub name: String, - pub strct: ItemStruct, - pub ctor_and_anchor: Option<(ImplItemMethod, Ident)>, - pub impl_block_and_methods: Option<(ItemImpl, Vec)>, - pub interfaces: Option>, - pub is_zero_copy: bool, -} - -#[derive(Debug)] -pub struct StateIx { - pub raw_method: ImplItemMethod, - pub ident: Ident, - pub args: Vec, - pub anchor_ident: Ident, - // True if there exists a &self on the method. - pub has_receiver: bool, -} - -#[derive(Debug)] -pub struct StateInterface { - pub trait_name: String, - pub methods: Vec, -} - -#[derive(Debug)] -pub struct Ix { - pub raw_method: ItemFn, - pub ident: Ident, - pub args: Vec, - pub returns: IxReturn, - // The ident for the struct deriving Accounts. - pub anchor_ident: Ident, -} - -#[derive(Debug)] -pub struct IxArg { - pub name: Ident, - pub raw_arg: PatType, -} - -#[derive(Debug)] -pub struct IxReturn { - pub ty: Type, -} - -#[derive(Debug)] -pub struct FallbackFn { - raw_method: ItemFn, -} - -#[derive(Debug)] -pub struct AccountsStruct { - // Name of the accounts struct. - pub ident: Ident, - // Generics + lifetimes on the accounts struct. - pub generics: Generics, - // Fields on the accounts struct. - pub fields: Vec, - // Instruction data api expression. - instruction_api: Option>, -} - -impl Parse for AccountsStruct { - fn parse(input: ParseStream) -> ParseResult { - let strct = ::parse(input)?; - accounts_parser::parse(&strct) - } -} - -impl From<&AccountsStruct> for TokenStream { - fn from(accounts: &AccountsStruct) -> Self { - accounts_codegen::generate(accounts) - } -} - -impl ToTokens for AccountsStruct { - fn to_tokens(&self, tokens: &mut TokenStream) { - tokens.extend::(self.into()); - } -} - -impl AccountsStruct { - pub fn new( - strct: ItemStruct, - fields: Vec, - instruction_api: Option>, - ) -> Self { - let ident = strct.ident.clone(); - let generics = strct.generics; - Self { - ident, - generics, - fields, - instruction_api, - } - } - - // Return value maps instruction name to type. - // E.g. if we have `#[instruction(data: u64)]` then returns - // { "data": "u64"}. - pub fn instruction_args(&self) -> Option> { - self.instruction_api.as_ref().map(|instruction_api| { - instruction_api - .iter() - .map(|expr| { - let arg = parser::tts_to_string(&expr); - let components: Vec<&str> = arg.split(" : ").collect(); - assert!(components.len() == 2); - (components[0].to_string(), components[1].to_string()) - }) - .collect() - }) - } - - pub fn field_names(&self) -> Vec { - self.fields - .iter() - .map(|field| field.ident().to_string()) - .collect() - } -} - -#[allow(clippy::large_enum_variant)] -#[derive(Debug)] -pub enum AccountField { - Field(Field), - CompositeField(CompositeField), -} - -impl AccountField { - fn ident(&self) -> &Ident { - match self { - AccountField::Field(field) => &field.ident, - AccountField::CompositeField(c_field) => &c_field.ident, - } - } - - pub fn ty_name(&self) -> Option { - match self { - AccountField::Field(field) => match &field.ty { - Ty::Account(account) => Some(parser::tts_to_string(&account.account_type_path)), - Ty::ProgramAccount(account) => { - Some(parser::tts_to_string(&account.account_type_path)) - } - _ => None, - }, - AccountField::CompositeField(field) => Some(field.symbol.clone()), - } - } -} - -#[derive(Debug)] -pub struct Field { - pub ident: Ident, - pub constraints: ConstraintGroup, - pub instruction_constraints: ConstraintGroup, - pub ty: Ty, - /// Documentation string. - pub docs: String, -} - -impl Field { - pub fn typed_ident(&self) -> proc_macro2::TokenStream { - let name = &self.ident; - let ty_decl = self.ty_decl(); - quote! { - #name: #ty_decl - } - } - - pub fn ty_decl(&self) -> proc_macro2::TokenStream { - let account_ty = self.account_ty(); - let container_ty = self.container_ty(); - match &self.ty { - Ty::AccountInfo => quote! { - AccountInfo - }, - Ty::UncheckedAccount => quote! { - UncheckedAccount - }, - Ty::Signer => quote! { - Signer - }, - Ty::ProgramData => quote! { - ProgramData - }, - Ty::SystemAccount => quote! { - SystemAccount - }, - Ty::Account(AccountTy { boxed, .. }) => { - if *boxed { - quote! { - Box<#container_ty<#account_ty>> - } - } else { - quote! { - #container_ty<#account_ty> - } - } - } - Ty::Sysvar(ty) => { - let account = match ty { - SysvarTy::Clock => quote! {Clock}, - SysvarTy::Rent => quote! {Rent}, - SysvarTy::EpochSchedule => quote! {EpochSchedule}, - SysvarTy::Fees => quote! {Fees}, - SysvarTy::RecentBlockhashes => quote! {RecentBlockhashes}, - SysvarTy::SlotHashes => quote! {SlotHashes}, - SysvarTy::SlotHistory => quote! {SlotHistory}, - SysvarTy::StakeHistory => quote! {StakeHistory}, - SysvarTy::Instructions => quote! {Instructions}, - SysvarTy::Rewards => quote! {Rewards}, - }; - quote! { - Sysvar<#account> - } - } - _ => quote! { - #container_ty<#account_ty> - }, - } - } - - // TODO: remove the option once `CpiAccount` is completely removed (not - // just deprecated). - pub fn from_account_info( - &self, - kind: Option<&InitKind>, - checked: bool, - ) -> proc_macro2::TokenStream { - let field = &self.ident; - let container_ty = self.container_ty(); - let owner_addr = match &kind { - None => quote! { program_id }, - Some(InitKind::Program { .. }) => quote! { - program_id - }, - _ => quote! { - &anchor_spl::token::ID - }, - }; - match &self.ty { - Ty::AccountInfo => quote! { #field.to_account_info() }, - Ty::UncheckedAccount => { - quote! { UncheckedAccount::try_from(#field.to_account_info()) } - } - Ty::Account(AccountTy { boxed, .. }) => { - let stream = if checked { - quote! { - #container_ty::try_from( - &#field, - )? - } - } else { - quote! { - #container_ty::try_from_unchecked( - &#field, - )? - } - }; - if *boxed { - quote! { - Box::new(#stream) - } - } else { - stream - } - } - Ty::CpiAccount(_) => { - if checked { - quote! { - #container_ty::try_from( - &#field, - )? - } - } else { - quote! { - #container_ty::try_from_unchecked( - &#field, - )? - } - } - } - Ty::AccountLoader(_) => { - if checked { - quote! { - #container_ty::try_from( - &#field, - )? - } - } else { - quote! { - #container_ty::try_from_unchecked( - #owner_addr, - &#field, - )? - } - } - } - _ => { - if checked { - quote! { - #container_ty::try_from( - #owner_addr, - &#field, - )? - } - } else { - quote! { - #container_ty::try_from_unchecked( - #owner_addr, - &#field, - )? - } - } - } - } - } - - pub fn container_ty(&self) -> proc_macro2::TokenStream { - match &self.ty { - Ty::ProgramAccount(_) => quote! { - anchor_lang::accounts::program_account::ProgramAccount - }, - Ty::Account(_) => quote! { - anchor_lang::accounts::account::Account - }, - Ty::AccountLoader(_) => quote! { - anchor_lang::accounts::account_loader::AccountLoader - }, - Ty::Loader(_) => quote! { - anchor_lang::accounts::loader::Loader - }, - Ty::CpiAccount(_) => quote! { - anchor_lang::accounts::cpi_account::CpiAccount - }, - Ty::Sysvar(_) => quote! { anchor_lang::accounts::sysvar::Sysvar }, - Ty::CpiState(_) => quote! { anchor_lang::accounts::cpi_state::CpiState }, - Ty::ProgramState(_) => quote! { anchor_lang::accounts::state::ProgramState }, - Ty::Program(_) => quote! { anchor_lang::accounts::program::Program }, - Ty::AccountInfo => quote! {}, - Ty::UncheckedAccount => quote! {}, - Ty::Signer => quote! {}, - Ty::SystemAccount => quote! {}, - Ty::ProgramData => quote! {}, - } - } - - // Returns the inner account struct type. - pub fn account_ty(&self) -> proc_macro2::TokenStream { - match &self.ty { - Ty::AccountInfo => quote! { - AccountInfo - }, - Ty::UncheckedAccount => quote! { - UncheckedAccount - }, - Ty::Signer => quote! { - Signer - }, - Ty::SystemAccount => quote! { - SystemAccount - }, - Ty::ProgramData => quote! { - ProgramData - }, - Ty::ProgramAccount(ty) => { - let ident = &ty.account_type_path; - quote! { - #ident - } - } - Ty::Account(ty) => { - let ident = &ty.account_type_path; - quote! { - #ident - } - } - Ty::AccountLoader(ty) => { - let ident = &ty.account_type_path; - quote! { - #ident - } - } - Ty::Loader(ty) => { - let ident = &ty.account_type_path; - quote! { - #ident - } - } - Ty::CpiAccount(ty) => { - let ident = &ty.account_type_path; - quote! { - #ident - } - } - Ty::ProgramState(ty) => { - let account = &ty.account_type_path; - quote! { - #account - } - } - Ty::CpiState(ty) => { - let account = &ty.account_type_path; - quote! { - #account - } - } - Ty::Sysvar(ty) => match ty { - SysvarTy::Clock => quote! {Clock}, - SysvarTy::Rent => quote! {Rent}, - SysvarTy::EpochSchedule => quote! {EpochSchedule}, - SysvarTy::Fees => quote! {Fees}, - SysvarTy::RecentBlockhashes => quote! {RecentBlockhashes}, - SysvarTy::SlotHashes => quote! {SlotHashes}, - SysvarTy::SlotHistory => quote! {SlotHistory}, - SysvarTy::StakeHistory => quote! {StakeHistory}, - SysvarTy::Instructions => quote! {Instructions}, - SysvarTy::Rewards => quote! {Rewards}, - }, - Ty::Program(ty) => { - let program = &ty.account_type_path; - quote! { - #program - } - } - } - } -} - -#[derive(Debug)] -pub struct CompositeField { - pub ident: Ident, - pub constraints: ConstraintGroup, - pub instruction_constraints: ConstraintGroup, - pub symbol: String, - pub raw_field: syn::Field, - /// Documentation string. - pub docs: String, -} - -// A type of an account field. -#[derive(Debug, PartialEq)] -pub enum Ty { - AccountInfo, - UncheckedAccount, - ProgramState(ProgramStateTy), - CpiState(CpiStateTy), - ProgramAccount(ProgramAccountTy), - Loader(LoaderTy), - AccountLoader(AccountLoaderTy), - CpiAccount(CpiAccountTy), - Sysvar(SysvarTy), - Account(AccountTy), - Program(ProgramTy), - Signer, - SystemAccount, - ProgramData, -} - -#[derive(Debug, PartialEq)] -pub enum SysvarTy { - Clock, - Rent, - EpochSchedule, - Fees, - RecentBlockhashes, - SlotHashes, - SlotHistory, - StakeHistory, - Instructions, - Rewards, -} - -#[derive(Debug, PartialEq)] -pub struct ProgramStateTy { - pub account_type_path: TypePath, -} - -#[derive(Debug, PartialEq)] -pub struct CpiStateTy { - pub account_type_path: TypePath, -} - -#[derive(Debug, PartialEq)] -pub struct ProgramAccountTy { - // The struct type of the account. - pub account_type_path: TypePath, -} - -#[derive(Debug, PartialEq)] -pub struct CpiAccountTy { - // The struct type of the account. - pub account_type_path: TypePath, -} - -#[derive(Debug, PartialEq)] -pub struct AccountLoaderTy { - // The struct type of the account. - pub account_type_path: TypePath, -} - -#[derive(Debug, PartialEq)] -pub struct LoaderTy { - // The struct type of the account. - pub account_type_path: TypePath, -} - -#[derive(Debug, PartialEq)] -pub struct AccountTy { - // The struct type of the account. - pub account_type_path: TypePath, - // True if the account has been boxed via `Box`. - pub boxed: bool, -} - -#[derive(Debug, PartialEq)] -pub struct ProgramTy { - // The struct type of the account. - pub account_type_path: TypePath, -} - -#[derive(Debug)] -pub struct Error { - pub name: String, - pub raw_enum: ItemEnum, - pub ident: Ident, - pub codes: Vec, - pub args: Option, -} - -#[derive(Debug)] -pub struct ErrorArgs { - pub offset: LitInt, -} - -impl Parse for ErrorArgs { - fn parse(stream: ParseStream) -> ParseResult { - let offset_span = stream.span(); - let offset = stream.call(Ident::parse_any)?; - if offset.to_string().as_str() != "offset" { - return Err(ParseError::new(offset_span, "expected keyword offset")); - } - stream.parse::()?; - Ok(ErrorArgs { - offset: stream.parse()?, - }) - } -} - -#[derive(Debug)] -pub struct ErrorCode { - pub id: u32, - pub ident: Ident, - pub msg: Option, -} - -// All well formed constraints on a single `Accounts` field. -#[derive(Debug, Default, Clone)] -pub struct ConstraintGroup { - init: Option, - zeroed: Option, - mutable: Option, - signer: Option, - owner: Option, - rent_exempt: Option, - seeds: Option, - executable: Option, - state: Option, - has_one: Vec, - literal: Vec, - raw: Vec, - close: Option, - address: Option, - associated_token: Option, - token_account: Option, - mint: Option, -} - -impl ConstraintGroup { - pub fn is_zeroed(&self) -> bool { - self.zeroed.is_some() - } - - pub fn is_mutable(&self) -> bool { - self.mutable.is_some() - } - - pub fn is_signer(&self) -> bool { - self.signer.is_some() - } - - pub fn is_close(&self) -> bool { - self.close.is_some() - } -} - -// A single account constraint *after* merging all tokens into a well formed -// constraint. Some constraints like "seeds" are defined by multiple -// tokens, so a merging phase is required. -#[allow(clippy::large_enum_variant)] -#[derive(Debug)] -pub enum Constraint { - Init(ConstraintInitGroup), - Zeroed(ConstraintZeroed), - Mut(ConstraintMut), - Signer(ConstraintSigner), - HasOne(ConstraintHasOne), - Literal(ConstraintLiteral), - Raw(ConstraintRaw), - Owner(ConstraintOwner), - RentExempt(ConstraintRentExempt), - Seeds(ConstraintSeedsGroup), - AssociatedToken(ConstraintAssociatedToken), - Executable(ConstraintExecutable), - State(ConstraintState), - Close(ConstraintClose), - Address(ConstraintAddress), - TokenAccount(ConstraintTokenAccountGroup), - Mint(ConstraintTokenMintGroup), -} - -// Constraint token is a single keyword in a `#[account()]` attribute. -#[allow(clippy::large_enum_variant)] -#[derive(Debug)] -pub enum ConstraintToken { - Init(Context), - Zeroed(Context), - Mut(Context), - Signer(Context), - HasOne(Context), - Literal(Context), - Raw(Context), - Owner(Context), - RentExempt(Context), - Seeds(Context), - Executable(Context), - State(Context), - Close(Context), - Payer(Context), - Space(Context), - Address(Context), - TokenMint(Context), - TokenAuthority(Context), - AssociatedTokenMint(Context), - AssociatedTokenAuthority(Context), - MintAuthority(Context), - MintFreezeAuthority(Context), - MintDecimals(Context), - Bump(Context), - ProgramSeed(Context), -} - -impl Parse for ConstraintToken { - fn parse(stream: ParseStream) -> ParseResult { - accounts_parser::constraints::parse_token(stream) - } -} - -#[derive(Debug, Clone)] -pub struct ConstraintInit { - pub if_needed: bool, -} - -#[derive(Debug, Clone)] -pub struct ConstraintInitIfNeeded {} - -#[derive(Debug, Clone)] -pub struct ConstraintZeroed {} - -#[derive(Debug, Clone)] -pub struct ConstraintMut { - pub error: Option, -} - -#[derive(Debug, Clone)] -pub struct ConstraintSigner { - pub error: Option, -} - -#[derive(Debug, Clone)] -pub struct ConstraintHasOne { - pub join_target: Expr, - pub error: Option, -} - -#[derive(Debug, Clone)] -pub struct ConstraintLiteral { - pub lit: LitStr, -} - -#[derive(Debug, Clone)] -pub struct ConstraintRaw { - pub raw: Expr, - pub error: Option, -} - -#[derive(Debug, Clone)] -pub struct ConstraintOwner { - pub owner_address: Expr, - pub error: Option, -} - -#[derive(Debug, Clone)] -pub struct ConstraintAddress { - pub address: Expr, - pub error: Option, -} - -#[derive(Debug, Clone)] -pub enum ConstraintRentExempt { - Enforce, - Skip, -} - -#[derive(Debug, Clone)] -pub struct ConstraintInitGroup { - pub if_needed: bool, - pub seeds: Option, - pub payer: Expr, - pub space: Option, - pub kind: InitKind, -} - -#[derive(Debug, Clone)] -pub struct ConstraintSeedsGroup { - pub is_init: bool, - pub seeds: Punctuated, - pub bump: Option, // None => bump was given without a target. - pub program_seed: Option, // None => use the current program's program_id. -} - -#[derive(Debug, Clone)] -pub struct ConstraintSeeds { - pub seeds: Punctuated, -} - -#[derive(Debug, Clone)] -pub struct ConstraintExecutable {} - -#[derive(Debug, Clone)] -pub struct ConstraintState { - pub program_target: Ident, -} - -#[derive(Debug, Clone)] -pub struct ConstraintPayer { - pub target: Expr, -} - -#[derive(Debug, Clone)] -pub struct ConstraintSpace { - pub space: Expr, -} - -#[derive(Debug, Clone)] -#[allow(clippy::large_enum_variant)] -pub enum InitKind { - Program { - owner: Option, - }, - // Owner for token and mint represents the authority. Not to be confused - // with the owner of the AccountInfo. - Token { - owner: Expr, - mint: Expr, - }, - AssociatedToken { - owner: Expr, - mint: Expr, - }, - Mint { - owner: Expr, - freeze_authority: Option, - decimals: Expr, - }, -} - -#[derive(Debug, Clone)] -pub struct ConstraintClose { - pub sol_dest: Ident, -} - -#[derive(Debug, Clone)] -pub struct ConstraintTokenMint { - mint: Expr, -} - -#[derive(Debug, Clone)] -pub struct ConstraintTokenAuthority { - auth: Expr, -} - -#[derive(Debug, Clone)] -pub struct ConstraintMintAuthority { - mint_auth: Expr, -} - -#[derive(Debug, Clone)] -pub struct ConstraintMintFreezeAuthority { - mint_freeze_auth: Expr, -} - -#[derive(Debug, Clone)] -pub struct ConstraintMintDecimals { - decimals: Expr, -} - -#[derive(Debug, Clone)] -pub struct ConstraintTokenBump { - bump: Option, -} - -#[derive(Debug, Clone)] -pub struct ConstraintProgramSeed { - program_seed: Expr, -} - -#[derive(Debug, Clone)] -pub struct ConstraintAssociatedToken { - pub wallet: Expr, - pub mint: Expr, -} - -#[derive(Debug, Clone)] -pub struct ConstraintTokenAccountGroup { - pub mint: Option, - pub authority: Option, -} - -#[derive(Debug, Clone)] -pub struct ConstraintTokenMintGroup { - pub decimals: Option, - pub mint_authority: Option, - pub freeze_authority: Option, -} - -// Syntaxt context object for preserving metadata about the inner item. -#[derive(Debug, Clone)] -pub struct Context { - span: Span, - inner: T, -} - -impl Context { - pub fn new(span: Span, inner: T) -> Self { - Self { span, inner } - } - - pub fn into_inner(self) -> T { - self.inner - } -} - -impl Deref for Context { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -impl Spanned for Context { - fn span(&self) -> Span { - self.span - } -} diff --git a/lang/syn/src/parser/accounts/constraints.rs b/lang/syn/src/parser/accounts/constraints.rs deleted file mode 100644 index d6ce11b1..00000000 --- a/lang/syn/src/parser/accounts/constraints.rs +++ /dev/null @@ -1,1043 +0,0 @@ -use crate::*; -use syn::ext::IdentExt; -use syn::parse::{Error as ParseError, Parse, ParseStream, Result as ParseResult}; -use syn::punctuated::Punctuated; -use syn::spanned::Spanned; -use syn::token::Comma; -use syn::{bracketed, Expr, Ident, LitStr, Token}; - -pub fn parse( - f: &syn::Field, - f_ty: Option<&Ty>, - has_instruction_api: bool, -) -> ParseResult<(ConstraintGroup, ConstraintGroup)> { - let mut constraints = ConstraintGroupBuilder::new(f_ty); - for attr in f.attrs.iter().filter(is_account) { - for c in attr.parse_args_with(Punctuated::::parse_terminated)? { - constraints.add(c)?; - } - } - let account_constraints = constraints.build()?; - let mut constraints = ConstraintGroupBuilder::new(f_ty); - for attr in f.attrs.iter().filter(is_instruction) { - if !has_instruction_api { - return Err(ParseError::new( - attr.span(), - "an instruction api must be declared", - )); - } - for c in attr.parse_args_with(Punctuated::::parse_terminated)? { - constraints.add(c)?; - } - } - let instruction_constraints = constraints.build()?; - - Ok((account_constraints, instruction_constraints)) -} - -pub fn is_account(attr: &&syn::Attribute) -> bool { - attr.path - .get_ident() - .map_or(false, |ident| ident == "account") -} - -pub fn is_instruction(attr: &&syn::Attribute) -> bool { - attr.path - .get_ident() - .map_or(false, |ident| ident == "instruction") -} - -// Parses a single constraint from a parse stream for `#[account()]`. -pub fn parse_token(stream: ParseStream) -> ParseResult { - let is_lit = stream.peek(LitStr); - if is_lit { - let lit: LitStr = stream.parse()?; - let c = ConstraintToken::Literal(Context::new(lit.span(), ConstraintLiteral { lit })); - return Ok(c); - } - - let ident = stream.call(Ident::parse_any)?; - let kw = ident.to_string(); - - let c = match kw.as_str() { - "init" => ConstraintToken::Init(Context::new( - ident.span(), - ConstraintInit { if_needed: false }, - )), - "init_if_needed" => ConstraintToken::Init(Context::new( - ident.span(), - ConstraintInit { if_needed: true }, - )), - "zero" => ConstraintToken::Zeroed(Context::new(ident.span(), ConstraintZeroed {})), - "mut" => ConstraintToken::Mut(Context::new( - ident.span(), - ConstraintMut { - error: parse_optional_custom_error(&stream)?, - }, - )), - "signer" => ConstraintToken::Signer(Context::new( - ident.span(), - ConstraintSigner { - error: parse_optional_custom_error(&stream)?, - }, - )), - "executable" => { - ConstraintToken::Executable(Context::new(ident.span(), ConstraintExecutable {})) - } - "mint" => { - stream.parse::()?; - stream.parse::()?; - let kw = stream.call(Ident::parse_any)?.to_string(); - stream.parse::()?; - - let span = ident - .span() - .join(stream.span()) - .unwrap_or_else(|| ident.span()); - - match kw.as_str() { - "authority" => ConstraintToken::MintAuthority(Context::new( - span, - ConstraintMintAuthority { - mint_auth: stream.parse()?, - }, - )), - "freeze_authority" => ConstraintToken::MintFreezeAuthority(Context::new( - span, - ConstraintMintFreezeAuthority { - mint_freeze_auth: stream.parse()?, - }, - )), - "decimals" => ConstraintToken::MintDecimals(Context::new( - span, - ConstraintMintDecimals { - decimals: stream.parse()?, - }, - )), - _ => return Err(ParseError::new(ident.span(), "Invalid attribute")), - } - } - "token" => { - stream.parse::()?; - stream.parse::()?; - let kw = stream.call(Ident::parse_any)?.to_string(); - stream.parse::()?; - - let span = ident - .span() - .join(stream.span()) - .unwrap_or_else(|| ident.span()); - - match kw.as_str() { - "mint" => ConstraintToken::TokenMint(Context::new( - span, - ConstraintTokenMint { - mint: stream.parse()?, - }, - )), - "authority" => ConstraintToken::TokenAuthority(Context::new( - span, - ConstraintTokenAuthority { - auth: stream.parse()?, - }, - )), - _ => return Err(ParseError::new(ident.span(), "Invalid attribute")), - } - } - "associated_token" => { - stream.parse::()?; - stream.parse::()?; - let kw = stream.call(Ident::parse_any)?.to_string(); - stream.parse::()?; - - let span = ident - .span() - .join(stream.span()) - .unwrap_or_else(|| ident.span()); - - match kw.as_str() { - "mint" => ConstraintToken::AssociatedTokenMint(Context::new( - span, - ConstraintTokenMint { - mint: stream.parse()?, - }, - )), - "authority" => ConstraintToken::AssociatedTokenAuthority(Context::new( - span, - ConstraintTokenAuthority { - auth: stream.parse()?, - }, - )), - _ => return Err(ParseError::new(ident.span(), "Invalid attribute")), - } - } - "bump" => { - let bump = { - if stream.peek(Token![=]) { - stream.parse::()?; - Some(stream.parse()?) - } else { - None - } - }; - ConstraintToken::Bump(Context::new(ident.span(), ConstraintTokenBump { bump })) - } - "seeds" => { - if stream.peek(Token![:]) { - stream.parse::()?; - stream.parse::()?; - let kw = stream.call(Ident::parse_any)?.to_string(); - stream.parse::()?; - - let span = ident - .span() - .join(stream.span()) - .unwrap_or_else(|| ident.span()); - - match kw.as_str() { - "program" => ConstraintToken::ProgramSeed(Context::new( - span, - ConstraintProgramSeed { - program_seed: stream.parse()?, - }, - )), - _ => return Err(ParseError::new(ident.span(), "Invalid attribute")), - } - } else { - stream.parse::()?; - let span = ident - .span() - .join(stream.span()) - .unwrap_or_else(|| ident.span()); - let seeds; - let bracket = bracketed!(seeds in stream); - ConstraintToken::Seeds(Context::new( - span.join(bracket.span).unwrap_or(span), - ConstraintSeeds { - seeds: seeds.parse_terminated(Expr::parse)?, - }, - )) - } - } - _ => { - stream.parse::()?; - let span = ident - .span() - .join(stream.span()) - .unwrap_or_else(|| ident.span()); - match kw.as_str() { - "has_one" => ConstraintToken::HasOne(Context::new( - span, - ConstraintHasOne { - join_target: stream.parse()?, - error: parse_optional_custom_error(&stream)?, - }, - )), - "owner" => ConstraintToken::Owner(Context::new( - span, - ConstraintOwner { - owner_address: stream.parse()?, - error: parse_optional_custom_error(&stream)?, - }, - )), - "rent_exempt" => ConstraintToken::RentExempt(Context::new( - span, - match stream.parse::()?.to_string().as_str() { - "skip" => ConstraintRentExempt::Skip, - "enforce" => ConstraintRentExempt::Enforce, - _ => { - return Err(ParseError::new( - span, - "rent_exempt must be either skip or enforce", - )) - } - }, - )), - "state" => ConstraintToken::State(Context::new( - span, - ConstraintState { - program_target: stream.parse()?, - }, - )), - "payer" => ConstraintToken::Payer(Context::new( - span, - ConstraintPayer { - target: stream.parse()?, - }, - )), - "space" => ConstraintToken::Space(Context::new( - span, - ConstraintSpace { - space: stream.parse()?, - }, - )), - "constraint" => ConstraintToken::Raw(Context::new( - span, - ConstraintRaw { - raw: stream.parse()?, - error: parse_optional_custom_error(&stream)?, - }, - )), - "close" => ConstraintToken::Close(Context::new( - span, - ConstraintClose { - sol_dest: stream.parse()?, - }, - )), - "address" => ConstraintToken::Address(Context::new( - span, - ConstraintAddress { - address: stream.parse()?, - error: parse_optional_custom_error(&stream)?, - }, - )), - _ => return Err(ParseError::new(ident.span(), "Invalid attribute")), - } - } - }; - - Ok(c) -} - -fn parse_optional_custom_error(stream: &ParseStream) -> ParseResult> { - if stream.peek(Token![@]) { - stream.parse::()?; - stream.parse().map(Some) - } else { - Ok(None) - } -} - -#[derive(Default)] -pub struct ConstraintGroupBuilder<'ty> { - pub f_ty: Option<&'ty Ty>, - pub init: Option>, - pub zeroed: Option>, - pub mutable: Option>, - pub signer: Option>, - pub has_one: Vec>, - pub literal: Vec>, - pub raw: Vec>, - pub owner: Option>, - pub rent_exempt: Option>, - pub seeds: Option>, - pub executable: Option>, - pub state: Option>, - pub payer: Option>, - pub space: Option>, - pub close: Option>, - pub address: Option>, - pub token_mint: Option>, - pub token_authority: Option>, - pub associated_token_mint: Option>, - pub associated_token_authority: Option>, - pub mint_authority: Option>, - pub mint_freeze_authority: Option>, - pub mint_decimals: Option>, - pub bump: Option>, - pub program_seed: Option>, -} - -impl<'ty> ConstraintGroupBuilder<'ty> { - pub fn new(f_ty: Option<&'ty Ty>) -> Self { - Self { - f_ty, - init: None, - zeroed: None, - mutable: None, - signer: None, - has_one: Vec::new(), - literal: Vec::new(), - raw: Vec::new(), - owner: None, - rent_exempt: None, - seeds: None, - executable: None, - state: None, - payer: None, - space: None, - close: None, - address: None, - token_mint: None, - token_authority: None, - associated_token_mint: None, - associated_token_authority: None, - mint_authority: None, - mint_freeze_authority: None, - mint_decimals: None, - bump: None, - program_seed: None, - } - } - - pub fn build(mut self) -> ParseResult { - // Init. - if let Some(i) = &self.init { - if cfg!(not(feature = "init-if-needed")) && i.if_needed { - return Err(ParseError::new( - i.span(), - "init_if_needed requires that anchor-lang be imported \ - with the init-if-needed cargo feature enabled. \ - Carefully read the init_if_needed docs before using this feature \ - to make sure you know how to protect yourself against \ - re-initialization attacks.", - )); - } - - match self.mutable { - Some(m) => { - return Err(ParseError::new( - m.span(), - "mut cannot be provided with init", - )) - } - None => self - .mutable - .replace(Context::new(i.span(), ConstraintMut { error: None })), - }; - // Rent exempt if not explicitly skipped. - if self.rent_exempt.is_none() { - self.rent_exempt - .replace(Context::new(i.span(), ConstraintRentExempt::Enforce)); - } - if self.payer.is_none() { - return Err(ParseError::new( - i.span(), - "payer must be provided when initializing an account", - )); - } - // When initializing a non-PDA account, the account being - // initialized must sign to invoke the system program's create - // account instruction. - if self.signer.is_none() && self.seeds.is_none() && self.associated_token_mint.is_none() - { - self.signer - .replace(Context::new(i.span(), ConstraintSigner { error: None })); - } - - // Assert a bump target is not given on init. - if let Some(b) = &self.bump { - if b.bump.is_some() { - return Err(ParseError::new( - b.span(), - "bump targets should not be provided with init. Please use bump without a target." - )); - } - } - - // TokenAccount. - if let Some(token_mint) = &self.token_mint { - if self.token_authority.is_none() { - return Err(ParseError::new( - token_mint.span(), - "when initializing, token authority must be provided if token mint is", - )); - } - } - if let Some(token_authority) = &self.token_authority { - if self.token_mint.is_none() { - return Err(ParseError::new( - token_authority.span(), - "when initializing, token mint must be provided if token authority is", - )); - } - } - - // Mint. - if let Some(mint_decimals) = &self.mint_decimals { - if self.mint_authority.is_none() { - return Err(ParseError::new( - mint_decimals.span(), - "when initializing, mint authority must be provided if mint decimals is", - )); - } - } - if let Some(mint_authority) = &self.mint_authority { - if self.mint_decimals.is_none() { - return Err(ParseError::new( - mint_authority.span(), - "when initializing, mint decimals must be provided if mint authority is", - )); - } - } - } - - // Zero. - if let Some(z) = &self.zeroed { - match self.mutable { - Some(m) => { - return Err(ParseError::new( - m.span(), - "mut cannot be provided with zeroed", - )) - } - None => self - .mutable - .replace(Context::new(z.span(), ConstraintMut { error: None })), - }; - // Rent exempt if not explicitly skipped. - if self.rent_exempt.is_none() { - self.rent_exempt - .replace(Context::new(z.span(), ConstraintRentExempt::Enforce)); - } - } - - // Seeds. - if let Some(i) = &self.seeds { - if self.init.is_some() && self.payer.is_none() { - return Err(ParseError::new( - i.span(), - "payer must be provided when creating a program derived address", - )); - } - if self.bump.is_none() { - return Err(ParseError::new( - i.span(), - "bump must be provided with seeds", - )); - } - } - - // Space. - if let Some(i) = &self.init { - let initializing_token_program_acc = self.token_mint.is_some() - || self.mint_authority.is_some() - || self.token_authority.is_some() - || self.associated_token_authority.is_some(); - - match (self.space.is_some(), initializing_token_program_acc) { - (true, true) => { - return Err(ParseError::new( - self.space.as_ref().unwrap().span(), - "space is not required for initializing an spl account", - )); - } - (false, false) => { - return Err(ParseError::new( - i.span(), - "space must be provided with init", - )); - } - _ => (), - } - } - - let ConstraintGroupBuilder { - f_ty: _, - init, - zeroed, - mutable, - signer, - has_one, - literal, - raw, - owner, - rent_exempt, - seeds, - executable, - state, - payer, - space, - close, - address, - token_mint, - token_authority, - associated_token_mint, - associated_token_authority, - mint_authority, - mint_freeze_authority, - mint_decimals, - bump, - program_seed, - } = self; - - // Converts Option> -> Option. - macro_rules! into_inner { - ($opt:ident) => { - $opt.map(|c| c.into_inner()) - }; - ($opt:expr) => { - $opt.map(|c| c.into_inner()) - }; - } - // Converts Vec> - Vec. - macro_rules! into_inner_vec { - ($opt:ident) => { - $opt.into_iter().map(|c| c.into_inner()).collect() - }; - } - - let is_init = init.is_some(); - let seeds = seeds.map(|c| ConstraintSeedsGroup { - is_init, - seeds: c.seeds.clone(), - bump: into_inner!(bump) - .map(|b| b.bump) - .expect("bump must be provided with seeds"), - program_seed: into_inner!(program_seed).map(|id| id.program_seed), - }); - let associated_token = match (associated_token_mint, associated_token_authority) { - (Some(mint), Some(auth)) => Some(ConstraintAssociatedToken { - wallet: auth.into_inner().auth, - mint: mint.into_inner().mint, - }), - (Some(mint), None) => return Err(ParseError::new( - mint.span(), - "authority must be provided to specify an associated token program derived address", - )), - (None, Some(auth)) => { - return Err(ParseError::new( - auth.span(), - "mint must be provided to specify an associated token program derived address", - )) - } - _ => None, - }; - let token_account = match (&token_mint, &token_authority) { - (None, None) => None, - _ => Some(ConstraintTokenAccountGroup { - mint: token_mint.as_ref().map(|a| a.clone().into_inner().mint), - authority: token_authority - .as_ref() - .map(|a| a.clone().into_inner().auth), - }), - }; - - let mint = match (&mint_decimals, &mint_authority, &mint_freeze_authority) { - (None, None, None) => None, - _ => Some(ConstraintTokenMintGroup { - decimals: mint_decimals - .as_ref() - .map(|a| a.clone().into_inner().decimals), - mint_authority: mint_authority - .as_ref() - .map(|a| a.clone().into_inner().mint_auth), - freeze_authority: mint_freeze_authority - .as_ref() - .map(|a| a.clone().into_inner().mint_freeze_auth), - }), - }; - - Ok(ConstraintGroup { - init: init.as_ref().map(|i| Ok(ConstraintInitGroup { - if_needed: i.if_needed, - seeds: seeds.clone(), - payer: into_inner!(payer.clone()).unwrap().target, - space: space.clone().map(|s| s.space.clone()), - kind: if let Some(tm) = &token_mint { - InitKind::Token { - mint: tm.clone().into_inner().mint, - owner: match &token_authority { - Some(a) => a.clone().into_inner().auth, - None => return Err(ParseError::new( - tm.span(), - "authority must be provided to initialize a token program derived address" - )), - }, - } - } else if let Some(at) = &associated_token { - InitKind::AssociatedToken { - mint: at.mint.clone(), - owner: at.wallet.clone() - } - } else if let Some(d) = &mint_decimals { - InitKind::Mint { - decimals: d.clone().into_inner().decimals, - owner: match &mint_authority { - Some(a) => a.clone().into_inner().mint_auth, - None => return Err(ParseError::new( - d.span(), - "authority must be provided to initialize a mint program derived address" - )) - }, - freeze_authority: mint_freeze_authority.map(|fa| fa.into_inner().mint_freeze_auth) - } - } else { - InitKind::Program { - owner: owner.as_ref().map(|o| o.owner_address.clone()), - } - }, - })).transpose()?, - zeroed: into_inner!(zeroed), - mutable: into_inner!(mutable), - signer: into_inner!(signer), - has_one: into_inner_vec!(has_one), - literal: into_inner_vec!(literal), - raw: into_inner_vec!(raw), - owner: into_inner!(owner), - rent_exempt: into_inner!(rent_exempt), - executable: into_inner!(executable), - state: into_inner!(state), - close: into_inner!(close), - address: into_inner!(address), - associated_token: if !is_init { associated_token } else { None }, - seeds, - token_account: if !is_init {token_account} else {None}, - mint: if !is_init {mint} else {None}, - }) - } - - pub fn add(&mut self, c: ConstraintToken) -> ParseResult<()> { - match c { - ConstraintToken::Init(c) => self.add_init(c), - ConstraintToken::Zeroed(c) => self.add_zeroed(c), - ConstraintToken::Mut(c) => self.add_mut(c), - ConstraintToken::Signer(c) => self.add_signer(c), - ConstraintToken::HasOne(c) => self.add_has_one(c), - ConstraintToken::Literal(c) => self.add_literal(c), - ConstraintToken::Raw(c) => self.add_raw(c), - ConstraintToken::Owner(c) => self.add_owner(c), - ConstraintToken::RentExempt(c) => self.add_rent_exempt(c), - ConstraintToken::Seeds(c) => self.add_seeds(c), - ConstraintToken::Executable(c) => self.add_executable(c), - ConstraintToken::State(c) => self.add_state(c), - ConstraintToken::Payer(c) => self.add_payer(c), - ConstraintToken::Space(c) => self.add_space(c), - ConstraintToken::Close(c) => self.add_close(c), - ConstraintToken::Address(c) => self.add_address(c), - ConstraintToken::TokenAuthority(c) => self.add_token_authority(c), - ConstraintToken::TokenMint(c) => self.add_token_mint(c), - ConstraintToken::AssociatedTokenAuthority(c) => self.add_associated_token_authority(c), - ConstraintToken::AssociatedTokenMint(c) => self.add_associated_token_mint(c), - ConstraintToken::MintAuthority(c) => self.add_mint_authority(c), - ConstraintToken::MintFreezeAuthority(c) => self.add_mint_freeze_authority(c), - ConstraintToken::MintDecimals(c) => self.add_mint_decimals(c), - ConstraintToken::Bump(c) => self.add_bump(c), - ConstraintToken::ProgramSeed(c) => self.add_program_seed(c), - } - } - - fn add_init(&mut self, c: Context) -> ParseResult<()> { - if self.init.is_some() { - return Err(ParseError::new(c.span(), "init already provided")); - } - if self.zeroed.is_some() { - return Err(ParseError::new(c.span(), "zeroed already provided")); - } - if self.token_mint.is_some() { - return Err(ParseError::new( - c.span(), - "init must be provided before token mint", - )); - } - if self.token_authority.is_some() { - return Err(ParseError::new( - c.span(), - "init must be provided before token authority", - )); - } - if self.mint_authority.is_some() { - return Err(ParseError::new( - c.span(), - "init must be provided before mint authority", - )); - } - if self.mint_freeze_authority.is_some() { - return Err(ParseError::new( - c.span(), - "init must be provided before mint freeze authority", - )); - } - if self.mint_decimals.is_some() { - return Err(ParseError::new( - c.span(), - "init must be provided before mint decimals", - )); - } - if self.associated_token_mint.is_some() { - return Err(ParseError::new( - c.span(), - "init must be provided before associated token mint", - )); - } - if self.associated_token_authority.is_some() { - return Err(ParseError::new( - c.span(), - "init must be provided before associated token authority", - )); - } - self.init.replace(c); - Ok(()) - } - - fn add_zeroed(&mut self, c: Context) -> ParseResult<()> { - if self.zeroed.is_some() { - return Err(ParseError::new(c.span(), "zeroed already provided")); - } - if self.init.is_some() { - return Err(ParseError::new(c.span(), "init already provided")); - } - self.zeroed.replace(c); - Ok(()) - } - - fn add_close(&mut self, c: Context) -> ParseResult<()> { - if !matches!(self.f_ty, Some(Ty::ProgramAccount(_))) - && !matches!(self.f_ty, Some(Ty::Account(_))) - && !matches!(self.f_ty, Some(Ty::Loader(_))) - && !matches!(self.f_ty, Some(Ty::AccountLoader(_))) - { - return Err(ParseError::new( - c.span(), - "close must be on an Account, ProgramAccount, or Loader", - )); - } - if self.mutable.is_none() { - return Err(ParseError::new( - c.span(), - "mut must be provided before close", - )); - } - if self.close.is_some() { - return Err(ParseError::new(c.span(), "close already provided")); - } - self.close.replace(c); - Ok(()) - } - - fn add_address(&mut self, c: Context) -> ParseResult<()> { - if self.address.is_some() { - return Err(ParseError::new(c.span(), "address already provided")); - } - self.address.replace(c); - Ok(()) - } - - fn add_token_mint(&mut self, c: Context) -> ParseResult<()> { - if self.token_mint.is_some() { - return Err(ParseError::new(c.span(), "token mint already provided")); - } - if self.associated_token_mint.is_some() { - return Err(ParseError::new( - c.span(), - "associated token mint already provided", - )); - } - self.token_mint.replace(c); - Ok(()) - } - - fn add_associated_token_mint(&mut self, c: Context) -> ParseResult<()> { - if self.associated_token_mint.is_some() { - return Err(ParseError::new( - c.span(), - "associated token mint already provided", - )); - } - if self.token_mint.is_some() { - return Err(ParseError::new(c.span(), "token mint already provided")); - } - self.associated_token_mint.replace(c); - Ok(()) - } - - fn add_bump(&mut self, c: Context) -> ParseResult<()> { - if self.bump.is_some() { - return Err(ParseError::new(c.span(), "bump already provided")); - } - if self.seeds.is_none() { - return Err(ParseError::new( - c.span(), - "seeds must be provided before bump", - )); - } - self.bump.replace(c); - Ok(()) - } - - fn add_program_seed(&mut self, c: Context) -> ParseResult<()> { - if self.program_seed.is_some() { - return Err(ParseError::new(c.span(), "seeds::program already provided")); - } - if self.seeds.is_none() { - return Err(ParseError::new( - c.span(), - "seeds must be provided before seeds::program", - )); - } - if let Some(ref init) = self.init { - if init.if_needed { - return Err(ParseError::new( - c.span(), - "seeds::program cannot be used with init_if_needed", - )); - } else { - return Err(ParseError::new( - c.span(), - "seeds::program cannot be used with init", - )); - } - } - self.program_seed.replace(c); - Ok(()) - } - - fn add_token_authority(&mut self, c: Context) -> ParseResult<()> { - if self.token_authority.is_some() { - return Err(ParseError::new( - c.span(), - "token authority already provided", - )); - } - self.token_authority.replace(c); - Ok(()) - } - - fn add_associated_token_authority( - &mut self, - c: Context, - ) -> ParseResult<()> { - if self.associated_token_authority.is_some() { - return Err(ParseError::new( - c.span(), - "associated token authority already provided", - )); - } - if self.token_authority.is_some() { - return Err(ParseError::new( - c.span(), - "token authority already provided", - )); - } - self.associated_token_authority.replace(c); - Ok(()) - } - - fn add_mint_authority(&mut self, c: Context) -> ParseResult<()> { - if self.mint_authority.is_some() { - return Err(ParseError::new(c.span(), "mint authority already provided")); - } - self.mint_authority.replace(c); - Ok(()) - } - - fn add_mint_freeze_authority( - &mut self, - c: Context, - ) -> ParseResult<()> { - if self.mint_freeze_authority.is_some() { - return Err(ParseError::new( - c.span(), - "mint freeze_authority already provided", - )); - } - self.mint_freeze_authority.replace(c); - Ok(()) - } - - fn add_mint_decimals(&mut self, c: Context) -> ParseResult<()> { - if self.mint_decimals.is_some() { - return Err(ParseError::new(c.span(), "mint decimals already provided")); - } - self.mint_decimals.replace(c); - Ok(()) - } - - fn add_mut(&mut self, c: Context) -> ParseResult<()> { - if self.mutable.is_some() { - return Err(ParseError::new(c.span(), "mut already provided")); - } - self.mutable.replace(c); - Ok(()) - } - - fn add_signer(&mut self, c: Context) -> ParseResult<()> { - if self.signer.is_some() { - return Err(ParseError::new(c.span(), "signer already provided")); - } - self.signer.replace(c); - Ok(()) - } - - fn add_has_one(&mut self, c: Context) -> ParseResult<()> { - if self - .has_one - .iter() - .filter(|item| item.join_target == c.join_target) - .count() - > 0 - { - return Err(ParseError::new(c.span(), "has_one target already provided")); - } - self.has_one.push(c); - Ok(()) - } - - fn add_literal(&mut self, c: Context) -> ParseResult<()> { - self.literal.push(c); - Ok(()) - } - - fn add_raw(&mut self, c: Context) -> ParseResult<()> { - self.raw.push(c); - Ok(()) - } - - fn add_owner(&mut self, c: Context) -> ParseResult<()> { - if self.owner.is_some() { - return Err(ParseError::new(c.span(), "owner already provided")); - } - self.owner.replace(c); - Ok(()) - } - - fn add_rent_exempt(&mut self, c: Context) -> ParseResult<()> { - if self.rent_exempt.is_some() { - return Err(ParseError::new(c.span(), "rent already provided")); - } - self.rent_exempt.replace(c); - Ok(()) - } - - fn add_seeds(&mut self, c: Context) -> ParseResult<()> { - if self.seeds.is_some() { - return Err(ParseError::new(c.span(), "seeds already provided")); - } - self.seeds.replace(c); - Ok(()) - } - - fn add_executable(&mut self, c: Context) -> ParseResult<()> { - if self.executable.is_some() { - return Err(ParseError::new(c.span(), "executable already provided")); - } - self.executable.replace(c); - Ok(()) - } - - fn add_state(&mut self, c: Context) -> ParseResult<()> { - if self.state.is_some() { - return Err(ParseError::new(c.span(), "state already provided")); - } - self.state.replace(c); - Ok(()) - } - - fn add_payer(&mut self, c: Context) -> ParseResult<()> { - if self.init.is_none() { - return Err(ParseError::new( - c.span(), - "init must be provided before payer", - )); - } - if self.payer.is_some() { - return Err(ParseError::new(c.span(), "payer already provided")); - } - self.payer.replace(c); - Ok(()) - } - - fn add_space(&mut self, c: Context) -> ParseResult<()> { - if self.init.is_none() { - return Err(ParseError::new( - c.span(), - "init must be provided before space", - )); - } - if self.space.is_some() { - return Err(ParseError::new(c.span(), "space already provided")); - } - self.space.replace(c); - Ok(()) - } -} diff --git a/lang/syn/src/parser/accounts/mod.rs b/lang/syn/src/parser/accounts/mod.rs deleted file mode 100644 index b0178af5..00000000 --- a/lang/syn/src/parser/accounts/mod.rs +++ /dev/null @@ -1,420 +0,0 @@ -use crate::*; -use syn::parse::{Error as ParseError, Result as ParseResult}; -use syn::punctuated::Punctuated; -use syn::spanned::Spanned; -use syn::token::Comma; -use syn::Expr; - -pub mod constraints; - -pub fn parse(strct: &syn::ItemStruct) -> ParseResult { - let instruction_api: Option> = strct - .attrs - .iter() - .find(|a| { - a.path - .get_ident() - .map_or(false, |ident| ident == "instruction") - }) - .map(|ix_attr| ix_attr.parse_args_with(Punctuated::::parse_terminated)) - .transpose()?; - let fields = match &strct.fields { - syn::Fields::Named(fields) => fields - .named - .iter() - .map(|f| parse_account_field(f, instruction_api.is_some())) - .collect::>>()?, - _ => { - return Err(ParseError::new_spanned( - &strct.fields, - "fields must be named", - )) - } - }; - - let _ = constraints_cross_checks(&fields)?; - - Ok(AccountsStruct::new(strct.clone(), fields, instruction_api)) -} - -fn constraints_cross_checks(fields: &[AccountField]) -> ParseResult<()> { - // INIT - let init_fields: Vec<&Field> = fields - .iter() - .filter_map(|f| match f { - AccountField::Field(field) if field.constraints.init.is_some() => Some(field), - _ => None, - }) - .collect(); - - if !init_fields.is_empty() { - // init needs system program. - if fields.iter().all(|f| f.ident() != "system_program") { - return Err(ParseError::new( - init_fields[0].ident.span(), - "the init constraint requires \ - the system_program field to exist in the account \ - validation struct. Use the program type to add \ - the system_program field to your validation struct.", - )); - } - - let kind = &init_fields[0].constraints.init.as_ref().unwrap().kind; - // init token/a_token/mint needs token program. - match kind { - InitKind::Program { .. } => (), - InitKind::Token { .. } | InitKind::AssociatedToken { .. } | InitKind::Mint { .. } => { - if fields.iter().all(|f| f.ident() != "token_program") { - return Err(ParseError::new( - init_fields[0].ident.span(), - "the init constraint requires \ - the token_program field to exist in the account \ - validation struct. Use the program type to add \ - the token_program field to your validation struct.", - )); - } - } - } - // a_token needs associated token program. - if let InitKind::AssociatedToken { .. } = kind { - if fields - .iter() - .all(|f| f.ident() != "associated_token_program") - { - return Err(ParseError::new( - init_fields[0].ident.span(), - "the init constraint requires \ - the associated_token_program field to exist in the account \ - validation struct. Use the program type to add \ - the associated_token_program field to your validation struct.", - )); - } - } - - for field in init_fields { - // Get payer for init-ed account - let associated_payer_name = match field.constraints.init.clone().unwrap().payer { - // composite payer, check not supported - Expr::Field(_) => continue, - field_name => field_name.to_token_stream().to_string(), - }; - - // Check payer is mutable - let associated_payer_field = fields.iter().find_map(|f| match f { - AccountField::Field(field) if *f.ident() == associated_payer_name => Some(field), - _ => None, - }); - match associated_payer_field { - Some(associated_payer_field) => { - if !associated_payer_field.constraints.is_mutable() { - return Err(ParseError::new( - field.ident.span(), - "the payer specified for an init constraint must be mutable.", - )); - } - } - _ => { - return Err(ParseError::new( - field.ident.span(), - "the payer specified does not exist.", - )); - } - } - } - } - Ok(()) -} - -pub fn parse_account_field(f: &syn::Field, has_instruction_api: bool) -> ParseResult { - let ident = f.ident.clone().unwrap(); - let docs: String = f - .attrs - .iter() - .map(|a| { - let meta_result = a.parse_meta(); - if let Ok(syn::Meta::NameValue(meta)) = meta_result { - if meta.path.is_ident("doc") { - if let syn::Lit::Str(doc) = meta.lit { - return format!(" {}\n", doc.value().trim()); - } - } - } - "".to_string() - }) - .collect::(); - let account_field = match is_field_primitive(f)? { - true => { - let ty = parse_ty(f)?; - let (account_constraints, instruction_constraints) = - constraints::parse(f, Some(&ty), has_instruction_api)?; - AccountField::Field(Field { - ident, - ty, - constraints: account_constraints, - instruction_constraints, - docs, - }) - } - false => { - let (account_constraints, instruction_constraints) = - constraints::parse(f, None, has_instruction_api)?; - AccountField::CompositeField(CompositeField { - ident, - constraints: account_constraints, - instruction_constraints, - symbol: ident_string(f)?, - raw_field: f.clone(), - docs, - }) - } - }; - Ok(account_field) -} - -fn is_field_primitive(f: &syn::Field) -> ParseResult { - let r = matches!( - ident_string(f)?.as_str(), - "ProgramState" - | "ProgramAccount" - | "CpiAccount" - | "Sysvar" - | "AccountInfo" - | "UncheckedAccount" - | "CpiState" - | "Loader" - | "AccountLoader" - | "Account" - | "Program" - | "Signer" - | "SystemAccount" - | "ProgramData" - ); - Ok(r) -} - -fn parse_ty(f: &syn::Field) -> ParseResult { - let path = match &f.ty { - syn::Type::Path(ty_path) => ty_path.path.clone(), - _ => return Err(ParseError::new(f.ty.span(), "invalid account type given")), - }; - let ty = match ident_string(f)?.as_str() { - "ProgramState" => Ty::ProgramState(parse_program_state(&path)?), - "CpiState" => Ty::CpiState(parse_cpi_state(&path)?), - "ProgramAccount" => Ty::ProgramAccount(parse_program_account(&path)?), - "CpiAccount" => Ty::CpiAccount(parse_cpi_account(&path)?), - "Sysvar" => Ty::Sysvar(parse_sysvar(&path)?), - "AccountInfo" => Ty::AccountInfo, - "UncheckedAccount" => Ty::UncheckedAccount, - "Loader" => Ty::Loader(parse_program_account_zero_copy(&path)?), - "AccountLoader" => Ty::AccountLoader(parse_program_account_loader(&path)?), - "Account" => Ty::Account(parse_account_ty(&path)?), - "Program" => Ty::Program(parse_program_ty(&path)?), - "Signer" => Ty::Signer, - "SystemAccount" => Ty::SystemAccount, - "ProgramData" => Ty::ProgramData, - _ => return Err(ParseError::new(f.ty.span(), "invalid account type given")), - }; - - Ok(ty) -} - -fn ident_string(f: &syn::Field) -> ParseResult { - let path = match &f.ty { - syn::Type::Path(ty_path) => ty_path.path.clone(), - _ => return Err(ParseError::new(f.ty.span(), "invalid type")), - }; - if parser::tts_to_string(&path) - .replace(' ', "") - .starts_with("Box ParseResult { - let account_ident = parse_account(path)?; - Ok(ProgramStateTy { - account_type_path: account_ident, - }) -} - -fn parse_cpi_state(path: &syn::Path) -> ParseResult { - let account_ident = parse_account(path)?; - Ok(CpiStateTy { - account_type_path: account_ident, - }) -} - -fn parse_cpi_account(path: &syn::Path) -> ParseResult { - let account_ident = parse_account(path)?; - Ok(CpiAccountTy { - account_type_path: account_ident, - }) -} - -fn parse_program_account(path: &syn::Path) -> ParseResult { - let account_ident = parse_account(path)?; - Ok(ProgramAccountTy { - account_type_path: account_ident, - }) -} - -fn parse_program_account_zero_copy(path: &syn::Path) -> ParseResult { - let account_ident = parse_account(path)?; - Ok(LoaderTy { - account_type_path: account_ident, - }) -} -fn parse_program_account_loader(path: &syn::Path) -> ParseResult { - let account_ident = parse_account(path)?; - Ok(AccountLoaderTy { - account_type_path: account_ident, - }) -} - -fn parse_account_ty(path: &syn::Path) -> ParseResult { - let account_type_path = parse_account(path)?; - let boxed = parser::tts_to_string(&path) - .replace(' ', "") - .starts_with("Box ParseResult { - let account_type_path = parse_account(path)?; - Ok(ProgramTy { account_type_path }) -} - -// TODO: this whole method is a hack. Do something more idiomatic. -fn parse_account(mut path: &syn::Path) -> ParseResult { - if parser::tts_to_string(path) - .replace(' ', "") - .starts_with("Box { - // Expected: <'info, MyType>. - if args.args.len() != 1 { - return Err(ParseError::new( - args.args.span(), - "bracket arguments must be the lifetime and type", - )); - } - match &args.args[0] { - syn::GenericArgument::Type(syn::Type::Path(ty_path)) => { - path = &ty_path.path; - } - _ => { - return Err(ParseError::new( - args.args[1].span(), - "first bracket argument must be a lifetime", - )) - } - } - } - _ => { - return Err(ParseError::new( - segments.arguments.span(), - "expected angle brackets with a lifetime and type", - )) - } - } - } - - let segments = &path.segments[0]; - match &segments.arguments { - syn::PathArguments::AngleBracketed(args) => { - // Expected: <'info, MyType>. - if args.args.len() != 2 { - return Err(ParseError::new( - args.args.span(), - "bracket arguments must be the lifetime and type", - )); - } - match &args.args[1] { - syn::GenericArgument::Type(syn::Type::Path(ty_path)) => Ok(ty_path.clone()), - _ => Err(ParseError::new( - args.args[1].span(), - "first bracket argument must be a lifetime", - )), - } - } - _ => Err(ParseError::new( - segments.arguments.span(), - "expected angle brackets with a lifetime and type", - )), - } -} - -fn parse_sysvar(path: &syn::Path) -> ParseResult { - let segments = &path.segments[0]; - let account_ident = match &segments.arguments { - syn::PathArguments::AngleBracketed(args) => { - // Expected: <'info, MyType>. - if args.args.len() != 2 { - return Err(ParseError::new( - args.args.span(), - "bracket arguments must be the lifetime and type", - )); - } - match &args.args[1] { - syn::GenericArgument::Type(syn::Type::Path(ty_path)) => { - // TODO: allow segmented paths. - if ty_path.path.segments.len() != 1 { - return Err(ParseError::new( - ty_path.path.span(), - "segmented paths are not currently allowed", - )); - } - let path_segment = &ty_path.path.segments[0]; - path_segment.ident.clone() - } - _ => { - return Err(ParseError::new( - args.args[1].span(), - "first bracket argument must be a lifetime", - )) - } - } - } - _ => { - return Err(ParseError::new( - segments.arguments.span(), - "expected angle brackets with a lifetime and type", - )) - } - }; - let ty = match account_ident.to_string().as_str() { - "Clock" => SysvarTy::Clock, - "Rent" => SysvarTy::Rent, - "EpochSchedule" => SysvarTy::EpochSchedule, - "Fees" => SysvarTy::Fees, - "RecentBlockhashes" => SysvarTy::RecentBlockhashes, - "SlotHashes" => SysvarTy::SlotHashes, - "SlotHistory" => SysvarTy::SlotHistory, - "StakeHistory" => SysvarTy::StakeHistory, - "Instructions" => SysvarTy::Instructions, - "Rewards" => SysvarTy::Rewards, - _ => { - return Err(ParseError::new( - account_ident.span(), - "invalid sysvar provided", - )) - } - }; - Ok(ty) -} diff --git a/lang/syn/src/parser/context.rs b/lang/syn/src/parser/context.rs deleted file mode 100644 index 65a3bc3f..00000000 --- a/lang/syn/src/parser/context.rs +++ /dev/null @@ -1,247 +0,0 @@ -use anyhow::anyhow; -use std::collections::BTreeMap; -use std::path::{Path, PathBuf}; -use syn::parse::{Error as ParseError, Result as ParseResult}; - -/// Crate parse context -/// -/// Keeps track of modules defined within a crate. -pub struct CrateContext { - modules: BTreeMap, -} - -impl CrateContext { - pub fn consts(&self) -> impl Iterator { - self.modules.iter().flat_map(|(_, ctx)| ctx.consts()) - } - - pub fn structs(&self) -> impl Iterator { - self.modules.iter().flat_map(|(_, ctx)| ctx.structs()) - } - - pub fn enums(&self) -> impl Iterator { - self.modules.iter().flat_map(|(_, ctx)| ctx.enums()) - } - - pub fn modules(&self) -> impl Iterator { - self.modules - .iter() - .map(move |(_, detail)| ModuleContext { detail }) - } - - pub fn root_module(&self) -> ModuleContext { - ModuleContext { - detail: self.modules.get("crate").unwrap(), - } - } - - pub fn parse(root: impl AsRef) -> Result { - Ok(CrateContext { - modules: ParsedModule::parse_recursive(root.as_ref())?, - }) - } - - // Perform Anchor safety checks on the parsed create - pub fn safety_checks(&self) -> Result<(), anyhow::Error> { - // Check all structs for unsafe field types, i.e. AccountInfo and UncheckedAccount. - for (_, ctx) in self.modules.iter() { - for unsafe_field in ctx.unsafe_struct_fields() { - // Check if unsafe field type has been documented with a /// SAFETY: doc string. - let is_documented = unsafe_field.attrs.iter().any(|attr| { - attr.tokens.clone().into_iter().any(|token| match token { - // Check for doc comments containing CHECK - proc_macro2::TokenTree::Literal(s) => s.to_string().contains("CHECK"), - _ => false, - }) - }); - if !is_documented { - let ident = unsafe_field.ident.as_ref().unwrap(); - let span = ident.span(); - // Error if undocumented. - return Err(anyhow!( - r#" - {}:{}:{} - Struct field "{}" is unsafe, but is not documented. - Please add a `/// CHECK:` doc comment explaining why no checks through types are necessary. - See https://book.anchor-lang.com/chapter_3/the_accounts_struct.html#safety-checks for more information. - "#, - ctx.file.canonicalize().unwrap().display(), - span.start().line, - span.start().column, - ident.to_string() - )); - }; - } - } - Ok(()) - } -} - -/// Module parse context -/// -/// Keeps track of items defined within a module. -#[derive(Copy, Clone)] -pub struct ModuleContext<'krate> { - detail: &'krate ParsedModule, -} - -impl<'krate> ModuleContext<'krate> { - pub fn items(&self) -> impl Iterator { - self.detail.items.iter() - } -} -struct ParsedModule { - name: String, - file: PathBuf, - path: String, - items: Vec, -} - -impl ParsedModule { - fn parse_recursive(root: &Path) -> Result, anyhow::Error> { - let mut modules = BTreeMap::new(); - - let root_content = std::fs::read_to_string(root)?; - let root_file = syn::parse_file(&root_content)?; - let root_mod = Self::new( - String::new(), - root.to_owned(), - "crate".to_owned(), - root_file.items, - ); - - struct UnparsedModule { - file: PathBuf, - path: String, - name: String, - item: syn::ItemMod, - } - - let mut unparsed = root_mod - .submodules() - .map(|item| UnparsedModule { - file: root_mod.file.clone(), - path: root_mod.path.clone(), - name: item.ident.to_string(), - item: item.clone(), - }) - .collect::>(); - - while let Some(to_parse) = unparsed.pop() { - let path = format!("{}::{}", to_parse.path, to_parse.name); - let name = to_parse.name; - let module = Self::from_item_mod(&to_parse.file, &path, to_parse.item)?; - - unparsed.extend(module.submodules().map(|item| UnparsedModule { - item: item.clone(), - file: module.file.clone(), - path: module.path.clone(), - name: item.ident.to_string(), - })); - modules.insert(format!("{}{}", module.path.clone(), name.clone()), module); - } - - modules.insert(root_mod.name.clone(), root_mod); - - Ok(modules) - } - - fn from_item_mod( - parent_file: &Path, - parent_path: &str, - item: syn::ItemMod, - ) -> ParseResult { - Ok(match item.content { - Some((_, items)) => { - // The module content is within the parent file being parsed - Self::new( - parent_path.to_owned(), - parent_file.to_owned(), - item.ident.to_string(), - items, - ) - } - None => { - // The module is referencing some other file, so we need to load that - // to parse the items it has. - let parent_dir = parent_file.parent().unwrap(); - let parent_filename = parent_file.file_stem().unwrap().to_str().unwrap(); - let parent_mod_dir = parent_dir.join(parent_filename); - - let possible_file_paths = vec![ - parent_dir.join(format!("{}.rs", item.ident)), - parent_dir.join(format!("{}/mod.rs", item.ident)), - parent_mod_dir.join(format!("{}.rs", item.ident)), - parent_mod_dir.join(format!("{}/mod.rs", item.ident)), - ]; - - let mod_file_path = possible_file_paths - .into_iter() - .find(|p| p.exists()) - .ok_or_else(|| ParseError::new_spanned(&item, "could not find file"))?; - let mod_file_content = std::fs::read_to_string(&mod_file_path) - .map_err(|_| ParseError::new_spanned(&item, "could not read file"))?; - let mod_file = syn::parse_file(&mod_file_content)?; - - Self::new( - parent_path.to_owned(), - mod_file_path, - item.ident.to_string(), - mod_file.items, - ) - } - }) - } - - fn new(path: String, file: PathBuf, name: String, items: Vec) -> Self { - Self { - name, - file, - path, - items, - } - } - - fn submodules(&self) -> impl Iterator { - self.items.iter().filter_map(|i| match i { - syn::Item::Mod(item) => Some(item), - _ => None, - }) - } - - fn structs(&self) -> impl Iterator { - self.items.iter().filter_map(|i| match i { - syn::Item::Struct(item) => Some(item), - _ => None, - }) - } - - fn unsafe_struct_fields(&self) -> impl Iterator { - self.structs() - .flat_map(|s| &s.fields) - .filter(|f| match &f.ty { - syn::Type::Path(syn::TypePath { - path: syn::Path { segments, .. }, - .. - }) => { - segments.len() == 1 && segments[0].ident == "UncheckedAccount" - || segments[0].ident == "AccountInfo" - } - _ => false, - }) - } - - fn enums(&self) -> impl Iterator { - self.items.iter().filter_map(|i| match i { - syn::Item::Enum(item) => Some(item), - _ => None, - }) - } - - fn consts(&self) -> impl Iterator { - self.items.iter().filter_map(|i| match i { - syn::Item::Const(item) => Some(item), - _ => None, - }) - } -} diff --git a/lang/syn/src/parser/error.rs b/lang/syn/src/parser/error.rs deleted file mode 100644 index 5967b086..00000000 --- a/lang/syn/src/parser/error.rs +++ /dev/null @@ -1,90 +0,0 @@ -use crate::{Error, ErrorArgs, ErrorCode}; -use syn::parse::{Parse, Result as ParseResult}; -use syn::Expr; - -// Removes any internal #[msg] attributes, as they are inert. -pub fn parse(error_enum: &mut syn::ItemEnum, args: Option) -> Error { - let ident = error_enum.ident.clone(); - let mut last_discriminant = 0; - let codes: Vec = error_enum - .variants - .iter_mut() - .map(|variant: &mut syn::Variant| { - let msg = parse_error_attribute(variant); - let ident = variant.ident.clone(); - let id = match &variant.discriminant { - None => last_discriminant, - Some((_, disc)) => match disc { - syn::Expr::Lit(expr_lit) => match &expr_lit.lit { - syn::Lit::Int(int) => { - int.base10_parse::().expect("Must be a base 10 number") - } - _ => panic!("Invalid error discriminant"), - }, - _ => panic!("Invalid error discriminant"), - }, - }; - last_discriminant = id + 1; - - // Remove any non-doc attributes on the error variant. - variant.attrs = variant - .attrs - .iter() - .filter(|attr| attr.path.segments[0].ident == "doc") - .cloned() - .collect(); - - ErrorCode { id, ident, msg } - }) - .collect(); - Error { - name: error_enum.ident.to_string(), - raw_enum: error_enum.clone(), - ident, - codes, - args, - } -} - -fn parse_error_attribute(variant: &syn::Variant) -> Option { - let attrs = variant - .attrs - .iter() - .filter(|attr| attr.path.segments[0].ident != "doc") - .collect::>(); - match attrs.len() { - 0 => None, - 1 => { - let attr = &attrs[0]; - let attr_str = attr.path.segments[0].ident.to_string(); - assert!(&attr_str == "msg", "Use msg to specify error strings"); - - let mut tts = attr.tokens.clone().into_iter(); - let g_stream = match tts.next().expect("Must have a token group") { - proc_macro2::TokenTree::Group(g) => g.stream(), - _ => panic!("Invalid syntax"), - }; - - let msg = match g_stream.into_iter().next() { - None => panic!("Must specify a message string"), - Some(msg) => msg.to_string().replace('\"', ""), - }; - - Some(msg) - } - _ => { - panic!("Too many attributes found. Use `msg` to specify error strings"); - } - } -} - -pub struct ErrorInput { - pub error_code: Expr, -} - -impl Parse for ErrorInput { - fn parse(stream: syn::parse::ParseStream) -> ParseResult { - let error_code = stream.call(Expr::parse)?; - Ok(Self { error_code }) - } -} diff --git a/lang/syn/src/parser/mod.rs b/lang/syn/src/parser/mod.rs deleted file mode 100644 index cb20cbe4..00000000 --- a/lang/syn/src/parser/mod.rs +++ /dev/null @@ -1,10 +0,0 @@ -pub mod accounts; -pub mod context; -pub mod error; -pub mod program; - -pub fn tts_to_string(item: T) -> String { - let mut tts = proc_macro2::TokenStream::new(); - item.to_tokens(&mut tts); - tts.to_string() -} diff --git a/lang/syn/src/parser/program/instructions.rs b/lang/syn/src/parser/program/instructions.rs deleted file mode 100644 index 4cf5aa80..00000000 --- a/lang/syn/src/parser/program/instructions.rs +++ /dev/null @@ -1,126 +0,0 @@ -use crate::parser::program::ctx_accounts_ident; -use crate::{FallbackFn, Ix, IxArg, IxReturn}; -use syn::parse::{Error as ParseError, Result as ParseResult}; -use syn::spanned::Spanned; - -// Parse all non-state ix handlers from the program mod definition. -pub fn parse(program_mod: &syn::ItemMod) -> ParseResult<(Vec, Option)> { - let mod_content = &program_mod - .content - .as_ref() - .ok_or_else(|| ParseError::new(program_mod.span(), "program content not provided"))? - .1; - - let ixs = mod_content - .iter() - .filter_map(|item| match item { - syn::Item::Fn(item_fn) => { - let (ctx, _) = parse_args(item_fn).ok()?; - ctx_accounts_ident(&ctx.raw_arg).ok()?; - Some(item_fn) - } - _ => None, - }) - .map(|method: &syn::ItemFn| { - let (ctx, args) = parse_args(method)?; - let returns = parse_return(method)?; - let anchor_ident = ctx_accounts_ident(&ctx.raw_arg)?; - Ok(Ix { - raw_method: method.clone(), - ident: method.sig.ident.clone(), - args, - anchor_ident, - returns, - }) - }) - .collect::>>()?; - - let fallback_fn = { - let fallback_fns = mod_content - .iter() - .filter_map(|item| match item { - syn::Item::Fn(item_fn) => { - let (ctx, _args) = parse_args(item_fn).ok()?; - if ctx_accounts_ident(&ctx.raw_arg).is_ok() { - return None; - } - Some(item_fn) - } - _ => None, - }) - .collect::>(); - if fallback_fns.len() > 1 { - return Err(ParseError::new( - fallback_fns[0].span(), - "More than one fallback function found", - )); - } - fallback_fns - .first() - .map(|method: &&syn::ItemFn| FallbackFn { - raw_method: (*method).clone(), - }) - }; - - Ok((ixs, fallback_fn)) -} - -pub fn parse_args(method: &syn::ItemFn) -> ParseResult<(IxArg, Vec)> { - let mut args: Vec = method - .sig - .inputs - .iter() - .map(|arg: &syn::FnArg| match arg { - syn::FnArg::Typed(arg) => { - let ident = match &*arg.pat { - syn::Pat::Ident(ident) => &ident.ident, - _ => return Err(ParseError::new(arg.pat.span(), "expected argument name")), - }; - Ok(IxArg { - name: ident.clone(), - raw_arg: arg.clone(), - }) - } - syn::FnArg::Receiver(_) => Err(ParseError::new( - arg.span(), - "expected a typed argument not self", - )), - }) - .collect::>()?; - - // Remove the Context argument - let ctx = args.remove(0); - - Ok((ctx, args)) -} - -pub fn parse_return(method: &syn::ItemFn) -> ParseResult { - match method.sig.output { - syn::ReturnType::Type(_, ref ty) => { - let ty = match ty.as_ref() { - syn::Type::Path(ty) => ty, - _ => return Err(ParseError::new(ty.span(), "expected a return type")), - }; - // Assume unit return by default - let default_generic_arg = syn::GenericArgument::Type(syn::parse_str("()").unwrap()); - let generic_args = match &ty.path.segments.last().unwrap().arguments { - syn::PathArguments::AngleBracketed(params) => params.args.iter().last().unwrap(), - _ => &default_generic_arg, - }; - let ty = match generic_args { - syn::GenericArgument::Type(ty) => ty.clone(), - _ => { - return Err(ParseError::new( - ty.span(), - "expected generic return type to be a type", - )) - } - }; - Ok(IxReturn { ty }) - } - _ => Err(ParseError::new( - method.sig.output.span(), - "expected a return type", - )), - } -} diff --git a/lang/syn/src/parser/program/mod.rs b/lang/syn/src/parser/program/mod.rs deleted file mode 100644 index e1303076..00000000 --- a/lang/syn/src/parser/program/mod.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::Program; -use syn::parse::{Error as ParseError, Result as ParseResult}; -use syn::spanned::Spanned; - -mod instructions; -mod state; - -pub fn parse(program_mod: syn::ItemMod) -> ParseResult { - let state = state::parse(&program_mod)?; - let (ixs, fallback_fn) = instructions::parse(&program_mod)?; - Ok(Program { - state, - ixs, - name: program_mod.ident.clone(), - program_mod, - fallback_fn, - }) -} - -fn ctx_accounts_ident(path_ty: &syn::PatType) -> ParseResult { - let p = match &*path_ty.ty { - syn::Type::Path(p) => &p.path, - _ => return Err(ParseError::new(path_ty.ty.span(), "invalid type")), - }; - let segment = p - .segments - .first() - .ok_or_else(|| ParseError::new(p.segments.span(), "expected generic arguments here"))?; - - let generic_args = match &segment.arguments { - syn::PathArguments::AngleBracketed(args) => args, - _ => return Err(ParseError::new(path_ty.span(), "missing accounts context")), - }; - let generic_ty = generic_args - .args - .iter() - .filter_map(|arg| match arg { - syn::GenericArgument::Type(ty) => Some(ty), - _ => None, - }) - .next() - .ok_or_else(|| ParseError::new(generic_args.span(), "expected Accounts type"))?; - - let path = match generic_ty { - syn::Type::Path(ty_path) => &ty_path.path, - _ => { - return Err(ParseError::new( - generic_ty.span(), - "expected Accounts struct type", - )) - } - }; - Ok(path.segments[0].ident.clone()) -} diff --git a/lang/syn/src/parser/program/state.rs b/lang/syn/src/parser/program/state.rs deleted file mode 100644 index 3b81a53f..00000000 --- a/lang/syn/src/parser/program/state.rs +++ /dev/null @@ -1,310 +0,0 @@ -use crate::parser; -use crate::parser::program::ctx_accounts_ident; -use crate::{IxArg, State, StateInterface, StateIx}; -use syn::parse::{Error as ParseError, Result as ParseResult}; -use syn::spanned::Spanned; - -// Name of the attribute denoting a state struct. -const STATE_STRUCT_ATTRIBUTE: &str = "state"; - -// Reserved keyword for the constructor method. -const CTOR_METHOD_NAME: &str = "new"; - -// Parse the state from the program mod definition. -pub fn parse(program_mod: &syn::ItemMod) -> ParseResult> { - let mod_content = &program_mod - .content - .as_ref() - .ok_or_else(|| ParseError::new(program_mod.span(), "program content not provided"))? - .1; - - // Parse `struct` marked with the `#[state]` attribute. - let strct: Option<(&syn::ItemStruct, bool)> = mod_content - .iter() - .filter_map(|item| match item { - syn::Item::Struct(item_strct) => { - let attrs = &item_strct.attrs; - if attrs.is_empty() { - return None; - } - let attr_label = attrs[0].path.get_ident().map(|i| i.to_string()); - if attr_label != Some(STATE_STRUCT_ATTRIBUTE.to_string()) { - return None; - } - let is_zero_copy = parser::tts_to_string(&attrs[0].tokens) == "(zero_copy)"; - Some((item_strct, is_zero_copy)) - } - _ => None, - }) - .next(); - - // Parse `impl` block for the state struct. - let impl_block: Option = match strct { - None => None, - Some((strct, _)) => mod_content - .iter() - .filter_map(|item| match item { - syn::Item::Impl(item_impl) => { - let impl_ty_str = parser::tts_to_string(&item_impl.self_ty); - let strct_name = strct.ident.to_string(); - if item_impl.trait_.is_some() { - return None; - } - if strct_name != impl_ty_str { - return None; - } - Some(item_impl.clone()) - } - _ => None, - }) - .next(), - }; - - // Parse ctor and the generic type in `Context`. - let ctor_and_anchor: Option<(syn::ImplItemMethod, syn::Ident)> = impl_block - .as_ref() - .map(|impl_block| { - let r: Option> = impl_block - .items - .iter() - .filter_map(|item: &syn::ImplItem| match item { - syn::ImplItem::Method(m) => match m.sig.ident == CTOR_METHOD_NAME { - false => None, - true => Some(m), - }, - _ => None, - }) - .map(|m: &syn::ImplItemMethod| { - let (_, is_zero_copy) = strct - .as_ref() - .expect("impl_block exists therefore the struct exists"); - let ctx_arg = { - if *is_zero_copy { - // Second param is context. - let mut iter = m.sig.inputs.iter(); - match iter.next() { - None => { - return Err(ParseError::new( - m.sig.span(), - "first parameter must be &mut self", - )) - } - Some(arg) => match arg { - syn::FnArg::Receiver(r) => { - if r.mutability.is_none() { - return Err(ParseError::new( - m.sig.span(), - "first parameter must be &mut self", - )); - } - } - syn::FnArg::Typed(_) => { - return Err(ParseError::new( - m.sig.span(), - "first parameter must be &mut self", - )) - } - }, - }; - match iter.next() { - None => { - return Err(ParseError::new( - m.sig.span(), - "second parameter must be the Context", - )) - } - Some(ctx_arg) => match ctx_arg { - syn::FnArg::Receiver(_) => { - return Err(ParseError::new( - ctx_arg.span(), - "second parameter must be the Context", - )) - } - syn::FnArg::Typed(arg) => arg, - }, - } - } else { - match m.sig.inputs.first() { - None => { - return Err(ParseError::new( - m.sig.span(), - "first parameter must be the Context", - )) - } - Some(ctx_arg) => match ctx_arg { - syn::FnArg::Receiver(_) => { - return Err(ParseError::new( - ctx_arg.span(), - "second parameter must be the Context", - )) - } - syn::FnArg::Typed(arg) => arg, - }, - } - } - }; - Ok((m.clone(), ctx_accounts_ident(ctx_arg)?)) - }) - .next(); - r.transpose() - }) - .transpose()? - .unwrap_or(None); - - // Parse all methods in the above `impl` block. - let methods: Option> = impl_block - .as_ref() - .map(|impl_block| { - impl_block - .items - .iter() - .filter_map(|item| match item { - syn::ImplItem::Method(m) => match m.sig.ident != CTOR_METHOD_NAME { - false => None, - true => Some(m), - }, - _ => None, - }) - .map(|m: &syn::ImplItemMethod| { - let mut args = m - .sig - .inputs - .iter() - .filter_map(|arg| match arg { - syn::FnArg::Receiver(_) => None, - syn::FnArg::Typed(arg) => Some(arg), - }) - .map(|raw_arg| { - let ident = match &*raw_arg.pat { - syn::Pat::Ident(ident) => &ident.ident, - _ => { - return Err(ParseError::new( - raw_arg.pat.span(), - "unexpected type argument", - )) - } - }; - Ok(IxArg { - name: ident.clone(), - raw_arg: raw_arg.clone(), - }) - }) - .collect::>>()?; - // Remove the Anchor accounts argument - let anchor = args.remove(0); - let anchor_ident = ctx_accounts_ident(&anchor.raw_arg)?; - - Ok(StateIx { - raw_method: m.clone(), - ident: m.sig.ident.clone(), - args, - anchor_ident, - has_receiver: true, - }) - }) - .collect::>>() - }) - .transpose()?; - - // Parse all trait implementations for the above `#[state]` struct. - let trait_impls: Option> = strct - .map(|_strct| { - mod_content - .iter() - .filter_map(|item| match item { - syn::Item::Impl(item_impl) => match &item_impl.trait_ { - None => None, - Some((_, path, _)) => { - let trait_name = path - .segments - .iter() - .next() - .expect("Must have one segment in a path") - .ident - .clone() - .to_string(); - Some((item_impl, trait_name)) - } - }, - _ => None, - }) - .map(|(item_impl, trait_name)| { - let methods = item_impl - .items - .iter() - .filter_map(|item: &syn::ImplItem| match item { - syn::ImplItem::Method(m) => Some(m), - _ => None, - }) - .map(|m: &syn::ImplItemMethod| { - match m.sig.inputs.first() { - None => Err(ParseError::new( - m.sig.inputs.span(), - "state methods must have a self argument", - )), - Some(_arg) => { - let mut has_receiver = false; - let mut args = m - .sig - .inputs - .iter() - .filter_map(|arg| match arg { - syn::FnArg::Receiver(_) => { - has_receiver = true; - None - } - syn::FnArg::Typed(arg) => Some(arg), - }) - .map(|raw_arg| { - let ident = match &*raw_arg.pat { - syn::Pat::Ident(ident) => &ident.ident, - _ => panic!("invalid syntax"), - }; - IxArg { - name: ident.clone(), - raw_arg: raw_arg.clone(), - } - }) - .collect::>(); - // Remove the Anchor accounts argument - let anchor = args.remove(0); - let anchor_ident = ctx_accounts_ident(&anchor.raw_arg)?; - - Ok(StateIx { - raw_method: m.clone(), - ident: m.sig.ident.clone(), - args, - anchor_ident, - has_receiver, - }) - } - } - }) - .collect::>>()?; - Ok(StateInterface { - trait_name, - methods, - }) - }) - .collect::>>() - }) - .transpose()?; - - Ok(strct.map(|(strct, is_zero_copy)| { - // Chop off the `#[state]` attribute. It's just a marker. - // - // TODO: instead of mutating the syntax, we should just implement - // a macro that does nothing. - let mut strct = strct.clone(); - strct.attrs = vec![]; - - State { - name: strct.ident.to_string(), - strct, - interfaces: trait_impls, - impl_block_and_methods: impl_block.map(|impl_block| (impl_block, methods.unwrap())), - ctor_and_anchor, - is_zero_copy, - } - })) -} diff --git a/lang/tests/generics_test.rs b/lang/tests/generics_test.rs deleted file mode 100644 index 3ecb8f32..00000000 --- a/lang/tests/generics_test.rs +++ /dev/null @@ -1,55 +0,0 @@ -#![allow(dead_code)] - -use anchor_lang::prelude::borsh::maybestd::io::Write; -use anchor_lang::prelude::*; -use borsh::{BorshDeserialize, BorshSerialize}; -use solana_program::pubkey::Pubkey; - -// Needed to declare accounts. -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[derive(Accounts)] -pub struct GenericsTest<'info, T, U, const N: usize> -where - T: AccountSerialize + AccountDeserialize + Owner + Clone, - U: BorshSerialize + BorshDeserialize + Default + Clone, -{ - pub non_generic: AccountInfo<'info>, - pub generic: Account<'info, T>, - - pub const_generic: AccountLoader<'info, FooAccount>, - pub const_generic_loader: AccountLoader<'info, FooAccount>, - pub associated: Account<'info, Associated>, -} - -#[account(zero_copy)] -pub struct FooAccount { - pub data: WrappedU8Array, -} - -#[account] -#[derive(Default)] -pub struct Associated -where - T: BorshDeserialize + BorshSerialize + Default, -{ - pub data: T, -} - -#[derive(Copy, Clone)] -pub struct WrappedU8Array(u8); -impl BorshSerialize for WrappedU8Array { - fn serialize(&self, _writer: &mut W) -> borsh::maybestd::io::Result<()> { - todo!() - } -} -impl BorshDeserialize for WrappedU8Array { - fn deserialize(_buf: &mut &[u8]) -> borsh::maybestd::io::Result { - todo!() - } -} -impl Owner for WrappedU8Array { - fn owner() -> Pubkey { - crate::ID - } -} diff --git a/ts/package.json b/package.json similarity index 100% rename from ts/package.json rename to package.json diff --git a/ts/rollup.config.ts b/rollup.config.ts similarity index 100% rename from ts/rollup.config.ts rename to rollup.config.ts diff --git a/spl/Cargo.toml b/spl/Cargo.toml deleted file mode 100644 index 42120398..00000000 --- a/spl/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "anchor-spl" -version = "0.24.2" -authors = ["Serum Foundation "] -rust-version = "1.56" -edition = "2021" -license = "Apache-2.0" -description = "CPI clients for SPL programs" - -[features] -default = ["mint", "token", "associated_token"] -mint = [] -token = ["spl-token"] -associated_token = ["spl-associated-token-account"] -governance = [] -shmem = [] -devnet = [] -dex = ["serum_dex"] - -[dependencies] -anchor-lang = { path = "../lang", version = "0.24.2", features = ["derive"] } -serum_dex = { git = "https://github.com/project-serum/serum-dex", rev = "1be91f2", version = "0.4.0", features = ["no-entrypoint"], optional = true } -solana-program = "~1.9.13" -spl-token = { version = "3.1.1", features = ["no-entrypoint"], optional = true } -spl-associated-token-account = { version = "1.0.3", features = ["no-entrypoint"], optional = true } diff --git a/spl/src/associated_token.rs b/spl/src/associated_token.rs deleted file mode 100644 index c46b09b8..00000000 --- a/spl/src/associated_token.rs +++ /dev/null @@ -1,48 +0,0 @@ -use anchor_lang::solana_program::account_info::AccountInfo; -use anchor_lang::solana_program::pubkey::Pubkey; -use anchor_lang::Result; -use anchor_lang::{context::CpiContext, Accounts}; - -pub use spl_associated_token_account::{get_associated_token_address, ID}; - -pub fn create<'info>(ctx: CpiContext<'_, '_, '_, 'info, Create<'info>>) -> Result<()> { - let ix = spl_associated_token_account::create_associated_token_account( - ctx.accounts.payer.key, - ctx.accounts.authority.key, - ctx.accounts.mint.key, - ); - solana_program::program::invoke_signed( - &ix, - &[ - ctx.accounts.payer, - ctx.accounts.associated_token, - ctx.accounts.authority, - ctx.accounts.mint, - ctx.accounts.system_program, - ctx.accounts.token_program, - ctx.accounts.rent, - ], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -#[derive(Accounts)] -pub struct Create<'info> { - pub payer: AccountInfo<'info>, - pub associated_token: AccountInfo<'info>, - pub authority: AccountInfo<'info>, - pub mint: AccountInfo<'info>, - pub system_program: AccountInfo<'info>, - pub token_program: AccountInfo<'info>, - pub rent: AccountInfo<'info>, -} - -#[derive(Clone)] -pub struct AssociatedToken; - -impl anchor_lang::Id for AssociatedToken { - fn id() -> Pubkey { - ID - } -} diff --git a/spl/src/dex.rs b/spl/src/dex.rs deleted file mode 100644 index 460f9cbe..00000000 --- a/spl/src/dex.rs +++ /dev/null @@ -1,299 +0,0 @@ -use anchor_lang::solana_program::account_info::AccountInfo; -use anchor_lang::solana_program::program_error::ProgramError; -use anchor_lang::solana_program::pubkey::Pubkey; -use anchor_lang::{context::CpiContext, Accounts, Result, ToAccountInfos}; -use serum_dex::instruction::SelfTradeBehavior; -use serum_dex::matching::{OrderType, Side}; -use std::num::NonZeroU64; - -pub use serum_dex; - -#[cfg(not(feature = "devnet"))] -anchor_lang::solana_program::declare_id!("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"); - -#[cfg(feature = "devnet")] -anchor_lang::solana_program::declare_id!("DESVgJVGajEgKGXhb6XmqDHGz3VjdgP7rEVESBgxmroY"); - -#[allow(clippy::too_many_arguments)] -pub fn new_order_v3<'info>( - ctx: CpiContext<'_, '_, '_, 'info, NewOrderV3<'info>>, - side: Side, - limit_price: NonZeroU64, - max_coin_qty: NonZeroU64, - max_native_pc_qty_including_fees: NonZeroU64, - self_trade_behavior: SelfTradeBehavior, - order_type: OrderType, - client_order_id: u64, - limit: u16, -) -> Result<()> { - let referral = ctx.remaining_accounts.get(0); - let ix = serum_dex::instruction::new_order( - ctx.accounts.market.key, - ctx.accounts.open_orders.key, - ctx.accounts.request_queue.key, - ctx.accounts.event_queue.key, - ctx.accounts.market_bids.key, - ctx.accounts.market_asks.key, - ctx.accounts.order_payer_token_account.key, - ctx.accounts.open_orders_authority.key, - ctx.accounts.coin_vault.key, - ctx.accounts.pc_vault.key, - ctx.accounts.token_program.key, - ctx.accounts.rent.key, - referral.map(|r| r.key), - &ID, - side, - limit_price, - max_coin_qty, - order_type, - client_order_id, - self_trade_behavior, - limit, - max_native_pc_qty_including_fees, - ) - .map_err(|pe| ProgramError::from(pe))?; - solana_program::program::invoke_signed( - &ix, - &ToAccountInfos::to_account_infos(&ctx), - ctx.signer_seeds, - )?; - Ok(()) -} - -pub fn cancel_order_v2<'info>( - ctx: CpiContext<'_, '_, '_, 'info, CancelOrderV2<'info>>, - side: Side, - order_id: u128, -) -> Result<()> { - let ix = serum_dex::instruction::cancel_order( - &ID, - ctx.accounts.market.key, - ctx.accounts.market_bids.key, - ctx.accounts.market_asks.key, - ctx.accounts.open_orders.key, - ctx.accounts.open_orders_authority.key, - ctx.accounts.event_queue.key, - side, - order_id, - ) - .map_err(|pe| ProgramError::from(pe))?; - solana_program::program::invoke_signed( - &ix, - &ToAccountInfos::to_account_infos(&ctx), - ctx.signer_seeds, - )?; - Ok(()) -} - -pub fn settle_funds<'info>(ctx: CpiContext<'_, '_, '_, 'info, SettleFunds<'info>>) -> Result<()> { - let referral = ctx.remaining_accounts.get(0); - let ix = serum_dex::instruction::settle_funds( - &ID, - ctx.accounts.market.key, - ctx.accounts.token_program.key, - ctx.accounts.open_orders.key, - ctx.accounts.open_orders_authority.key, - ctx.accounts.coin_vault.key, - ctx.accounts.coin_wallet.key, - ctx.accounts.pc_vault.key, - ctx.accounts.pc_wallet.key, - referral.map(|r| r.key), - ctx.accounts.vault_signer.key, - ) - .map_err(|pe| ProgramError::from(pe))?; - solana_program::program::invoke_signed( - &ix, - &ToAccountInfos::to_account_infos(&ctx), - ctx.signer_seeds, - )?; - Ok(()) -} - -pub fn init_open_orders<'info>( - ctx: CpiContext<'_, '_, '_, 'info, InitOpenOrders<'info>>, -) -> Result<()> { - let ix = serum_dex::instruction::init_open_orders( - &ID, - ctx.accounts.open_orders.key, - ctx.accounts.authority.key, - ctx.accounts.market.key, - ctx.remaining_accounts.first().map(|acc| acc.key), - ) - .map_err(|pe| ProgramError::from(pe))?; - solana_program::program::invoke_signed( - &ix, - &ToAccountInfos::to_account_infos(&ctx), - ctx.signer_seeds, - )?; - Ok(()) -} - -pub fn close_open_orders<'info>( - ctx: CpiContext<'_, '_, '_, 'info, CloseOpenOrders<'info>>, -) -> Result<()> { - let ix = serum_dex::instruction::close_open_orders( - &ID, - ctx.accounts.open_orders.key, - ctx.accounts.authority.key, - ctx.accounts.destination.key, - ctx.accounts.market.key, - ) - .map_err(|pe| ProgramError::from(pe))?; - solana_program::program::invoke_signed( - &ix, - &ToAccountInfos::to_account_infos(&ctx), - ctx.signer_seeds, - )?; - Ok(()) -} - -pub fn sweep_fees<'info>(ctx: CpiContext<'_, '_, '_, 'info, SweepFees<'info>>) -> Result<()> { - let ix = serum_dex::instruction::sweep_fees( - &ID, - ctx.accounts.market.key, - ctx.accounts.pc_vault.key, - ctx.accounts.sweep_authority.key, - ctx.accounts.sweep_receiver.key, - ctx.accounts.vault_signer.key, - ctx.accounts.token_program.key, - ) - .map_err(|pe| ProgramError::from(pe))?; - solana_program::program::invoke_signed( - &ix, - &ToAccountInfos::to_account_infos(&ctx), - ctx.signer_seeds, - )?; - Ok(()) -} - -pub fn initialize_market<'info>( - ctx: CpiContext<'_, '_, '_, 'info, InitializeMarket<'info>>, - coin_lot_size: u64, - pc_lot_size: u64, - vault_signer_nonce: u64, - pc_dust_threshold: u64, -) -> Result<()> { - let authority = ctx.remaining_accounts.get(0); - let prune_authority = ctx.remaining_accounts.get(1); - let ix = serum_dex::instruction::initialize_market( - ctx.accounts.market.key, - &ID, - ctx.accounts.coin_mint.key, - ctx.accounts.pc_mint.key, - ctx.accounts.coin_vault.key, - ctx.accounts.pc_vault.key, - authority.map(|r| r.key), - prune_authority.map(|r| r.key), - ctx.accounts.bids.key, - ctx.accounts.asks.key, - ctx.accounts.req_q.key, - ctx.accounts.event_q.key, - coin_lot_size, - pc_lot_size, - vault_signer_nonce, - pc_dust_threshold, - ) - .map_err(|pe| ProgramError::from(pe))?; - solana_program::program::invoke_signed( - &ix, - &ToAccountInfos::to_account_infos(&ctx), - ctx.signer_seeds, - )?; - Ok(()) -} - -#[derive(Accounts)] -pub struct NewOrderV3<'info> { - pub market: AccountInfo<'info>, - pub open_orders: AccountInfo<'info>, - pub request_queue: AccountInfo<'info>, - pub event_queue: AccountInfo<'info>, - pub market_bids: AccountInfo<'info>, - pub market_asks: AccountInfo<'info>, - // Token account where funds are transferred from for the order. If - // posting a bid market A/B, then this is the SPL token account for B. - pub order_payer_token_account: AccountInfo<'info>, - pub open_orders_authority: AccountInfo<'info>, - // Also known as the "base" currency. For a given A/B market, - // this is the vault for the A mint. - pub coin_vault: AccountInfo<'info>, - // Also known as the "quote" currency. For a given A/B market, - // this is the vault for the B mint. - pub pc_vault: AccountInfo<'info>, - pub token_program: AccountInfo<'info>, - pub rent: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct CancelOrderV2<'info> { - pub market: AccountInfo<'info>, - pub market_bids: AccountInfo<'info>, - pub market_asks: AccountInfo<'info>, - pub open_orders: AccountInfo<'info>, - pub open_orders_authority: AccountInfo<'info>, - pub event_queue: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct SettleFunds<'info> { - pub market: AccountInfo<'info>, - pub open_orders: AccountInfo<'info>, - pub open_orders_authority: AccountInfo<'info>, - pub coin_vault: AccountInfo<'info>, - pub pc_vault: AccountInfo<'info>, - pub coin_wallet: AccountInfo<'info>, - pub pc_wallet: AccountInfo<'info>, - pub vault_signer: AccountInfo<'info>, - pub token_program: AccountInfo<'info>, -} - -/// To use an (optional) market authority, add it as the first account of the -/// CpiContext's `remaining_accounts` Vec. -#[derive(Accounts)] -pub struct InitOpenOrders<'info> { - pub open_orders: AccountInfo<'info>, - pub authority: AccountInfo<'info>, - pub market: AccountInfo<'info>, - pub rent: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct CloseOpenOrders<'info> { - pub open_orders: AccountInfo<'info>, - pub authority: AccountInfo<'info>, - pub destination: AccountInfo<'info>, - pub market: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct SweepFees<'info> { - pub market: AccountInfo<'info>, - pub pc_vault: AccountInfo<'info>, - pub sweep_authority: AccountInfo<'info>, - pub sweep_receiver: AccountInfo<'info>, - pub vault_signer: AccountInfo<'info>, - pub token_program: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct InitializeMarket<'info> { - pub market: AccountInfo<'info>, - pub coin_mint: AccountInfo<'info>, - pub pc_mint: AccountInfo<'info>, - pub coin_vault: AccountInfo<'info>, - pub pc_vault: AccountInfo<'info>, - pub bids: AccountInfo<'info>, - pub asks: AccountInfo<'info>, - pub req_q: AccountInfo<'info>, - pub event_q: AccountInfo<'info>, - pub rent: AccountInfo<'info>, -} - -#[derive(Clone)] -pub struct Dex; - -impl anchor_lang::Id for Dex { - fn id() -> Pubkey { - ID - } -} diff --git a/spl/src/governance.rs b/spl/src/governance.rs deleted file mode 100644 index 48ae908e..00000000 --- a/spl/src/governance.rs +++ /dev/null @@ -1,58 +0,0 @@ -/// A macro is exposed so that we can embed the program ID. -#[macro_export] -macro_rules! vote_weight_record { - ($id:expr) => { - /// Anchor wrapper for the SPL governance program's VoterWeightRecord type. - #[derive(Clone)] - pub struct VoterWeightRecord(spl_governance_addin_api::voter_weight::VoterWeightRecord); - - impl anchor_lang::AccountDeserialize for VoterWeightRecord { - fn try_deserialize(buf: &mut &[u8]) -> anchor_lang::Result { - let mut data = buf; - let vwr: spl_governance_addin_api::voter_weight::VoterWeightRecord = - anchor_lang::AnchorDeserialize::deserialize(&mut data) - .map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotDeserialize)?; - if !solana_program::program_pack::IsInitialized::is_initialized(&vwr) { - return Err(anchor_lang::error::ErrorCode::AccountDidNotSerialize.into()); - } - Ok(VoterWeightRecord(vwr)) - } - - fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result { - let mut data = buf; - let vwr: spl_governance_addin_api::voter_weight::VoterWeightRecord = - anchor_lang::AnchorDeserialize::deserialize(&mut data) - .map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotDeserialize)?; - Ok(VoterWeightRecord(vwr)) - } - } - - impl anchor_lang::AccountSerialize for VoterWeightRecord { - fn try_serialize(&self, writer: &mut W) -> anchor_lang::Result<()> { - anchor_lang::AnchorSerialize::serialize(&self.0, writer) - .map_err(|_| anchor_lang::error::ErrorCode::AccountDidNotSerialize)?; - Ok(()) - } - } - - impl anchor_lang::Owner for VoterWeightRecord { - fn owner() -> Pubkey { - $id - } - } - - impl std::ops::Deref for VoterWeightRecord { - type Target = spl_governance_addin_api::voter_weight::VoterWeightRecord; - - fn deref(&self) -> &Self::Target { - &self.0 - } - } - - impl std::ops::DerefMut for VoterWeightRecord { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } - } - }; -} diff --git a/spl/src/lib.rs b/spl/src/lib.rs deleted file mode 100644 index 62d09628..00000000 --- a/spl/src/lib.rs +++ /dev/null @@ -1,17 +0,0 @@ -#[cfg(feature = "associated_token")] -pub mod associated_token; - -#[cfg(feature = "mint")] -pub mod mint; - -#[cfg(feature = "token")] -pub mod token; - -#[cfg(feature = "dex")] -pub mod dex; - -#[cfg(feature = "governance")] -pub mod governance; - -#[cfg(feature = "shmem")] -pub mod shmem; diff --git a/spl/src/mint.rs b/spl/src/mint.rs deleted file mode 100644 index 0444becc..00000000 --- a/spl/src/mint.rs +++ /dev/null @@ -1,13 +0,0 @@ -use anchor_lang::solana_program::declare_id; - -pub use srm::ID as SRM; -mod srm { - use super::*; - declare_id!("SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt"); -} - -pub use usdc::ID as USDC; -mod usdc { - use super::*; - declare_id!("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"); -} diff --git a/spl/src/shmem.rs b/spl/src/shmem.rs deleted file mode 100644 index 450c6806..00000000 --- a/spl/src/shmem.rs +++ /dev/null @@ -1,50 +0,0 @@ -//! CPI API for interacting with the SPL shared memory -//! [program](https://github.com/solana-labs/solana-program-library/tree/master/shared-memory). - -use anchor_lang::ToAccountInfo; -use anchor_lang::{context::CpiContext, Accounts}; -use solana_program::account_info::AccountInfo; -use solana_program::declare_id; -use solana_program::entrypoint::ProgramResult; -use solana_program::instruction::{AccountMeta, Instruction}; -use solana_program::program; - -// TODO: update this once the final shared memory program gets released. -// shmem4EWT2sPdVGvTZCzXXRAURL9G5vpPxNwSeKhHUL. -declare_id!("DynWy94wrWp5RimU49creYMQ5py3Up8BBNS4VA73VCpi"); - -/// `ret` writes the given `data` field to the shared memory account -/// acting as a return value that can be used across CPI. -/// The caleee should use this to write data into the shared memory account. -/// The caler should use the account directly to pull out and interpret the -/// bytes. Shared memory serialization is not specified and is up to the -/// caller and callee. -pub fn ret<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, Ret<'info>>, - data: Vec, -) -> ProgramResult { - let instruction = Instruction { - program_id: *ctx.program.key, - accounts: vec![AccountMeta::new(*ctx.accounts.buffer.key, false)], - data, - }; - let mut accounts = vec![ctx.accounts.buffer]; - accounts.push(ctx.program.clone()); - program::invoke(&instruction, &accounts) -} - -#[derive(Accounts)] -pub struct Ret<'info> { - #[account(mut)] - pub buffer: AccountInfo<'info>, -} - -// A set of accounts that can be used with shared memory. -#[derive(Accounts)] -pub struct Shmem<'info> { - // Shared memory account to write the return value into. - #[account(mut, constraint = shmem.owner == shmem_program.key)] - pub shmem: AccountInfo<'info>, - #[account(constraint = shmem_program.key == &ID)] - pub shmem_program: AccountInfo<'info>, -} diff --git a/spl/src/token.rs b/spl/src/token.rs deleted file mode 100644 index 555ffdc0..00000000 --- a/spl/src/token.rs +++ /dev/null @@ -1,432 +0,0 @@ -use anchor_lang::solana_program::account_info::AccountInfo; - -use anchor_lang::solana_program::program_pack::Pack; -use anchor_lang::solana_program::pubkey::Pubkey; -use anchor_lang::{context::CpiContext, Accounts}; -use anchor_lang::{solana_program, Result}; -use std::ops::Deref; - -pub use spl_token; -pub use spl_token::ID; - -pub fn transfer<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, Transfer<'info>>, - amount: u64, -) -> Result<()> { - let ix = spl_token::instruction::transfer( - &spl_token::ID, - ctx.accounts.from.key, - ctx.accounts.to.key, - ctx.accounts.authority.key, - &[], - amount, - )?; - solana_program::program::invoke_signed( - &ix, - &[ - ctx.accounts.from.clone(), - ctx.accounts.to.clone(), - ctx.accounts.authority.clone(), - ], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -pub fn mint_to<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, MintTo<'info>>, - amount: u64, -) -> Result<()> { - let ix = spl_token::instruction::mint_to( - &spl_token::ID, - ctx.accounts.mint.key, - ctx.accounts.to.key, - ctx.accounts.authority.key, - &[], - amount, - )?; - solana_program::program::invoke_signed( - &ix, - &[ - ctx.accounts.to.clone(), - ctx.accounts.mint.clone(), - ctx.accounts.authority.clone(), - ], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -pub fn burn<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, Burn<'info>>, - amount: u64, -) -> Result<()> { - let ix = spl_token::instruction::burn( - &spl_token::ID, - ctx.accounts.from.key, - ctx.accounts.mint.key, - ctx.accounts.authority.key, - &[], - amount, - )?; - solana_program::program::invoke_signed( - &ix, - &[ - ctx.accounts.from.clone(), - ctx.accounts.mint.clone(), - ctx.accounts.authority.clone(), - ], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -pub fn approve<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, Approve<'info>>, - amount: u64, -) -> Result<()> { - let ix = spl_token::instruction::approve( - &spl_token::ID, - ctx.accounts.to.key, - ctx.accounts.delegate.key, - ctx.accounts.authority.key, - &[], - amount, - )?; - solana_program::program::invoke_signed( - &ix, - &[ - ctx.accounts.to.clone(), - ctx.accounts.delegate.clone(), - ctx.accounts.authority.clone(), - ], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -pub fn revoke<'a, 'b, 'c, 'info>(ctx: CpiContext<'a, 'b, 'c, 'info, Revoke<'info>>) -> Result<()> { - let ix = spl_token::instruction::revoke( - &spl_token::ID, - ctx.accounts.source.key, - ctx.accounts.authority.key, - &[], - )?; - solana_program::program::invoke_signed( - &ix, - &[ctx.accounts.source.clone(), ctx.accounts.authority.clone()], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -pub fn initialize_account<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, InitializeAccount<'info>>, -) -> Result<()> { - let ix = spl_token::instruction::initialize_account( - &spl_token::ID, - ctx.accounts.account.key, - ctx.accounts.mint.key, - ctx.accounts.authority.key, - )?; - solana_program::program::invoke_signed( - &ix, - &[ - ctx.accounts.account.clone(), - ctx.accounts.mint.clone(), - ctx.accounts.authority.clone(), - ctx.accounts.rent.clone(), - ], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -pub fn close_account<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, CloseAccount<'info>>, -) -> Result<()> { - let ix = spl_token::instruction::close_account( - &spl_token::ID, - ctx.accounts.account.key, - ctx.accounts.destination.key, - ctx.accounts.authority.key, - &[], // TODO: support multisig - )?; - solana_program::program::invoke_signed( - &ix, - &[ - ctx.accounts.account.clone(), - ctx.accounts.destination.clone(), - ctx.accounts.authority.clone(), - ], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -pub fn freeze_account<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, FreezeAccount<'info>>, -) -> Result<()> { - let ix = spl_token::instruction::freeze_account( - &spl_token::ID, - ctx.accounts.account.key, - ctx.accounts.mint.key, - ctx.accounts.authority.key, - &[], // TODO: Support multisig signers. - )?; - solana_program::program::invoke_signed( - &ix, - &[ - ctx.accounts.account.clone(), - ctx.accounts.mint.clone(), - ctx.accounts.authority.clone(), - ], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -pub fn thaw_account<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, ThawAccount<'info>>, -) -> Result<()> { - let ix = spl_token::instruction::thaw_account( - &spl_token::ID, - ctx.accounts.account.key, - ctx.accounts.mint.key, - ctx.accounts.authority.key, - &[], // TODO: Support multisig signers. - )?; - solana_program::program::invoke_signed( - &ix, - &[ - ctx.accounts.account.clone(), - ctx.accounts.mint.clone(), - ctx.accounts.authority.clone(), - ], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -pub fn initialize_mint<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, InitializeMint<'info>>, - decimals: u8, - authority: &Pubkey, - freeze_authority: Option<&Pubkey>, -) -> Result<()> { - let ix = spl_token::instruction::initialize_mint( - &spl_token::ID, - ctx.accounts.mint.key, - authority, - freeze_authority, - decimals, - )?; - solana_program::program::invoke_signed( - &ix, - &[ctx.accounts.mint.clone(), ctx.accounts.rent.clone()], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -pub fn set_authority<'a, 'b, 'c, 'info>( - ctx: CpiContext<'a, 'b, 'c, 'info, SetAuthority<'info>>, - authority_type: spl_token::instruction::AuthorityType, - new_authority: Option, -) -> Result<()> { - let mut spl_new_authority: Option<&Pubkey> = None; - if new_authority.is_some() { - spl_new_authority = new_authority.as_ref() - } - - let ix = spl_token::instruction::set_authority( - &spl_token::ID, - ctx.accounts.account_or_mint.key, - spl_new_authority, - authority_type, - ctx.accounts.current_authority.key, - &[], // TODO: Support multisig signers. - )?; - solana_program::program::invoke_signed( - &ix, - &[ - ctx.accounts.account_or_mint.clone(), - ctx.accounts.current_authority.clone(), - ], - ctx.signer_seeds, - ) - .map_err(Into::into) -} - -#[derive(Accounts)] -pub struct Transfer<'info> { - pub from: AccountInfo<'info>, - pub to: AccountInfo<'info>, - pub authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct MintTo<'info> { - pub mint: AccountInfo<'info>, - pub to: AccountInfo<'info>, - pub authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct Burn<'info> { - pub mint: AccountInfo<'info>, - pub from: AccountInfo<'info>, - pub authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct Approve<'info> { - pub to: AccountInfo<'info>, - pub delegate: AccountInfo<'info>, - pub authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct Revoke<'info> { - pub source: AccountInfo<'info>, - pub authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct InitializeAccount<'info> { - pub account: AccountInfo<'info>, - pub mint: AccountInfo<'info>, - pub authority: AccountInfo<'info>, - pub rent: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct CloseAccount<'info> { - pub account: AccountInfo<'info>, - pub destination: AccountInfo<'info>, - pub authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct FreezeAccount<'info> { - pub account: AccountInfo<'info>, - pub mint: AccountInfo<'info>, - pub authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct ThawAccount<'info> { - pub account: AccountInfo<'info>, - pub mint: AccountInfo<'info>, - pub authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct InitializeMint<'info> { - pub mint: AccountInfo<'info>, - pub rent: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct SetAuthority<'info> { - pub current_authority: AccountInfo<'info>, - pub account_or_mint: AccountInfo<'info>, -} - -#[derive(Clone)] -pub struct TokenAccount(spl_token::state::Account); - -impl TokenAccount { - pub const LEN: usize = spl_token::state::Account::LEN; -} - -impl anchor_lang::AccountDeserialize for TokenAccount { - fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result { - spl_token::state::Account::unpack(buf) - .map(TokenAccount) - .map_err(Into::into) - } -} - -impl anchor_lang::AccountSerialize for TokenAccount {} - -impl anchor_lang::Owner for TokenAccount { - fn owner() -> Pubkey { - ID - } -} - -impl Deref for TokenAccount { - type Target = spl_token::state::Account; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -#[derive(Clone)] -pub struct Mint(spl_token::state::Mint); - -impl Mint { - pub const LEN: usize = spl_token::state::Mint::LEN; -} - -impl anchor_lang::AccountDeserialize for Mint { - fn try_deserialize_unchecked(buf: &mut &[u8]) -> anchor_lang::Result { - spl_token::state::Mint::unpack(buf) - .map(Mint) - .map_err(Into::into) - } -} - -impl anchor_lang::AccountSerialize for Mint {} - -impl anchor_lang::Owner for Mint { - fn owner() -> Pubkey { - ID - } -} - -impl Deref for Mint { - type Target = spl_token::state::Mint; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -#[derive(Clone)] -pub struct Token; - -impl anchor_lang::Id for Token { - fn id() -> Pubkey { - ID - } -} - -// Field parsers to save compute. All account validation is assumed to be done -// outside of these methods. -pub mod accessor { - use super::*; - - pub fn amount(account: &AccountInfo) -> Result { - let bytes = account.try_borrow_data()?; - let mut amount_bytes = [0u8; 8]; - amount_bytes.copy_from_slice(&bytes[64..72]); - Ok(u64::from_le_bytes(amount_bytes)) - } - - pub fn mint(account: &AccountInfo) -> Result { - let bytes = account.try_borrow_data()?; - let mut mint_bytes = [0u8; 32]; - mint_bytes.copy_from_slice(&bytes[..32]); - Ok(Pubkey::new_from_array(mint_bytes)) - } - - pub fn authority(account: &AccountInfo) -> Result { - let bytes = account.try_borrow_data()?; - let mut owner_bytes = [0u8; 32]; - owner_bytes.copy_from_slice(&bytes[32..64]); - Ok(Pubkey::new_from_array(owner_bytes)) - } -} diff --git a/ts/src/coder/borsh/accounts.ts b/src/coder/borsh/accounts.ts similarity index 100% rename from ts/src/coder/borsh/accounts.ts rename to src/coder/borsh/accounts.ts diff --git a/ts/src/coder/borsh/event.ts b/src/coder/borsh/event.ts similarity index 100% rename from ts/src/coder/borsh/event.ts rename to src/coder/borsh/event.ts diff --git a/ts/src/coder/borsh/idl.ts b/src/coder/borsh/idl.ts similarity index 100% rename from ts/src/coder/borsh/idl.ts rename to src/coder/borsh/idl.ts diff --git a/ts/src/coder/borsh/index.ts b/src/coder/borsh/index.ts similarity index 100% rename from ts/src/coder/borsh/index.ts rename to src/coder/borsh/index.ts diff --git a/ts/src/coder/borsh/instruction.ts b/src/coder/borsh/instruction.ts similarity index 100% rename from ts/src/coder/borsh/instruction.ts rename to src/coder/borsh/instruction.ts diff --git a/ts/src/coder/borsh/state.ts b/src/coder/borsh/state.ts similarity index 100% rename from ts/src/coder/borsh/state.ts rename to src/coder/borsh/state.ts diff --git a/ts/src/coder/common.ts b/src/coder/common.ts similarity index 100% rename from ts/src/coder/common.ts rename to src/coder/common.ts diff --git a/ts/src/coder/index.ts b/src/coder/index.ts similarity index 100% rename from ts/src/coder/index.ts rename to src/coder/index.ts diff --git a/ts/src/coder/spl-token/accounts.ts b/src/coder/spl-token/accounts.ts similarity index 100% rename from ts/src/coder/spl-token/accounts.ts rename to src/coder/spl-token/accounts.ts diff --git a/ts/src/coder/spl-token/buffer-layout.ts b/src/coder/spl-token/buffer-layout.ts similarity index 100% rename from ts/src/coder/spl-token/buffer-layout.ts rename to src/coder/spl-token/buffer-layout.ts diff --git a/ts/src/coder/spl-token/events.ts b/src/coder/spl-token/events.ts similarity index 100% rename from ts/src/coder/spl-token/events.ts rename to src/coder/spl-token/events.ts diff --git a/ts/src/coder/spl-token/index.ts b/src/coder/spl-token/index.ts similarity index 100% rename from ts/src/coder/spl-token/index.ts rename to src/coder/spl-token/index.ts diff --git a/ts/src/coder/spl-token/instruction.ts b/src/coder/spl-token/instruction.ts similarity index 100% rename from ts/src/coder/spl-token/instruction.ts rename to src/coder/spl-token/instruction.ts diff --git a/ts/src/coder/spl-token/state.ts b/src/coder/spl-token/state.ts similarity index 100% rename from ts/src/coder/spl-token/state.ts rename to src/coder/spl-token/state.ts diff --git a/ts/src/error.ts b/src/error.ts similarity index 100% rename from ts/src/error.ts rename to src/error.ts diff --git a/ts/src/idl.ts b/src/idl.ts similarity index 100% rename from ts/src/idl.ts rename to src/idl.ts diff --git a/ts/src/index.ts b/src/index.ts similarity index 100% rename from ts/src/index.ts rename to src/index.ts diff --git a/ts/src/nodewallet.ts b/src/nodewallet.ts similarity index 100% rename from ts/src/nodewallet.ts rename to src/nodewallet.ts diff --git a/ts/src/program/accounts-resolver.ts b/src/program/accounts-resolver.ts similarity index 100% rename from ts/src/program/accounts-resolver.ts rename to src/program/accounts-resolver.ts diff --git a/ts/src/program/common.ts b/src/program/common.ts similarity index 100% rename from ts/src/program/common.ts rename to src/program/common.ts diff --git a/ts/src/program/context.ts b/src/program/context.ts similarity index 100% rename from ts/src/program/context.ts rename to src/program/context.ts diff --git a/ts/src/program/event.ts b/src/program/event.ts similarity index 100% rename from ts/src/program/event.ts rename to src/program/event.ts diff --git a/ts/src/program/index.ts b/src/program/index.ts similarity index 100% rename from ts/src/program/index.ts rename to src/program/index.ts diff --git a/ts/src/program/namespace/account.ts b/src/program/namespace/account.ts similarity index 100% rename from ts/src/program/namespace/account.ts rename to src/program/namespace/account.ts diff --git a/ts/src/program/namespace/index.ts b/src/program/namespace/index.ts similarity index 100% rename from ts/src/program/namespace/index.ts rename to src/program/namespace/index.ts diff --git a/ts/src/program/namespace/instruction.ts b/src/program/namespace/instruction.ts similarity index 100% rename from ts/src/program/namespace/instruction.ts rename to src/program/namespace/instruction.ts diff --git a/ts/src/program/namespace/methods.ts b/src/program/namespace/methods.ts similarity index 100% rename from ts/src/program/namespace/methods.ts rename to src/program/namespace/methods.ts diff --git a/ts/src/program/namespace/rpc.ts b/src/program/namespace/rpc.ts similarity index 100% rename from ts/src/program/namespace/rpc.ts rename to src/program/namespace/rpc.ts diff --git a/ts/src/program/namespace/simulate.ts b/src/program/namespace/simulate.ts similarity index 100% rename from ts/src/program/namespace/simulate.ts rename to src/program/namespace/simulate.ts diff --git a/ts/src/program/namespace/state.ts b/src/program/namespace/state.ts similarity index 100% rename from ts/src/program/namespace/state.ts rename to src/program/namespace/state.ts diff --git a/ts/src/program/namespace/transaction.ts b/src/program/namespace/transaction.ts similarity index 100% rename from ts/src/program/namespace/transaction.ts rename to src/program/namespace/transaction.ts diff --git a/ts/src/program/namespace/types.ts b/src/program/namespace/types.ts similarity index 100% rename from ts/src/program/namespace/types.ts rename to src/program/namespace/types.ts diff --git a/ts/src/program/namespace/views.ts b/src/program/namespace/views.ts similarity index 100% rename from ts/src/program/namespace/views.ts rename to src/program/namespace/views.ts diff --git a/ts/src/provider.ts b/src/provider.ts similarity index 100% rename from ts/src/provider.ts rename to src/provider.ts diff --git a/ts/src/spl/index.ts b/src/spl/index.ts similarity index 100% rename from ts/src/spl/index.ts rename to src/spl/index.ts diff --git a/ts/src/spl/token.ts b/src/spl/token.ts similarity index 100% rename from ts/src/spl/token.ts rename to src/spl/token.ts diff --git a/ts/src/utils/bytes/base64.ts b/src/utils/bytes/base64.ts similarity index 100% rename from ts/src/utils/bytes/base64.ts rename to src/utils/bytes/base64.ts diff --git a/ts/src/utils/bytes/bs58.ts b/src/utils/bytes/bs58.ts similarity index 100% rename from ts/src/utils/bytes/bs58.ts rename to src/utils/bytes/bs58.ts diff --git a/ts/src/utils/bytes/hex.ts b/src/utils/bytes/hex.ts similarity index 100% rename from ts/src/utils/bytes/hex.ts rename to src/utils/bytes/hex.ts diff --git a/ts/src/utils/bytes/index.ts b/src/utils/bytes/index.ts similarity index 100% rename from ts/src/utils/bytes/index.ts rename to src/utils/bytes/index.ts diff --git a/ts/src/utils/bytes/utf8.ts b/src/utils/bytes/utf8.ts similarity index 100% rename from ts/src/utils/bytes/utf8.ts rename to src/utils/bytes/utf8.ts diff --git a/ts/src/utils/common.ts b/src/utils/common.ts similarity index 100% rename from ts/src/utils/common.ts rename to src/utils/common.ts diff --git a/ts/src/utils/features.ts b/src/utils/features.ts similarity index 100% rename from ts/src/utils/features.ts rename to src/utils/features.ts diff --git a/ts/src/utils/index.ts b/src/utils/index.ts similarity index 100% rename from ts/src/utils/index.ts rename to src/utils/index.ts diff --git a/ts/src/utils/pubkey.ts b/src/utils/pubkey.ts similarity index 100% rename from ts/src/utils/pubkey.ts rename to src/utils/pubkey.ts diff --git a/ts/src/utils/registry.ts b/src/utils/registry.ts similarity index 100% rename from ts/src/utils/registry.ts rename to src/utils/registry.ts diff --git a/ts/src/utils/rpc.ts b/src/utils/rpc.ts similarity index 100% rename from ts/src/utils/rpc.ts rename to src/utils/rpc.ts diff --git a/ts/src/utils/sha256.ts b/src/utils/sha256.ts similarity index 100% rename from ts/src/utils/sha256.ts rename to src/utils/sha256.ts diff --git a/ts/src/utils/token.ts b/src/utils/token.ts similarity index 100% rename from ts/src/utils/token.ts rename to src/utils/token.ts diff --git a/ts/src/workspace.ts b/src/workspace.ts similarity index 100% rename from ts/src/workspace.ts rename to src/workspace.ts diff --git a/tests/.prettierignore b/tests/.prettierignore deleted file mode 100644 index 08fc59ee..00000000 --- a/tests/.prettierignore +++ /dev/null @@ -1,3 +0,0 @@ -**/target/types/*.ts -cfo/deps/ -auction-house/deps/ \ No newline at end of file diff --git a/tests/README.md b/tests/README.md deleted file mode 100644 index e81d409c..00000000 --- a/tests/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Tests - -No program here is guaranteed to be safe or secure in any way. In most cases, they are -simply just simple integration tests to illustrate some particular functionality -of the framework. If used, one should audit any programs used and take full -responsibility for the consequences that occur due to any outstanding bugs -or security vulnerabilities that exist. diff --git a/tests/auction-house b/tests/auction-house deleted file mode 160000 index 2d4d9583..00000000 --- a/tests/auction-house +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2d4d9583467d697267b87b250af8c8bd360f3745 diff --git a/tests/bpf-upgradeable-state/.gitignore b/tests/bpf-upgradeable-state/.gitignore deleted file mode 100644 index 8ee01d32..00000000 --- a/tests/bpf-upgradeable-state/.gitignore +++ /dev/null @@ -1 +0,0 @@ -yarn.lock diff --git a/tests/bpf-upgradeable-state/Anchor.toml b/tests/bpf-upgradeable-state/Anchor.toml deleted file mode 100644 index 84740b9e..00000000 --- a/tests/bpf-upgradeable-state/Anchor.toml +++ /dev/null @@ -1,12 +0,0 @@ -[programs.localnet] -bpf_upgradeable_state = "Cum9tTyj5HwcEiAmhgaS7Bbj4UczCwsucrCkxRECzM4e" - -[registry] -url = "https://anchor.projectserum.com" - -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[scripts] -test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" diff --git a/tests/bpf-upgradeable-state/Cargo.toml b/tests/bpf-upgradeable-state/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/bpf-upgradeable-state/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/bpf-upgradeable-state/bpf_upgradeable_state-keypair.json b/tests/bpf-upgradeable-state/bpf_upgradeable_state-keypair.json deleted file mode 100644 index ae39fe95..00000000 --- a/tests/bpf-upgradeable-state/bpf_upgradeable_state-keypair.json +++ /dev/null @@ -1 +0,0 @@ -[114,99,192,17,48,208,90,184,231,46,220,91,47,115,132,253,218,163,228,101,8,121,220,138,41,140,176,127,254,91,51,28,176,244,174,182,223,57,57,125,117,201,31,213,9,39,207,212,100,173,88,252,61,235,89,156,53,86,4,90,16,251,191,219] \ No newline at end of file diff --git a/tests/bpf-upgradeable-state/migrations/deploy.ts b/tests/bpf-upgradeable-state/migrations/deploy.ts deleted file mode 100644 index 5e3df0dc..00000000 --- a/tests/bpf-upgradeable-state/migrations/deploy.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Migrations are an early feature. Currently, they're nothing more than this -// single deploy script that's invoked from the CLI, injecting a provider -// configured from the workspace's Anchor.toml. - -const anchor = require("@project-serum/anchor"); - -module.exports = async function (provider) { - // Configure client to use the provider. - anchor.setProvider(provider); - - // Add your deploy script here. -}; diff --git a/tests/bpf-upgradeable-state/package.json b/tests/bpf-upgradeable-state/package.json deleted file mode 100644 index cb43b909..00000000 --- a/tests/bpf-upgradeable-state/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "bpf-upgradeable-state", - "version": "0.24.0", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - } -} diff --git a/tests/bpf-upgradeable-state/program_with_different_programdata.json b/tests/bpf-upgradeable-state/program_with_different_programdata.json deleted file mode 100644 index 8825b0f8..00000000 --- a/tests/bpf-upgradeable-state/program_with_different_programdata.json +++ /dev/null @@ -1 +0,0 @@ -[86,234,116,86,82,140,116,250,254,32,75,217,35,39,9,238,39,98,242,254,25,216,201,66,1,239,93,12,81,19,34,108,219,67,158,98,245,234,81,126,228,157,205,206,130,5,14,54,1,21,88,246,128,124,240,93,157,49,102,19,253,19,205,178] \ No newline at end of file diff --git a/tests/bpf-upgradeable-state/programs/bpf-upgradeable-state/Cargo.toml b/tests/bpf-upgradeable-state/programs/bpf-upgradeable-state/Cargo.toml deleted file mode 100644 index 637cf46a..00000000 --- a/tests/bpf-upgradeable-state/programs/bpf-upgradeable-state/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "bpf-upgradeable-state" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "bpf_upgradeable_state" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } diff --git a/tests/bpf-upgradeable-state/programs/bpf-upgradeable-state/Xargo.toml b/tests/bpf-upgradeable-state/programs/bpf-upgradeable-state/Xargo.toml deleted file mode 100644 index 475fb71e..00000000 --- a/tests/bpf-upgradeable-state/programs/bpf-upgradeable-state/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] diff --git a/tests/bpf-upgradeable-state/programs/bpf-upgradeable-state/src/lib.rs b/tests/bpf-upgradeable-state/programs/bpf-upgradeable-state/src/lib.rs deleted file mode 100644 index 53fdf435..00000000 --- a/tests/bpf-upgradeable-state/programs/bpf-upgradeable-state/src/lib.rs +++ /dev/null @@ -1,80 +0,0 @@ -use anchor_lang::prelude::*; - -declare_id!("Cum9tTyj5HwcEiAmhgaS7Bbj4UczCwsucrCkxRECzM4e"); - -#[program] -pub mod bpf_upgradeable_state { - use super::*; - pub fn set_admin_settings(ctx: Context, admin_data: u64) -> Result<()> { - match *ctx.accounts.program { - UpgradeableLoaderState::Program { - programdata_address, - } => { - if programdata_address != ctx.accounts.program_data.key() { - return err!(CustomError::InvalidProgramDataAddress); - } - } - _ => { - return err!(CustomError::AccountNotProgram); - } - }; - ctx.accounts.settings.admin_data = admin_data; - Ok(()) - } - - pub fn set_admin_settings_use_program_state( - ctx: Context, - admin_data: u64, - ) -> Result<()> { - ctx.accounts.settings.admin_data = admin_data; - Ok(()) - } -} - -#[account] -pub struct Settings { - admin_data: u64, -} - -impl Settings { - pub const LEN: usize = 8; -} - -#[error_code] -pub enum CustomError { - InvalidProgramDataAddress, - AccountNotProgram, - AccountNotBpfUpgradableProgram, -} - -#[derive(Accounts)] -pub struct SetAdminSettings<'info> { - // In a real program, this should be a PDA, - // so the authority cannot create multiple settings accounts. - // Not done here for easier testing - #[account(init, payer = authority, space = Settings::LEN + 8)] - pub settings: Account<'info, Settings>, - #[account(mut)] - pub authority: Signer<'info>, - #[account(address = crate::ID)] - pub program: Account<'info, UpgradeableLoaderState>, - #[account(constraint = program_data.upgrade_authority_address == Some(authority.key()))] - pub program_data: Account<'info, ProgramData>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct SetAdminSettingsUseProgramState<'info> { - // In a real program, this should be a PDA, - // so the authority cannot create multiple settings accounts. - // Not done here for easier testing - #[account(init, payer = authority, space = Settings::LEN + 8)] - pub settings: Account<'info, Settings>, - #[account(mut)] - pub authority: Signer<'info>, - #[account(constraint = program.programdata_address()? == Some(program_data.key()))] - pub program: Program<'info, crate::program::BpfUpgradeableState>, - #[account(constraint = program_data.upgrade_authority_address == Some(authority.key()))] - pub program_data: Account<'info, ProgramData>, - pub system_program: Program<'info, System>, -} diff --git a/tests/bpf-upgradeable-state/tests/bpf-upgradable-state.ts b/tests/bpf-upgradeable-state/tests/bpf-upgradable-state.ts deleted file mode 100644 index 16ed36af..00000000 --- a/tests/bpf-upgradeable-state/tests/bpf-upgradable-state.ts +++ /dev/null @@ -1,203 +0,0 @@ -import * as anchor from "@project-serum/anchor"; -import { AnchorError, Program } from "@project-serum/anchor"; -import { findProgramAddressSync } from "@project-serum/anchor/dist/cjs/utils/pubkey"; -import { PublicKey } from "@solana/web3.js"; -import { assert } from "chai"; -import { BpfUpgradeableState } from "../target/types/bpf_upgradeable_state"; - -describe("bpf_upgradeable_state", () => { - const provider = anchor.AnchorProvider.env(); - // Configure the client to use the local cluster. - anchor.setProvider(provider); - - const program = anchor.workspace - .BpfUpgradeableState as Program; - const programDataAddress = findProgramAddressSync( - [program.programId.toBytes()], - new anchor.web3.PublicKey("BPFLoaderUpgradeab1e11111111111111111111111") - )[0]; - - it("Reads ProgramData and sets field", async () => { - const settings = anchor.web3.Keypair.generate(); - const tx = await program.rpc.setAdminSettings(new anchor.BN(500), { - accounts: { - authority: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - programData: programDataAddress, - program: program.programId, - settings: settings.publicKey, - }, - signers: [settings], - }); - assert.strictEqual( - ( - await program.account.settings.fetch(settings.publicKey) - ).adminData.toNumber(), - 500 - ); - }); - - it("Reads ProgramData and sets field, uses program state", async () => { - const settings = anchor.web3.Keypair.generate(); - const tx = await program.rpc.setAdminSettingsUseProgramState( - new anchor.BN(500), - { - accounts: { - authority: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - programData: programDataAddress, - program: program.programId, - settings: settings.publicKey, - }, - signers: [settings], - } - ); - assert.strictEqual( - ( - await program.account.settings.fetch(settings.publicKey) - ).adminData.toNumber(), - 500 - ); - }); - - it("Validates constraint on ProgramData", async () => { - const settings = anchor.web3.Keypair.generate(); - try { - const authority = anchor.web3.Keypair.generate(); - await provider.connection.confirmTransaction( - await provider.connection.requestAirdrop( - authority.publicKey, - 10000000000 - ), - "confirmed" - ); - await program.rpc.setAdminSettings(new anchor.BN(500), { - accounts: { - authority: authority.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - programData: programDataAddress, - settings: settings.publicKey, - program: program.programId, - }, - signers: [settings, authority], - }); - assert.ok(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2003); - assert.strictEqual( - err.error.errorMessage, - "A raw constraint was violated" - ); - } - }); - - it("Validates that account is ProgramData", async () => { - const settings = anchor.web3.Keypair.generate(); - try { - await program.rpc.setAdminSettings(new anchor.BN(500), { - accounts: { - authority: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - programData: program.programId, - settings: settings.publicKey, - program: program.programId, - }, - signers: [settings], - }); - assert.ok(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 3013); - assert.strictEqual( - err.error.errorMessage, - "The given account is not a program data account" - ); - } - }); - - it("Validates that account is owned by the upgradeable bpf loader", async () => { - const settings = anchor.web3.Keypair.generate(); - try { - await program.rpc.setAdminSettings(new anchor.BN(500), { - accounts: { - authority: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - programData: program.provider.wallet.publicKey, - settings: settings.publicKey, - program: program.programId, - }, - signers: [settings], - }); - assert.ok(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 3007); - assert.strictEqual( - err.error.errorMessage, - "The given account is owned by a different program than expected" - ); - } - }); - - it("Deserializes UpgradableLoaderState and validates that programData is the expected account", async () => { - const secondProgramAddress = new PublicKey( - "Fkv67TwmbakfZw2PoW57wYPbqNexAH6vuxpyT8vmrc3B" - ); - const secondProgramProgramDataAddress = findProgramAddressSync( - [secondProgramAddress.toBytes()], - new anchor.web3.PublicKey("BPFLoaderUpgradeab1e11111111111111111111111") - )[0]; - - const settings = anchor.web3.Keypair.generate(); - try { - await program.rpc.setAdminSettings(new anchor.BN(500), { - accounts: { - authority: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - programData: secondProgramProgramDataAddress, - settings: settings.publicKey, - program: program.programId, - }, - signers: [settings], - }); - assert.ok(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 6000); - } - }); - - it("Deserializes Program and validates that programData is the expected account", async () => { - const secondProgramAddress = new PublicKey( - "Fkv67TwmbakfZw2PoW57wYPbqNexAH6vuxpyT8vmrc3B" - ); - const secondProgramProgramDataAddress = findProgramAddressSync( - [secondProgramAddress.toBytes()], - new anchor.web3.PublicKey("BPFLoaderUpgradeab1e11111111111111111111111") - )[0]; - - const settings = anchor.web3.Keypair.generate(); - try { - await program.rpc.setAdminSettingsUseProgramState(new anchor.BN(500), { - accounts: { - authority: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - programData: secondProgramProgramDataAddress, - settings: settings.publicKey, - program: program.programId, - }, - signers: [settings], - }); - assert.ok(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2003); - } - }); -}); diff --git a/tests/bpf-upgradeable-state/tsconfig.json b/tests/bpf-upgradeable-state/tsconfig.json deleted file mode 100644 index cd5d2e3d..00000000 --- a/tests/bpf-upgradeable-state/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "types": ["mocha", "chai"], - "typeRoots": ["./node_modules/@types"], - "lib": ["es2015"], - "module": "commonjs", - "target": "es6", - "esModuleInterop": true - } -} diff --git a/tests/cashiers-check/Anchor.toml b/tests/cashiers-check/Anchor.toml deleted file mode 100644 index 5a733e1a..00000000 --- a/tests/cashiers-check/Anchor.toml +++ /dev/null @@ -1,11 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -cashiers_check = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[scripts] -test = "yarn run mocha -t 1000000 tests/" - -[features] diff --git a/tests/cashiers-check/Cargo.toml b/tests/cashiers-check/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/cashiers-check/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/cashiers-check/migrations/deploy.js b/tests/cashiers-check/migrations/deploy.js deleted file mode 100644 index 5e3df0dc..00000000 --- a/tests/cashiers-check/migrations/deploy.js +++ /dev/null @@ -1,12 +0,0 @@ -// Migrations are an early feature. Currently, they're nothing more than this -// single deploy script that's invoked from the CLI, injecting a provider -// configured from the workspace's Anchor.toml. - -const anchor = require("@project-serum/anchor"); - -module.exports = async function (provider) { - // Configure client to use the provider. - anchor.setProvider(provider); - - // Add your deploy script here. -}; diff --git a/tests/cashiers-check/package.json b/tests/cashiers-check/package.json deleted file mode 100644 index a539fd16..00000000 --- a/tests/cashiers-check/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "cashiers-check", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/cashiers-check/programs/cashiers-check/Cargo.toml b/tests/cashiers-check/programs/cashiers-check/Cargo.toml deleted file mode 100644 index 898ad639..00000000 --- a/tests/cashiers-check/programs/cashiers-check/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "cashiers-check" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "cashiers_check" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } -anchor-spl = { path = "../../../../spl" } diff --git a/tests/cashiers-check/programs/cashiers-check/Xargo.toml b/tests/cashiers-check/programs/cashiers-check/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/cashiers-check/programs/cashiers-check/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/cashiers-check/programs/cashiers-check/src/lib.rs b/tests/cashiers-check/programs/cashiers-check/src/lib.rs deleted file mode 100644 index 9cdae60d..00000000 --- a/tests/cashiers-check/programs/cashiers-check/src/lib.rs +++ /dev/null @@ -1,180 +0,0 @@ -//! A cashiers check example. The funds are immediately withdrawn from a user's -//! account and sent to a program controlled `Check` account, where the funds -//! reside until they are "cashed" by the intended recipient. The creator of -//! the check can cancel the check at any time to get back the funds. - -use anchor_lang::prelude::*; -use anchor_spl::token::{self, TokenAccount, Transfer}; -use std::convert::Into; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -pub mod cashiers_check { - use super::*; - - #[access_control(CreateCheck::accounts(&ctx, nonce))] - pub fn create_check( - ctx: Context, - amount: u64, - memo: Option, - nonce: u8, - ) -> Result<()> { - // Transfer funds to the check. - let cpi_accounts = Transfer { - from: ctx.accounts.from.to_account_info().clone(), - to: ctx.accounts.vault.to_account_info().clone(), - authority: ctx.accounts.owner.clone(), - }; - let cpi_program = ctx.accounts.token_program.clone(); - let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); - token::transfer(cpi_ctx, amount)?; - - // Print the check. - let check = &mut ctx.accounts.check; - check.amount = amount; - check.from = *ctx.accounts.from.to_account_info().key; - check.to = *ctx.accounts.to.to_account_info().key; - check.vault = *ctx.accounts.vault.to_account_info().key; - check.nonce = nonce; - check.memo = memo; - - Ok(()) - } - - #[access_control(not_burned(&ctx.accounts.check))] - pub fn cash_check(ctx: Context) -> Result<()> { - let seeds = &[ - ctx.accounts.check.to_account_info().key.as_ref(), - &[ctx.accounts.check.nonce], - ]; - let signer = &[&seeds[..]]; - let cpi_accounts = Transfer { - from: ctx.accounts.vault.to_account_info().clone(), - to: ctx.accounts.to.to_account_info().clone(), - authority: ctx.accounts.check_signer.clone(), - }; - let cpi_program = ctx.accounts.token_program.clone(); - let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); - token::transfer(cpi_ctx, ctx.accounts.check.amount)?; - // Burn the check for one time use. - ctx.accounts.check.burned = true; - Ok(()) - } - - #[access_control(not_burned(&ctx.accounts.check))] - pub fn cancel_check(ctx: Context) -> Result<()> { - let seeds = &[ - ctx.accounts.check.to_account_info().key.as_ref(), - &[ctx.accounts.check.nonce], - ]; - let signer = &[&seeds[..]]; - let cpi_accounts = Transfer { - from: ctx.accounts.vault.to_account_info().clone(), - to: ctx.accounts.from.to_account_info().clone(), - authority: ctx.accounts.check_signer.clone(), - }; - let cpi_program = ctx.accounts.token_program.clone(); - let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); - token::transfer(cpi_ctx, ctx.accounts.check.amount)?; - ctx.accounts.check.burned = true; - Ok(()) - } -} - -#[derive(Accounts)] -pub struct CreateCheck<'info> { - // Check being created. - #[account(zero)] - check: Account<'info, Check>, - // Check's token vault. - #[account(mut, constraint = &vault.owner == check_signer.key)] - vault: Account<'info, TokenAccount>, - // Program derived address for the check. - check_signer: AccountInfo<'info>, - // Token account the check is made from. - #[account(mut, has_one = owner)] - from: Account<'info, TokenAccount>, - // Token account the check is made to. - #[account(constraint = from.mint == to.mint)] - to: Account<'info, TokenAccount>, - // Owner of the `from` token account. - owner: AccountInfo<'info>, - token_program: AccountInfo<'info>, -} - -impl<'info> CreateCheck<'info> { - pub fn accounts(ctx: &Context, nonce: u8) -> Result<()> { - let signer = Pubkey::create_program_address( - &[ctx.accounts.check.to_account_info().key.as_ref(), &[nonce]], - ctx.program_id, - ) - .map_err(|_| error!(ErrorCode::InvalidCheckNonce))?; - if &signer != ctx.accounts.check_signer.to_account_info().key { - return err!(ErrorCode::InvalidCheckSigner); - } - Ok(()) - } -} - -#[derive(Accounts)] -pub struct CashCheck<'info> { - #[account(mut, has_one = vault, has_one = to)] - check: Account<'info, Check>, - #[account(mut)] - vault: AccountInfo<'info>, - #[account( - seeds = [check.to_account_info().key.as_ref()], - bump = check.nonce, - )] - check_signer: AccountInfo<'info>, - #[account(mut, has_one = owner)] - to: Account<'info, TokenAccount>, - owner: Signer<'info>, - token_program: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct CancelCheck<'info> { - #[account(mut, has_one = vault, has_one = from)] - check: Account<'info, Check>, - #[account(mut)] - vault: AccountInfo<'info>, - #[account( - seeds = [check.to_account_info().key.as_ref()], - bump = check.nonce, - )] - check_signer: AccountInfo<'info>, - #[account(mut, has_one = owner)] - from: Account<'info, TokenAccount>, - owner: Signer<'info>, - token_program: AccountInfo<'info>, -} - -#[account] -pub struct Check { - from: Pubkey, - to: Pubkey, - amount: u64, - memo: Option, - vault: Pubkey, - nonce: u8, - burned: bool, -} - -#[error_code] -pub enum ErrorCode { - #[msg("The given nonce does not create a valid program derived address.")] - InvalidCheckNonce, - #[msg("The derived check signer does not match that which was given.")] - InvalidCheckSigner, - #[msg("The given check has already been burned.")] - AlreadyBurned, -} - -fn not_burned(check: &Check) -> Result<()> { - if check.burned { - return err!(ErrorCode::AlreadyBurned); - } - Ok(()) -} diff --git a/tests/cashiers-check/tests/cashiers-check.js b/tests/cashiers-check/tests/cashiers-check.js deleted file mode 100644 index 8ddcebe3..00000000 --- a/tests/cashiers-check/tests/cashiers-check.js +++ /dev/null @@ -1,113 +0,0 @@ -const anchor = require("@project-serum/anchor"); -const serumCmn = require("@project-serum/common"); -const { assert } = require("chai"); -const { TOKEN_PROGRAM_ID } = require("@solana/spl-token"); - -describe("cashiers-check", () => { - // Configure the client to use the local cluster. - const provider = anchor.AnchorProvider.env(); - // hack so we don't have to update serum-common library - // to the new AnchorProvider class and Provider interface - provider.send = provider.sendAndConfirm; - anchor.setProvider(provider); - - const program = anchor.workspace.CashiersCheck; - - let mint = null; - let god = null; - let receiver = null; - - it("Sets up initial test state", async () => { - const [_mint, _god] = await serumCmn.createMintAndVault( - program.provider, - new anchor.BN(1000000) - ); - mint = _mint; - god = _god; - - receiver = await serumCmn.createTokenAccount( - program.provider, - mint, - program.provider.wallet.publicKey - ); - }); - - const check = anchor.web3.Keypair.generate(); - const vault = anchor.web3.Keypair.generate(); - - let checkSigner = null; - - it("Creates a check!", async () => { - let [_checkSigner, nonce] = await anchor.web3.PublicKey.findProgramAddress( - [check.publicKey.toBuffer()], - program.programId - ); - checkSigner = _checkSigner; - - await program.rpc.createCheck(new anchor.BN(100), "Hello world", nonce, { - accounts: { - check: check.publicKey, - vault: vault.publicKey, - checkSigner, - from: god, - to: receiver, - owner: program.provider.wallet.publicKey, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [check, vault], - instructions: [ - await program.account.check.createInstruction(check, 300), - ...(await serumCmn.createTokenAccountInstrs( - program.provider, - vault.publicKey, - mint, - checkSigner - )), - ], - }); - - const checkAccount = await program.account.check.fetch(check.publicKey); - assert.isTrue(checkAccount.from.equals(god)); - assert.isTrue(checkAccount.to.equals(receiver)); - assert.isTrue(checkAccount.amount.eq(new anchor.BN(100))); - assert.strictEqual(checkAccount.memo, "Hello world"); - assert.isTrue(checkAccount.vault.equals(vault.publicKey)); - assert.strictEqual(checkAccount.nonce, nonce); - assert.isFalse(checkAccount.burned); - - let vaultAccount = await serumCmn.getTokenAccount( - program.provider, - checkAccount.vault - ); - assert.isTrue(vaultAccount.amount.eq(new anchor.BN(100))); - }); - - it("Cashes a check", async () => { - await program.rpc.cashCheck({ - accounts: { - check: check.publicKey, - vault: vault.publicKey, - checkSigner: checkSigner, - to: receiver, - owner: program.provider.wallet.publicKey, - tokenProgram: TOKEN_PROGRAM_ID, - }, - }); - - const checkAccount = await program.account.check.fetch(check.publicKey); - assert.isTrue(checkAccount.burned); - - let vaultAccount = await serumCmn.getTokenAccount( - program.provider, - checkAccount.vault - ); - assert.isTrue(vaultAccount.amount.eq(new anchor.BN(0))); - - let receiverAccount = await serumCmn.getTokenAccount( - program.provider, - receiver - ); - assert.isTrue(receiverAccount.amount.eq(new anchor.BN(100))); - }); -}); diff --git a/tests/cfo/Anchor.toml b/tests/cfo/Anchor.toml deleted file mode 100644 index 903bbe80..00000000 --- a/tests/cfo/Anchor.toml +++ /dev/null @@ -1,45 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -cfo = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" -registry = { address = "GrAkKfEpTKQuVHG2Y97Y2FF4i7y7Q5AHLK94JBy7Y5yv", idl = "./deps/stake/target/idl/registry.json" } -lockup = { address = "6ebQNeTPZ1j7k3TtkCCtEPRvG7GQsucQrZ7sSEDQi9Ks", idl = "./deps/stake/target/idl/lockup.json" } - -[scripts] -# -# Testing. -# -test = "yarn run mocha -t 1000000 tests/" -test-with-build = "anchor run build && anchor test --skip-build --skip-lint" -# -# Build the program and all CPI dependencies. -# -build = "anchor run build-deps && anchor build --skip-lint" -build-deps = "anchor run build-dex && anchor run build-swap && anchor run build-stake" -build-dex = "pushd deps/serum-dex/dex/ && cargo build-bpf && popd" -build-swap = "cd deps/swap && pwd && anchor build --skip-lint && cd ../../" -build-stake = "pushd deps/stake && anchor build --skip-lint && popd" -# -# Runs a localnet with all the programs deployed. -# -localnet = "./scripts/localnet.sh" - -[[test.genesis]] -address = "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin" -program = "./deps/serum-dex/dex/target/deploy/serum_dex.so" - -[[test.genesis]] -address = "22Y43yTVxuUkoRKdm9thyRhQ3SdgQS7c7kB6UNCiaczD" -program = "./deps/swap/target/deploy/swap.so" - -[[test.genesis]] -address = "GrAkKfEpTKQuVHG2Y97Y2FF4i7y7Q5AHLK94JBy7Y5yv" -program = "./deps/stake/target/deploy/registry.so" - -[[test.genesis]] -address = "6ebQNeTPZ1j7k3TtkCCtEPRvG7GQsucQrZ7sSEDQi9Ks" -program = "./deps/stake/target/deploy/lockup.so" - -[features] diff --git a/tests/cfo/Cargo.toml b/tests/cfo/Cargo.toml deleted file mode 100644 index 5f959cf9..00000000 --- a/tests/cfo/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[workspace] -members = [ - "programs/*" -] -exclude = [ - "deps/serum-dex", - "deps/stake", - "deps/swap" -] diff --git a/tests/cfo/deps/serum-dex b/tests/cfo/deps/serum-dex deleted file mode 160000 index ed9d54a7..00000000 --- a/tests/cfo/deps/serum-dex +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ed9d54a717bec01de2924f6e6ca465f942b072aa diff --git a/tests/cfo/deps/stake b/tests/cfo/deps/stake deleted file mode 160000 index b68b9a6f..00000000 --- a/tests/cfo/deps/stake +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b68b9a6fdea2c8befe95aa0f1fcca394579fc3bd diff --git a/tests/cfo/deps/swap b/tests/cfo/deps/swap deleted file mode 160000 index 2ac6045c..00000000 --- a/tests/cfo/deps/swap +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2ac6045cf37436c4702fbe12678f3a6243feecb1 diff --git a/tests/cfo/migrations/deploy.js b/tests/cfo/migrations/deploy.js deleted file mode 100644 index 5e3df0dc..00000000 --- a/tests/cfo/migrations/deploy.js +++ /dev/null @@ -1,12 +0,0 @@ -// Migrations are an early feature. Currently, they're nothing more than this -// single deploy script that's invoked from the CLI, injecting a provider -// configured from the workspace's Anchor.toml. - -const anchor = require("@project-serum/anchor"); - -module.exports = async function (provider) { - // Configure client to use the provider. - anchor.setProvider(provider); - - // Add your deploy script here. -}; diff --git a/tests/cfo/package.json b/tests/cfo/package.json deleted file mode 100644 index edfcc080..00000000 --- a/tests/cfo/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "cfo", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor run test-with-build" - } -} diff --git a/tests/cfo/programs/cfo/Cargo.toml b/tests/cfo/programs/cfo/Cargo.toml deleted file mode 100644 index 9f38e609..00000000 --- a/tests/cfo/programs/cfo/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "cfo" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "cfo" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = ["test"] -test = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } -anchor-spl = { path = "../../../../spl" } -spl-token = { version = "3.1.1", features = ["no-entrypoint"] } -swap = { path = "../../deps/swap/programs/swap", features = ["cpi"] } -serum_dex = { path = "../../deps/serum-dex/dex", features = ["no-entrypoint"] } -registry = { path = "../../deps/stake/programs/registry", features = ["cpi"] } -lockup = { path = "../../deps/stake/programs/lockup", features = ["cpi"] } diff --git a/tests/cfo/programs/cfo/Xargo.toml b/tests/cfo/programs/cfo/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/cfo/programs/cfo/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/cfo/programs/cfo/src/lib.rs b/tests/cfo/programs/cfo/src/lib.rs deleted file mode 100644 index f9232eef..00000000 --- a/tests/cfo/programs/cfo/src/lib.rs +++ /dev/null @@ -1,995 +0,0 @@ -// WIP. This program has been checkpointed and is not production ready. - -use anchor_lang::prelude::*; -use anchor_lang::solana_program::sysvar::instructions as tx_instructions; -use anchor_spl::dex::{self, Dex}; -use anchor_spl::token::{self, Mint, Token, TokenAccount}; -use lockup::program::Lockup; -use registry::program::Registry; -use registry::{Registrar, RewardVendorKind}; -use serum_dex::state::OpenOrders; -use std::convert::TryInto; -use std::mem::size_of; -use swap::program::Swap; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -/// CFO is the program representing the Serum chief financial officer. It is -/// the program responsible for collecting and distributing fees from the Serum -/// DEX. -#[program] -pub mod cfo { - use super::*; - - /// Creates a financial officer account associated with a DEX program ID. - #[access_control(is_distribution_valid(&d))] - pub fn create_officer( - ctx: Context, - bumps: OfficerBumps, - d: Distribution, - registrar: Pubkey, - msrm_registrar: Pubkey, - ) -> Result<()> { - let officer = &mut ctx.accounts.officer; - officer.authority = *ctx.accounts.authority.key; - officer.swap_program = *ctx.accounts.swap_program.key; - officer.dex_program = ctx.accounts.dex_program.key(); - officer.distribution = d; - officer.registrar = registrar; - officer.msrm_registrar = msrm_registrar; - officer.stake = *ctx.accounts.stake.to_account_info().key; - officer.treasury = *ctx.accounts.treasury.to_account_info().key; - officer.srm_vault = *ctx.accounts.srm_vault.to_account_info().key; - officer.bumps = bumps; - emit!(OfficerDidCreate { - pubkey: *officer.to_account_info().key, - }); - Ok(()) - } - - /// Creates a market authorization token. - pub fn authorize_market(ctx: Context, bump: u8) -> Result<()> { - ctx.accounts.market_auth.bump = bump; - Ok(()) - } - - /// Revokes a market authorization token. - pub fn revoke_market(_ctx: Context) -> Result<()> { - Ok(()) - } - - /// Creates a deterministic token account owned by the CFO. - /// This should be used when a new mint is used for collecting fees. - /// Can only be called once per token CFO and token mint. - pub fn create_officer_token(_ctx: Context, _bump: u8) -> Result<()> { - Ok(()) - } - - /// Creates an open orders account for the given market. - pub fn create_officer_open_orders( - ctx: Context, - _bump: u8, - ) -> Result<()> { - let seeds = [ - ctx.accounts.dex_program.key.as_ref(), - &[ctx.accounts.officer.bumps.bump], - ]; - let cpi_ctx = CpiContext::from(&*ctx.accounts); - dex::init_open_orders(cpi_ctx.with_signer(&[&seeds])).map_err(Into::into) - } - - /// Updates the cfo's fee distribution. - #[access_control(is_distribution_valid(&d))] - pub fn set_distribution(ctx: Context, d: Distribution) -> Result<()> { - ctx.accounts.officer.distribution = d.clone(); - emit!(DistributionDidChange { distribution: d }); - Ok(()) - } - - /// Transfers fees from the dex to the CFO. - pub fn sweep_fees<'info>(ctx: Context<'_, '_, '_, 'info, SweepFees<'info>>) -> Result<()> { - let cpi_ctx = CpiContext::from(&*ctx.accounts); - let seeds = [ - ctx.accounts.dex.dex_program.key.as_ref(), - &[ctx.accounts.officer.bumps.bump], - ]; - dex::sweep_fees(cpi_ctx.with_signer(&[&seeds])).map_err(Into::into) - } - - /// Convert the CFO's entire non-SRM token balance into USDC. - /// Assumes USDC is the quote currency. - #[access_control(is_not_trading(&ctx.accounts.instructions))] - pub fn swap_to_usdc<'info>( - ctx: Context<'_, '_, '_, 'info, SwapToUsdc<'info>>, - min_exchange_rate: ExchangeRate, - ) -> Result<()> { - let seeds = [ - ctx.accounts.dex_program.key.as_ref(), - &[ctx.accounts.officer.bumps.bump], - ]; - let cpi_ctx = CpiContext::from(&*ctx.accounts); - swap::cpi::swap( - cpi_ctx.with_signer(&[&seeds]), - swap::Side::Ask, - ctx.accounts.from_vault.amount, - min_exchange_rate.into(), - ) - .map_err(Into::into) - } - - /// Convert the CFO's entire token balance into SRM. - /// Assumes SRM is the base currency. - #[access_control(is_not_trading(&ctx.accounts.instructions))] - pub fn swap_to_srm<'info>( - ctx: Context<'_, '_, '_, 'info, SwapToSrm<'info>>, - min_exchange_rate: ExchangeRate, - ) -> Result<()> { - let seeds = [ - ctx.accounts.dex_program.key.as_ref(), - &[ctx.accounts.officer.bumps.bump], - ]; - let cpi_ctx = CpiContext::from(&*ctx.accounts); - swap::cpi::swap( - cpi_ctx.with_signer(&[&seeds]), - swap::Side::Bid, - ctx.accounts.usdc_vault.amount, - min_exchange_rate.into(), - ) - .map_err(Into::into) - } - - /// Distributes srm tokens to the various categories. Before calling this, - /// one must convert the fees into SRM via the swap APIs. - #[access_control(is_distribution_ready(&ctx.accounts))] - pub fn distribute<'info>(ctx: Context<'_, '_, '_, 'info, Distribute<'info>>) -> Result<()> { - let total_fees = ctx.accounts.srm_vault.amount; - let seeds = [ - ctx.accounts.dex_program.key.as_ref(), - &[ctx.accounts.officer.bumps.bump], - ]; - - // Burn. - let burn_amount: u64 = u128::from(total_fees) - .checked_mul(ctx.accounts.officer.distribution.burn.into()) - .unwrap() - .checked_div(100) - .unwrap() - .try_into() - .map_err(|_| error!(ErrorCode::U128CannotConvert))?; - token::burn(ctx.accounts.into_burn().with_signer(&[&seeds]), burn_amount)?; - - // Stake. - let stake_amount: u64 = u128::from(total_fees) - .checked_mul(ctx.accounts.officer.distribution.stake.into()) - .unwrap() - .checked_div(100) - .unwrap() - .try_into() - .map_err(|_| error!(ErrorCode::U128CannotConvert))?; - token::transfer( - ctx.accounts.into_stake_transfer().with_signer(&[&seeds]), - stake_amount, - )?; - - // Treasury. - let treasury_amount: u64 = u128::from(total_fees) - .checked_mul(ctx.accounts.officer.distribution.treasury.into()) - .unwrap() - .checked_div(100) - .unwrap() - .try_into() - .map_err(|_| error!(ErrorCode::U128CannotConvert))?; - token::transfer( - ctx.accounts.into_treasury_transfer().with_signer(&[&seeds]), - treasury_amount, - )?; - - Ok(()) - } - - #[access_control(is_stake_reward_ready(&ctx.accounts))] - pub fn drop_stake_reward<'info>( - ctx: Context<'_, '_, '_, 'info, DropStakeReward<'info>>, - ) -> Result<()> { - // Common reward parameters. - let expiry_ts = 1853942400; // 9/30/2028. - let expiry_receiver = *ctx.accounts.officer.to_account_info().key; - let locked_kind = { - let start_ts = 1633017600; // 9/30.24.2. - let end_ts = 1822320000; // 9/30/2027. - let period_count = 2191; - RewardVendorKind::Locked { - start_ts, - end_ts, - period_count, - } - }; - let seeds = [ - ctx.accounts.dex_program.key.as_ref(), - &[ctx.accounts.officer.bumps.bump], - ]; - - // Total amount staked denominated in SRM (i.e. MSRM is converted to - // SRM) - let total_pool_value = u128::from(ctx.accounts.srm.pool_mint.supply) - .checked_mul(500) - .unwrap() - .checked_add( - u128::from(ctx.accounts.msrm.pool_mint.supply) - .checked_mul(1_000_000) - .unwrap(), - ) - .unwrap(); - - // Total reward split between both the SRM and MSRM stake pools. - let total_reward_amount = u128::from(ctx.accounts.stake.amount); - - // Proportion of the reward going to the srm pool. - // - // total_reward_amount * (srm_pool_value / total_pool_value) - // - let srm_amount: u64 = u128::from(ctx.accounts.srm.pool_mint.supply) - .checked_mul(500) - .unwrap() - .checked_mul(total_reward_amount) - .unwrap() - .checked_div(total_pool_value) - .unwrap() - .try_into() - .map_err(|_| error!(ErrorCode::U128CannotConvert))?; - - // Proportion of the reward going to the msrm pool. - // - // total_reward_amount * (msrm_pool_value / total_pool_value) - // - let msrm_amount = u128::from(ctx.accounts.msrm.pool_mint.supply) - .checked_mul(total_reward_amount) - .unwrap() - .checked_div(total_pool_value) - .unwrap() - .try_into() - .map_err(|_| error!(ErrorCode::U128CannotConvert))?; - - // SRM drop. - { - // Drop locked reward. - let (_, nonce) = Pubkey::find_program_address( - &[ - ctx.accounts.srm.registrar.to_account_info().key.as_ref(), - ctx.accounts.srm.vendor.to_account_info().key.as_ref(), - ], - ctx.accounts.token_program.key, - ); - registry::cpi::drop_reward( - ctx.accounts.into_srm_reward().with_signer(&[&seeds[..]]), - locked_kind.clone(), - srm_amount.try_into().unwrap(), - expiry_ts, - expiry_receiver, - nonce, - )?; - - // Drop unlocked reward. - registry::cpi::drop_reward( - ctx.accounts.into_srm_reward().with_signer(&[&seeds[..]]), - RewardVendorKind::Unlocked, - srm_amount, - expiry_ts, - expiry_receiver, - nonce, - )?; - } - - // MSRM drop. - { - // Drop locked reward. - let (_, nonce) = Pubkey::find_program_address( - &[ - ctx.accounts.msrm.registrar.to_account_info().key.as_ref(), - ctx.accounts.msrm.vendor.to_account_info().key.as_ref(), - ], - ctx.accounts.token_program.key, - ); - registry::cpi::drop_reward( - ctx.accounts.into_msrm_reward().with_signer(&[&seeds[..]]), - locked_kind, - msrm_amount, - expiry_ts, - expiry_receiver, - nonce, - )?; - - // Drop unlocked reward. - registry::cpi::drop_reward( - ctx.accounts.into_msrm_reward().with_signer(&[&seeds[..]]), - RewardVendorKind::Unlocked, - msrm_amount, - expiry_ts, - expiry_receiver, - nonce, - )?; - } - - Ok(()) - } -} - -// Context accounts. - -#[derive(Accounts)] -#[instruction(bumps: OfficerBumps)] -pub struct CreateOfficer<'info> { - #[account( - init, - seeds = [dex_program.key.as_ref()], - bump, - payer = authority, - space = Officer::LEN + 8 - )] - officer: Box>, - #[account( - init, - seeds = [b"token", officer.key().as_ref(), srm_mint.key().as_ref()], - bump, - payer = authority, - token::mint = srm_mint, - token::authority = officer - )] - srm_vault: Box>, - #[account( - init, - seeds = [b"token", officer.key().as_ref(), usdc_mint.key().as_ref()], - bump, - payer = authority, - token::mint = usdc_mint, - token::authority = officer - )] - usdc_vault: Box>, - #[account( - init, - seeds = [b"stake", officer.key().as_ref()], - bump, - payer = authority, - token::mint = srm_mint, - token::authority = officer - )] - stake: Box>, - #[account( - init, - seeds = [b"treasury", officer.key().as_ref()], - bump, - payer = authority, - token::mint = srm_mint, - token::authority = officer - )] - treasury: Box>, - #[account(mut)] - authority: Signer<'info>, - #[cfg_attr( - not(feature = "test"), - account(address = mint::SRM), - )] - srm_mint: Box>, - #[cfg_attr( - not(feature = "test"), - account(address = mint::USDC), - )] - usdc_mint: Box>, - dex_program: Program<'info, Dex>, - swap_program: Program<'info, Swap>, - system_program: Program<'info, System>, - token_program: Program<'info, Token>, - rent: Sysvar<'info, Rent>, -} - -#[derive(Accounts)] -#[instruction(bump: u8)] -pub struct AuthorizeMarket<'info> { - #[account(has_one = authority)] - officer: Account<'info, Officer>, - authority: Signer<'info>, - #[account( - init, - payer = payer, - seeds = [b"market-auth", officer.key().as_ref(), market.key.as_ref()], - bump, - space = MarketAuth::LEN + 8 - )] - market_auth: Account<'info, MarketAuth>, - #[account(mut)] - payer: Signer<'info>, - // Not read or written to so not validated. - market: UncheckedAccount<'info>, - system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct RevokeMarket<'info> { - #[account(has_one = authority)] - pub officer: Account<'info, Officer>, - pub authority: Signer<'info>, - #[account(mut, close = payer)] - pub auth: Account<'info, MarketAuth>, - pub payer: Signer<'info>, -} - -#[derive(Accounts)] -#[instruction(bump: u8)] -pub struct CreateOfficerToken<'info> { - officer: Account<'info, Officer>, - #[account( - init, - seeds = [b"token", officer.key().as_ref(), mint.key().as_ref()], - bump, - token::mint = mint, - token::authority = officer, - payer = payer - )] - token: Account<'info, TokenAccount>, - mint: Account<'info, Mint>, - #[account(mut)] - payer: Signer<'info>, - system_program: Program<'info, System>, - token_program: Program<'info, Token>, - rent: Sysvar<'info, Rent>, -} - -#[derive(Accounts)] -#[instruction(bump: u8)] -pub struct CreateOfficerOpenOrders<'info> { - officer: Account<'info, Officer>, - #[account( - init, - seeds = [b"open-orders", officer.key().as_ref(), market.key.as_ref()], - bump, - space = 12 + size_of::(), - payer = payer, - owner = dex::ID, - )] - open_orders: UncheckedAccount<'info>, - #[account(mut)] - payer: Signer<'info>, - dex_program: Program<'info, Dex>, - system_program: Program<'info, System>, - rent: Sysvar<'info, Rent>, - // Used for CPI. Not read or written so not validated. - market: UncheckedAccount<'info>, -} - -#[derive(Accounts)] -pub struct SetDistribution<'info> { - #[account(has_one = authority)] - officer: Account<'info, Officer>, - authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct SweepFees<'info> { - #[account( - seeds = [dex.dex_program.key.as_ref()], - bump = officer.bumps.bump, - )] - officer: Account<'info, Officer>, - #[account( - mut, - seeds = [b"token", officer.key().as_ref(), mint.key().as_ref()], - bump, - )] - sweep_vault: Account<'info, TokenAccount>, - mint: Account<'info, Mint>, - dex: DexAccounts<'info>, -} - -// DexAccounts are safe because they are used for CPI only. -// They are not read or written and so are not validated. -#[derive(Accounts)] -pub struct DexAccounts<'info> { - #[account(mut)] - market: UncheckedAccount<'info>, - #[account(mut)] - pc_vault: UncheckedAccount<'info>, - sweep_authority: UncheckedAccount<'info>, - vault_signer: UncheckedAccount<'info>, - dex_program: Program<'info, Dex>, - token_program: Program<'info, Token>, -} - -#[derive(Accounts)] -pub struct SwapToUsdc<'info> { - #[account( - seeds = [dex_program.key.as_ref()], - bump = officer.bumps.bump, - )] - officer: Box>, - market: DexMarketAccounts<'info>, - #[account( - seeds = [b"market-auth", officer.key().as_ref(), market.market.key.as_ref()], - bump = market_auth.bump, - )] - market_auth: Account<'info, MarketAuth>, - #[account( - mut, - constraint = &officer.treasury != &from_vault.key(), - constraint = &officer.stake != &from_vault.key(), - )] - from_vault: Box>, - #[account( - mut, - seeds = [b"token", officer.key().as_ref(), usdc_mint.key().as_ref()], - bump, - )] - usdc_vault: Box>, - #[cfg_attr(not(feature = "test"), account(address = mint::USDC))] - usdc_mint: Box>, - swap_program: Program<'info, Swap>, - dex_program: Program<'info, Dex>, - token_program: Program<'info, Token>, - #[account(address = tx_instructions::ID)] - instructions: UncheckedAccount<'info>, - rent: Sysvar<'info, Rent>, -} - -#[derive(Accounts)] -#[instruction(bump: u8)] -pub struct SwapToSrm<'info> { - #[account( - seeds = [dex_program.key.as_ref()], - bump = officer.bumps.bump, - )] - officer: Box>, - market: DexMarketAccounts<'info>, - #[account( - seeds = [b"market-auth", officer.key().as_ref(), market.market.key.as_ref()], - bump = market_auth.bump, - )] - market_auth: Account<'info, MarketAuth>, - #[account( - mut, - seeds = [b"token", officer.key().as_ref(), usdc_mint.key().as_ref()], - bump, - )] - usdc_vault: Box>, - #[account( - mut, - seeds = [b"token", officer.key().as_ref(), srm_mint.key().as_ref()], - bump, - )] - srm_vault: Box>, - #[cfg_attr(not(feature = "test"), account(address = mint::SRM))] - srm_mint: Box>, - #[cfg_attr(not(feature = "test"), account(address = mint::USDC))] - usdc_mint: Box>, - swap_program: Program<'info, Swap>, - dex_program: Program<'info, Dex>, - token_program: Program<'info, Token>, - #[account(address = tx_instructions::ID)] - instructions: UncheckedAccount<'info>, - rent: Sysvar<'info, Rent>, -} - -// Dex accounts are used for CPI only. -// They are not read or written and so are not validated. -#[derive(Accounts)] -pub struct DexMarketAccounts<'info> { - #[account(mut)] - market: UncheckedAccount<'info>, - #[account(mut)] - open_orders: UncheckedAccount<'info>, - #[account(mut)] - request_queue: UncheckedAccount<'info>, - #[account(mut)] - event_queue: UncheckedAccount<'info>, - #[account(mut)] - bids: UncheckedAccount<'info>, - #[account(mut)] - asks: UncheckedAccount<'info>, - // The `spl_token::Account` that funds will be taken from, i.e., transferred - // from the user into the market's vault. - // - // For bids, this is the base currency. For asks, the quote. - #[account(mut)] - order_payer_token_account: UncheckedAccount<'info>, - // Also known as the "base" currency. For a given A/B market, - // this is the vault for the A mint. - #[account(mut)] - coin_vault: UncheckedAccount<'info>, - // Also known as the "quote" currency. For a given A/B market, - // this is the vault for the B mint. - #[account(mut)] - pc_vault: UncheckedAccount<'info>, - // PDA owner of the DEX's token accounts for base + quote currencies. - vault_signer: UncheckedAccount<'info>, -} - -#[derive(Accounts)] -pub struct Distribute<'info> { - #[account( - has_one = srm_vault, - has_one = treasury, - has_one = stake, - )] - officer: Box>, - #[account(mut)] - treasury: Box>, - #[account(mut)] - stake: Account<'info, TokenAccount>, - #[account(mut)] - srm_vault: Account<'info, TokenAccount>, - #[account(mut)] - srm_mint: Account<'info, Mint>, - token_program: Program<'info, Token>, - dex_program: Program<'info, Dex>, -} - -#[derive(Accounts)] -pub struct DropStakeReward<'info> { - #[account( - has_one = stake, - constraint = srm.registrar.key == &officer.registrar, - constraint = msrm.registrar.key == &officer.msrm_registrar, - )] - officer: Box>, - #[account( - seeds = [b"stake", officer.key().as_ref()], - bump = officer.bumps.stake, - )] - stake: Box>, - #[cfg_attr( - not(feature = "test"), - account(address = mint::SRM), - )] - mint: UncheckedAccount<'info>, - srm: DropStakeRewardPool<'info>, - msrm: DropStakeRewardPool<'info>, - msrm_registrar: Box>, - token_program: Program<'info, Token>, - registry_program: Program<'info, Registry>, - lockup_program: Program<'info, Lockup>, - dex_program: Program<'info, Dex>, - clock: Sysvar<'info, Clock>, - rent: Sysvar<'info, Rent>, -} - -// Don't bother doing validation on the individual accounts. Allow the stake -// program to handle it. -#[derive(Accounts)] -pub struct DropStakeRewardPool<'info> { - registrar: UncheckedAccount<'info>, - reward_event_q: UncheckedAccount<'info>, - pool_mint: Account<'info, Mint>, - vendor: UncheckedAccount<'info>, - vendor_vault: UncheckedAccount<'info>, -} - -// Accounts. - -/// Officer represents a deployed instance of the CFO mechanism. It is tied -/// to a single deployment of the dex program. -/// -/// PDA - [dex_program_id]. -#[account] -pub struct Officer { - // Priviledged account. - pub authority: Pubkey, // 32 - // Vault holding the officer's SRM tokens prior to distribution. - pub srm_vault: Pubkey, // 32 - // Escrow SRM vault holding tokens which are dropped onto stakers. - pub stake: Pubkey, // 32 - // SRM token account to send treasury earned tokens to. - pub treasury: Pubkey, // 32 - // Defines the fee distribution, i.e., what percent each fee category gets. - pub distribution: Distribution, // Distribution::LEN - // Swap frontend for the dex. - pub swap_program: Pubkey, // 32 - // Dex program the officer is associated with. - pub dex_program: Pubkey, // 32 - // SRM stake pool address - pub registrar: Pubkey, // 32 - // MSRM stake pool address. - pub msrm_registrar: Pubkey, // 32 - // Bump seeds for pdas. - pub bumps: OfficerBumps, // OfficerBumps::LEN -} - -impl Officer { - pub const LEN: usize = 8 * 32 + Distribution::LEN + OfficerBumps::LEN; -} - -/// MarketAuth represents an authorization token created by the Officer -/// authority. This is used as an authorization token which allows one to -/// permissionlessly invoke the swap instructions on the market. Without this -/// one would be able to create their own market with prices unfavorable -/// to the smart contract (and subsequently swap on it). -/// -/// Because a PDA is used here, the account existing (without a tombstone) is -/// proof of the validity of a given market, which means that anyone can use -/// the vault here to swap. -/// -/// PDA - [b"market-auth", officer, market_address] -#[account] -pub struct MarketAuth { - // Bump seed for this account's PDA. - pub bump: u8, // 1 -} - -impl MarketAuth { - pub const LEN: usize = 1; -} - -#[derive(AnchorSerialize, AnchorDeserialize, Clone, Default)] -pub struct OfficerBumps { - pub bump: u8, // 1 - pub srm: u8, // 1 - pub usdc: u8, // 1 - pub stake: u8, // 1 - pub treasury: u8, // 1 -} - -impl OfficerBumps { - pub const LEN: usize = 5; -} - -#[derive(AnchorSerialize, AnchorDeserialize, Default, Clone)] -pub struct Distribution { - burn: u8, // 1 - stake: u8, // 1 - treasury: u8, // 1 -} - -impl Distribution { - pub const LEN: usize = 3; -} - -// CpiContext transformations. - -impl<'info> From<&CreateOfficerOpenOrders<'info>> - for CpiContext<'_, '_, '_, 'info, dex::InitOpenOrders<'info>> -{ - fn from(accs: &CreateOfficerOpenOrders<'info>) -> Self { - let program = accs.dex_program.to_account_info(); - let accounts = dex::InitOpenOrders { - open_orders: accs.open_orders.to_account_info(), - authority: accs.officer.to_account_info(), - market: accs.market.to_account_info(), - rent: accs.rent.to_account_info(), - }; - CpiContext::new(program, accounts) - } -} - -impl<'info> From<&SweepFees<'info>> for CpiContext<'_, '_, '_, 'info, dex::SweepFees<'info>> { - fn from(sweep: &SweepFees<'info>) -> Self { - let program = sweep.dex.dex_program.to_account_info(); - let accounts = dex::SweepFees { - market: sweep.dex.market.to_account_info(), - pc_vault: sweep.dex.pc_vault.to_account_info(), - sweep_authority: sweep.dex.sweep_authority.to_account_info(), - sweep_receiver: sweep.sweep_vault.to_account_info(), - vault_signer: sweep.dex.vault_signer.to_account_info(), - token_program: sweep.dex.token_program.to_account_info(), - }; - CpiContext::new(program.to_account_info(), accounts) - } -} - -impl<'info> From<&SwapToSrm<'info>> - for CpiContext<'_, '_, '_, 'info, swap::cpi::accounts::Swap<'info>> -{ - fn from(accs: &SwapToSrm<'info>) -> Self { - let program = accs.swap_program.to_account_info(); - let accounts = swap::cpi::accounts::Swap { - market: swap::cpi::accounts::MarketAccounts { - market: accs.market.market.to_account_info(), - open_orders: accs.market.open_orders.to_account_info(), - request_queue: accs.market.request_queue.to_account_info(), - event_queue: accs.market.event_queue.to_account_info(), - bids: accs.market.bids.to_account_info(), - asks: accs.market.asks.to_account_info(), - order_payer_token_account: accs.market.order_payer_token_account.to_account_info(), - coin_vault: accs.market.coin_vault.to_account_info(), - pc_vault: accs.market.pc_vault.to_account_info(), - vault_signer: accs.market.vault_signer.to_account_info(), - coin_wallet: accs.srm_vault.to_account_info(), - }, - authority: accs.officer.to_account_info(), - pc_wallet: accs.usdc_vault.to_account_info(), - dex_program: accs.dex_program.to_account_info(), - token_program: accs.token_program.to_account_info(), - rent: accs.rent.to_account_info(), - }; - CpiContext::new(program.to_account_info(), accounts) - } -} - -impl<'info> From<&SwapToUsdc<'info>> - for CpiContext<'_, '_, '_, 'info, swap::cpi::accounts::Swap<'info>> -{ - fn from(accs: &SwapToUsdc<'info>) -> Self { - let program = accs.swap_program.to_account_info(); - let accounts = swap::cpi::accounts::Swap { - market: swap::cpi::accounts::MarketAccounts { - market: accs.market.market.to_account_info(), - open_orders: accs.market.open_orders.to_account_info(), - request_queue: accs.market.request_queue.to_account_info(), - event_queue: accs.market.event_queue.to_account_info(), - bids: accs.market.bids.to_account_info(), - asks: accs.market.asks.to_account_info(), - order_payer_token_account: accs.market.order_payer_token_account.to_account_info(), - coin_vault: accs.market.coin_vault.to_account_info(), - pc_vault: accs.market.pc_vault.to_account_info(), - vault_signer: accs.market.vault_signer.to_account_info(), - coin_wallet: accs.from_vault.to_account_info(), - }, - authority: accs.officer.to_account_info(), - pc_wallet: accs.usdc_vault.to_account_info(), - dex_program: accs.dex_program.to_account_info(), - token_program: accs.token_program.to_account_info(), - rent: accs.rent.to_account_info(), - }; - CpiContext::new(program.to_account_info(), accounts) - } -} - -impl<'info> Distribute<'info> { - fn into_burn(&self) -> CpiContext<'_, '_, '_, 'info, token::Burn<'info>> { - let program = self.token_program.to_account_info(); - let accounts = token::Burn { - mint: self.srm_mint.to_account_info(), - from: self.srm_vault.to_account_info(), - authority: self.officer.to_account_info(), - }; - CpiContext::new(program, accounts) - } - - fn into_stake_transfer(&self) -> CpiContext<'_, '_, '_, 'info, token::Transfer<'info>> { - let program = self.token_program.to_account_info(); - let accounts = token::Transfer { - from: self.srm_vault.to_account_info(), - to: self.stake.to_account_info(), - authority: self.officer.to_account_info(), - }; - CpiContext::new(program, accounts) - } - - fn into_treasury_transfer(&self) -> CpiContext<'_, '_, '_, 'info, token::Transfer<'info>> { - let program = self.token_program.to_account_info(); - let accounts = token::Transfer { - from: self.srm_vault.to_account_info(), - to: self.treasury.to_account_info(), - authority: self.officer.to_account_info(), - }; - CpiContext::new(program, accounts) - } -} - -impl<'info> DropStakeReward<'info> { - fn into_srm_reward( - &self, - ) -> CpiContext<'_, '_, '_, 'info, registry::cpi::accounts::DropReward<'info>> { - let program = self.registry_program.clone(); - let accounts = registry::cpi::accounts::DropReward { - registrar: self.srm.registrar.to_account_info(), - reward_event_q: self.srm.reward_event_q.to_account_info(), - pool_mint: self.srm.pool_mint.to_account_info(), - vendor: self.srm.vendor.to_account_info(), - vendor_vault: self.srm.vendor_vault.to_account_info(), - depositor: self.stake.to_account_info(), - depositor_authority: self.officer.to_account_info(), - token_program: self.token_program.to_account_info(), - clock: self.clock.to_account_info(), - rent: self.rent.to_account_info(), - }; - CpiContext::new(program.to_account_info(), accounts) - } - - fn into_msrm_reward( - &self, - ) -> CpiContext<'_, '_, '_, 'info, registry::cpi::accounts::DropReward<'info>> { - let program = self.registry_program.clone(); - let accounts = registry::cpi::accounts::DropReward { - registrar: self.msrm.registrar.to_account_info(), - reward_event_q: self.msrm.reward_event_q.to_account_info(), - pool_mint: self.msrm.pool_mint.to_account_info(), - vendor: self.msrm.vendor.to_account_info(), - vendor_vault: self.msrm.vendor_vault.to_account_info(), - depositor: self.stake.to_account_info(), - depositor_authority: self.officer.to_account_info(), - token_program: self.token_program.to_account_info(), - clock: self.clock.to_account_info(), - rent: self.rent.to_account_info(), - }; - CpiContext::new(program.to_account_info(), accounts) - } -} - -// Events. - -#[event] -pub struct DistributionDidChange { - distribution: Distribution, -} - -#[event] -pub struct OfficerDidCreate { - pubkey: Pubkey, -} - -// Error. - -#[error_code] -pub enum ErrorCode { - #[msg("Distribution does not add to 100")] - InvalidDistribution, - #[msg("u128 cannot be converted into u64")] - U128CannotConvert, - #[msg("Only one instruction is allowed for this transaction")] - TooManyInstructions, - #[msg("Not enough SRM has been accumulated to distribute")] - InsufficientDistributionAmount, - #[msg("Must drop more SRM onto the stake pool")] - InsufficientStakeReward, -} - -// Access control. - -fn is_distribution_valid(d: &Distribution) -> Result<()> { - if d.burn + d.stake + d.treasury != 100 { - return err!(ErrorCode::InvalidDistribution); - } - Ok(()) -} - -fn is_distribution_ready(accounts: &Distribute) -> Result<()> { - if accounts.srm_vault.amount < 1_000_000 { - return err!(ErrorCode::InsufficientDistributionAmount); - } - Ok(()) -} - -// `ixs` must be the Instructions sysvar. -fn is_not_trading(ixs: &UncheckedAccount) -> Result<()> { - let data = ixs.try_borrow_data()?; - match tx_instructions::load_instruction_at(1, &data) { - Ok(_) => err!(ErrorCode::TooManyInstructions), - Err(_) => Ok(()), - } -} - -fn is_stake_reward_ready(accounts: &DropStakeReward) -> Result<()> { - // Min drop is 15,0000 SRM. - let min_reward: u64 = 15_000_000_000; - if accounts.stake.amount < min_reward { - return err!(ErrorCode::InsufficientStakeReward); - } - Ok(()) -} - -// Redefintions. -// -// The following types are redefined so that they can be parsed into the IDL, -// since Anchor doesn't yet support idl parsing across multiple crates. - -#[derive(AnchorSerialize, AnchorDeserialize)] -pub struct ExchangeRate { - rate: u64, - from_decimals: u8, - quote_decimals: u8, - strict: bool, -} - -impl From for swap::ExchangeRate { - fn from(e: ExchangeRate) -> Self { - let ExchangeRate { - rate, - from_decimals, - quote_decimals, - strict, - } = e; - Self { - rate, - from_decimals, - quote_decimals, - strict, - } - } -} diff --git a/tests/cfo/scripts/common.sh b/tests/cfo/scripts/common.sh deleted file mode 100644 index c8cd18c9..00000000 --- a/tests/cfo/scripts/common.sh +++ /dev/null @@ -1,19 +0,0 @@ -cleanup() { - pkill -P $$ || true - wait || true -} - -trap_add() { - trap_add_cmd=$1; shift || fatal "${FUNCNAME} usage error" - for trap_add_name in "$@"; do - trap -- "$( - extract_trap_cmd() { printf '%s\n' "${3:-}"; } - eval "extract_trap_cmd $(trap -p "${trap_add_name}")" - printf '%s\n' "${trap_add_cmd}" - )" "${trap_add_name}" \ - || fatal "unable to add to trap ${trap_add_name}" - done -} - -declare -f -t trap_add -trap_add 'cleanup' EXIT diff --git a/tests/cfo/scripts/fees.js b/tests/cfo/scripts/fees.js deleted file mode 100755 index 742877be..00000000 --- a/tests/cfo/scripts/fees.js +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env node - -const process = require("process"); -const fs = require("fs"); -const anchor = require("@project-serum/anchor"); -const { Market, OpenOrders } = require("@project-serum/serum"); -const Account = anchor.web3.Account; -const Program = anchor.Program; -const provider = anchor.AnchorProvider.local(); -const secret = JSON.parse(fs.readFileSync("./scripts/market-maker.json")); -const MARKET_MAKER = new Account(secret); -const PublicKey = anchor.web3.PublicKey; - -const DEX_PID = new PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"); - -async function main() { - const market = new PublicKey(process.argv[2]); - while (true) { - let marketClient = await Market.load( - provider.connection, - market, - { commitment: "processed" }, - DEX_PID - ); - console.log("Fees: ", marketClient._decoded.quoteFeesAccrued.toString()); - await sleep(3000); - } -} - -main(); - -function sleep(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} diff --git a/tests/cfo/scripts/list-market.js b/tests/cfo/scripts/list-market.js deleted file mode 100755 index 94c90769..00000000 --- a/tests/cfo/scripts/list-market.js +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env node - -// Script to list a market, logging the address to stdout. - -const utils = require("../tests/utils"); -const fs = require("fs"); -const anchor = require("@project-serum/anchor"); -const provider = anchor.AnchorProvider.local(); -// hack so we don't have to update serum-common library -// to the new AnchorProvider class and Provider interface -provider.send = provider.sendAndConfirm; - -async function main() { - ORDERBOOK_ENV = await utils.initMarket({ - provider, - }); - const out = { - market: ORDERBOOK_ENV.marketA._decoded.ownAddress.toString(), - }; - console.log(JSON.stringify(out)); -} - -main(); diff --git a/tests/cfo/scripts/localnet.sh b/tests/cfo/scripts/localnet.sh deleted file mode 100755 index e24eef81..00000000 --- a/tests/cfo/scripts/localnet.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -source scripts/common.sh - -DEX_PID="9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin" -PAYER_FILEPATH="$HOME/.config/solana/id.json" -CRANK="/home/armaniferrante/Documents/code/src/github.com/project-serum/serum-dex/target/debug/crank" -VALIDATOR_OUT="./validator-stdout.txt" -CRANK_LOGS="crank-logs.txt" -CRANK_STDOUT="crank-stdout.txt" -TRADE_BOT_STDOUT="trade-bot-stdout.txt" -FEES_STDOUT="fees.txt" - -main () { - echo "Cleaning old output files..." - rm -rf test-ledger - rm -f $TRADE_BOT_STDOUT - rm -f $FEES_STDOUT - rm -f $VALIDATOR_OUT - rm -f $CRANK_LOGS && touch $CRANK_LOGS - - echo "Starting local network..." - solana-test-validator \ - --bpf-program 9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin ./deps/serum-dex/dex/target/deploy/serum_dex.so \ - --bpf-program 22Y43yTVxuUkoRKdm9thyRhQ3SdgQS7c7kB6UNCiaczD ./deps/swap/target/deploy/swap.so \ - --bpf-program GrAkKfEpTKQuVHG2Y97Y2FF4i7y7Q5AHLK94JBy7Y5yv ./deps/stake/target/deploy/registry.so \ - --bpf-program 6ebQNeTPZ1j7k3TtkCCtEPRvG7GQsucQrZ7sSEDQi9Ks ./deps/stake/target/deploy/lockup.so \ - --bpf-program 5CHQcwNhkFiFXXM8HakHi8cB7AKP3M3GPdEBDeRJBWQq ./target/deploy/cfo.so > $VALIDATOR_OUT & - sleep 2 - - echo "Listing market..." - market=$(./scripts/list-market.js | jq -r .market) - sleep 2 - echo "Market listed $market" - - echo "Running crank..." - $CRANK localnet consume-events \ - -c $market \ - -d $DEX_PID -e 5 \ - --log-directory $CRANK_LOGS \ - --market $market \ - --num-workers 1 \ - --payer $PAYER_FILEPATH \ - --pc-wallet $market > $CRANK_STDOUT & - echo "Running trade bot..." - ./scripts/trade-bot.js $market > $TRADE_BOT_STDOUT & - - echo "Running fees listener..." - ./scripts/fees.js $market > $FEES_STDOUT & - - echo "Localnet running..." - echo "Ctl-c to exit." - wait -} - -main diff --git a/tests/cfo/scripts/market-maker.json b/tests/cfo/scripts/market-maker.json deleted file mode 100644 index fcaec8d7..00000000 --- a/tests/cfo/scripts/market-maker.json +++ /dev/null @@ -1 +0,0 @@ -[13,174,53,150,78,228,12,98,170,254,212,211,125,193,2,241,97,137,49,209,189,199,27,215,220,65,57,203,215,93,105,203,217,32,5,194,157,118,162,47,102,126,235,65,99,80,56,231,217,114,25,225,239,140,169,92,150,146,211,218,183,139,9,104] diff --git a/tests/cfo/scripts/trade-bot.js b/tests/cfo/scripts/trade-bot.js deleted file mode 100755 index b7d2be59..00000000 --- a/tests/cfo/scripts/trade-bot.js +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env node - -// Script to infinitely post orders that are immediately filled. - -const process = require("process"); -const anchor = require("@project-serum/anchor"); -const PublicKey = anchor.web3.PublicKey; -const { runTradeBot } = require("../tests/utils"); - -async function main() { - const market = new PublicKey(process.argv[2]); - const provider = anchor.AnchorProvider.local(); - runTradeBot(market, provider); -} - -main(); diff --git a/tests/cfo/tests/cfo.js b/tests/cfo/tests/cfo.js deleted file mode 100644 index 307bc6c6..00000000 --- a/tests/cfo/tests/cfo.js +++ /dev/null @@ -1,515 +0,0 @@ -const { assert } = require("chai"); -const { Token } = require("@solana/spl-token"); -const anchor = require("@project-serum/anchor"); -const serumCmn = require("@project-serum/common"); -const { Market } = require("@project-serum/serum"); -const utf8 = anchor.utils.bytes.utf8; -const { PublicKey, SystemProgram, Keypair, SYSVAR_RENT_PUBKEY } = anchor.web3; -const utils = require("./utils"); -const { setupStakePool } = require("./utils/stake"); - -const DEX_PID = new PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"); -const SWAP_PID = new PublicKey("22Y43yTVxuUkoRKdm9thyRhQ3SdgQS7c7kB6UNCiaczD"); -const TOKEN_PID = new PublicKey("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"); -const REGISTRY_PID = new PublicKey( - "GrAkKfEpTKQuVHG2Y97Y2FF4i7y7Q5AHLK94JBy7Y5yv" -); -const LOCKUP_PID = new PublicKey( - "6ebQNeTPZ1j7k3TtkCCtEPRvG7GQsucQrZ7sSEDQi9Ks" -); -const SYSVAR_INSTRUCTIONS_PUBKEY = new PublicKey( - "Sysvar1nstructions1111111111111111111111111" -); -const FEES = "6160355581"; - -describe("cfo", () => { - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const program = anchor.workspace.Cfo; - // hack so we don't have to update serum-common library - // to the new AnchorProvider class and Provider interface - program.provider.send = provider.sendAndConfirm; - const sweepAuthority = program.provider.wallet.publicKey; - let officer, srmVault, usdcVault, bVault, stake, treasury; - let officerBump, srmBump, usdcBump, bBump, stakeBump, treasuryBump; - let openOrders, openOrdersBump; - let openOrdersB, openOrdersBumpB; - let USDC_TOKEN_CLIENT, A_TOKEN_CLIENT, B_TOKEN_CLIENT; - let officerAccount; - let marketAClient, marketBClient; - let marketAuth, marketAuthBump; - let marketAuthB, marketAuthBumpB; - let distribution; - - // Accounts used to setup the orderbook. - let ORDERBOOK_ENV, - // Accounts used for A -> USDC swap transactions. - SWAP_A_USDC_ACCOUNTS, - // Accounts used for USDC -> A swap transactions. - SWAP_USDC_A_ACCOUNTS, - // Serum DEX vault PDA for market A/USDC. - marketAVaultSigner, - // Serum DEX vault PDA for market B/USDC. - marketBVaultSigner; - - let registrar, msrmRegistrar; - - it("BOILERPLATE: Sets up a market with funded fees", async () => { - ORDERBOOK_ENV = await utils.initMarket({ - provider: program.provider, - }); - console.log("Token A: ", ORDERBOOK_ENV.marketA.baseMintAddress.toString()); - console.log( - "Token USDC: ", - ORDERBOOK_ENV.marketA.quoteMintAddress.toString() - ); - USDC_TOKEN_CLIENT = new Token( - program.provider.connection, - ORDERBOOK_ENV.usdc, - TOKEN_PID, - program.provider.wallet.payer - ); - SRM_TOKEN_CLIENT = new Token( - program.provider.connection, - ORDERBOOK_ENV.mintA, - TOKEN_PID, - program.provider.wallet.payer - ); - B_TOKEN_CLIENT = new Token( - program.provider.connection, - ORDERBOOK_ENV.mintB, - TOKEN_PID, - program.provider.wallet.payer - ); - - await USDC_TOKEN_CLIENT.transfer( - ORDERBOOK_ENV.godUsdc, - ORDERBOOK_ENV.marketA._decoded.quoteVault, - program.provider.wallet.payer, - [], - 10000000000000 - ); - - const tokenAccount = await USDC_TOKEN_CLIENT.getAccountInfo( - ORDERBOOK_ENV.marketA._decoded.quoteVault - ); - assert.strictEqual(tokenAccount.amount.toString(), "10000902263700"); - }); - - it("BOILERPLATE: Executes trades to generate fees", async () => { - await utils.runTradeBot( - ORDERBOOK_ENV.marketA._decoded.ownAddress, - program.provider, - 1 - ); - marketAClient = await Market.load( - program.provider.connection, - ORDERBOOK_ENV.marketA.address, - { commitment: "processed" }, - DEX_PID - ); - marketBClient = await Market.load( - program.provider.connection, - ORDERBOOK_ENV.marketB.address, - { commitment: "processed" }, - DEX_PID - ); - assert.strictEqual( - marketAClient._decoded.quoteFeesAccrued.toString(), - FEES - ); - }); - - it("BOILERPLATE: Sets up the staking pools", async () => { - await setupStakePool(ORDERBOOK_ENV.mintA, ORDERBOOK_ENV.godA); - registrar = ORDERBOOK_ENV.usdc; - msrmRegistrar = registrar; - }); - - it("BOILERPLATE: Finds PDA addresses", async () => { - const [_officer, _officerBump] = await PublicKey.findProgramAddress( - [DEX_PID.toBuffer()], - program.programId - ); - const [_openOrders, _openOrdersBump] = await PublicKey.findProgramAddress( - [ - utf8.encode("open-orders"), - _officer.toBuffer(), - ORDERBOOK_ENV.marketA.address.toBuffer(), - ], - program.programId - ); - const [_openOrdersB, _openOrdersBumpB] = await PublicKey.findProgramAddress( - [ - utf8.encode("open-orders"), - _officer.toBuffer(), - ORDERBOOK_ENV.marketB.address.toBuffer(), - ], - program.programId - ); - const [_srmVault, _srmBump] = await PublicKey.findProgramAddress( - [ - utf8.encode("token"), - _officer.toBuffer(), - ORDERBOOK_ENV.mintA.toBuffer(), - ], - program.programId - ); - const [_bVault, _bBump] = await PublicKey.findProgramAddress( - [ - utf8.encode("token"), - _officer.toBuffer(), - ORDERBOOK_ENV.mintB.toBuffer(), - ], - program.programId - ); - const [_usdcVault, _usdcBump] = await PublicKey.findProgramAddress( - [ - utf8.encode("token"), - _officer.toBuffer(), - ORDERBOOK_ENV.usdc.toBuffer(), - ], - program.programId - ); - const [_stake, _stakeBump] = await PublicKey.findProgramAddress( - [utf8.encode("stake"), _officer.toBuffer()], - program.programId - ); - const [_treasury, _treasuryBump] = await PublicKey.findProgramAddress( - [utf8.encode("treasury"), _officer.toBuffer()], - program.programId - ); - const [_marketAuth, _marketAuthBump] = await PublicKey.findProgramAddress( - [ - utf8.encode("market-auth"), - _officer.toBuffer(), - ORDERBOOK_ENV.marketA.address.toBuffer(), - ], - program.programId - ); - const [_marketAuthB, _marketAuthBumpB] = await PublicKey.findProgramAddress( - [ - utf8.encode("market-auth"), - _officer.toBuffer(), - ORDERBOOK_ENV.marketB.address.toBuffer(), - ], - program.programId - ); - - officer = _officer; - officerBump = _officerBump; - openOrders = _openOrders; - openOrdersBump = _openOrdersBump; - openOrdersB = _openOrdersB; - openOrdersBumpB = _openOrdersBumpB; - srmVault = _srmVault; - srmBump = _srmBump; - usdcVault = _usdcVault; - usdcBump = _usdcBump; - bVault = _bVault; - bBump = _bBump; - stake = _stake; - stakeBump = _stakeBump; - treasury = _treasury; - treasuryBump = _treasuryBump; - marketAuth = _marketAuth; - marketAuthBump = _marketAuthBump; - marketAuthB = _marketAuthB; - marketAuthBumpB = _marketAuthBumpB; - }); - - it("Creates a CFO!", async () => { - distribution = { - burn: 80, - stake: 20, - treasury: 0, - }; - const bumps = { - bump: officerBump, - srm: srmBump, - usdc: usdcBump, - stake: stakeBump, - treasury: treasuryBump, - }; - await program.methods - .createOfficer(bumps, distribution, registrar, msrmRegistrar) - .accounts({ - officer, - srmVault, - usdcVault, - stake, - treasury, - srmMint: ORDERBOOK_ENV.mintA, - usdcMint: ORDERBOOK_ENV.usdc, - authority: program.provider.wallet.publicKey, - dexProgram: DEX_PID, - swapProgram: SWAP_PID, - tokenProgram: TOKEN_PID, - systemProgram: SystemProgram.programId, - rent: SYSVAR_RENT_PUBKEY, - }) - .rpc(); - - officerAccount = await program.account.officer.fetch(officer); - assert.isTrue( - officerAccount.authority.equals(program.provider.wallet.publicKey) - ); - assert.strictEqual( - JSON.stringify(officerAccount.distribution), - JSON.stringify(distribution) - ); - }); - - it("Creates a token account for the officer associated with the market", async () => { - await program.methods - .createOfficerToken(bBump) - .accounts({ - officer, - token: bVault, - mint: ORDERBOOK_ENV.mintB, - payer: program.provider.wallet.publicKey, - systemProgram: SystemProgram.programId, - tokenProgram: TOKEN_PID, - rent: SYSVAR_RENT_PUBKEY, - }) - .rpc(); - const tokenAccount = await B_TOKEN_CLIENT.getAccountInfo(bVault); - assert.strictEqual(tokenAccount.state, 1); - assert.isTrue(tokenAccount.isInitialized); - }); - - it("Creates an open orders account for the officer", async () => { - await program.methods - .createOfficerOpenOrders(openOrdersBump) - .accounts({ - officer, - openOrders, - payer: program.provider.wallet.publicKey, - dexProgram: DEX_PID, - systemProgram: SystemProgram.programId, - rent: SYSVAR_RENT_PUBKEY, - market: ORDERBOOK_ENV.marketA.address, - }) - .rpc(); - await program.rpc.createOfficerOpenOrders(openOrdersBumpB, { - accounts: { - officer, - openOrders: openOrdersB, - payer: program.provider.wallet.publicKey, - dexProgram: DEX_PID, - systemProgram: SystemProgram.programId, - rent: SYSVAR_RENT_PUBKEY, - market: ORDERBOOK_ENV.marketB.address, - }, - }); - }); - - it("Sweeps fees", async () => { - const [sweepVault, bump] = await PublicKey.findProgramAddress( - [utf8.encode("token"), officer.toBuffer(), ORDERBOOK_ENV.usdc.toBuffer()], - program.programId - ); - const beforeTokenAccount = await serumCmn.getTokenAccount( - program.provider, - sweepVault - ); - await program.methods - .sweepFees() - .accounts({ - officer, - sweepVault, - mint: ORDERBOOK_ENV.usdc, - dex: { - market: ORDERBOOK_ENV.marketA._decoded.ownAddress, - pcVault: ORDERBOOK_ENV.marketA._decoded.quoteVault, - sweepAuthority, - vaultSigner: ORDERBOOK_ENV.marketAVaultSigner, - dexProgram: DEX_PID, - tokenProgram: TOKEN_PID, - }, - }) - .rpc(); - const afterTokenAccount = await serumCmn.getTokenAccount( - program.provider, - sweepVault - ); - assert.strictEqual( - afterTokenAccount.amount.sub(beforeTokenAccount.amount).toString(), - FEES - ); - }); - - it("Creates a market auth token", async () => { - await program.methods - .authorizeMarket(marketAuthBump) - .accounts({ - officer, - authority: program.provider.wallet.publicKey, - marketAuth, - payer: program.provider.wallet.publicKey, - market: ORDERBOOK_ENV.marketA.address, - systemProgram: SystemProgram.programId, - }) - .rpc(); - await program.methods - .authorizeMarket(marketAuthBumpB) - .accounts({ - officer, - authority: program.provider.wallet.publicKey, - marketAuth: marketAuthB, - payer: program.provider.wallet.publicKey, - market: ORDERBOOK_ENV.marketB.address, - systemProgram: SystemProgram.programId, - }) - .rpc(); - }); - - it("Transfers into the mintB vault", async () => { - await B_TOKEN_CLIENT.transfer( - ORDERBOOK_ENV.godB, - bVault, - program.provider.wallet.payer, - [], - 616035558100 - ); - }); - - it("Swaps from B token to USDC", async () => { - const bVaultBefore = await B_TOKEN_CLIENT.getAccountInfo(bVault); - const usdcVaultBefore = await USDC_TOKEN_CLIENT.getAccountInfo(usdcVault); - - const minExchangeRate = { - rate: new anchor.BN(0), - fromDecimals: 6, - quoteDecimals: 6, - strict: false, - }; - await program.methods - .swapToUsdc(minExchangeRate) - .accounts({ - officer, - market: { - market: marketBClient.address, - openOrders: openOrdersB, - requestQueue: marketBClient.decoded.requestQueue, - eventQueue: marketBClient.decoded.eventQueue, - bids: marketBClient.decoded.bids, - asks: marketBClient.decoded.asks, - orderPayerTokenAccount: bVault, - coinVault: marketBClient.decoded.baseVault, - pcVault: marketBClient.decoded.quoteVault, - vaultSigner: ORDERBOOK_ENV.marketBVaultSigner, - }, - marketAuth: marketAuthB, - usdcVault, - fromVault: bVault, - usdcMint: ORDERBOOK_ENV.usdc, - swapProgram: SWAP_PID, - dexProgram: DEX_PID, - tokenProgram: TOKEN_PID, - instructions: SYSVAR_INSTRUCTIONS_PUBKEY, - rent: SYSVAR_RENT_PUBKEY, - }) - .rpc(); - - const bVaultAfter = await B_TOKEN_CLIENT.getAccountInfo(bVault); - const usdcVaultAfter = await USDC_TOKEN_CLIENT.getAccountInfo(usdcVault); - - assert.strictEqual(bVaultBefore.amount.toNumber(), 616035558100); - assert.strictEqual(usdcVaultBefore.amount.toNumber(), 6160355581); - assert.strictEqual(bVaultAfter.amount.toNumber(), 615884458100); - assert.strictEqual(usdcVaultAfter.amount.toNumber(), 7060634298); - }); - - it("Swaps to SRM", async () => { - const srmVaultBefore = await SRM_TOKEN_CLIENT.getAccountInfo(srmVault); - const usdcVaultBefore = await USDC_TOKEN_CLIENT.getAccountInfo(usdcVault); - - const minExchangeRate = { - rate: new anchor.BN(0), - fromDecimals: 6, - quoteDecimals: 6, - strict: false, - }; - await program.methods - .swapToSrm(minExchangeRate) - .accounts({ - officer, - market: { - market: marketAClient.address, - openOrders, - requestQueue: marketAClient.decoded.requestQueue, - eventQueue: marketAClient.decoded.eventQueue, - bids: marketAClient.decoded.bids, - asks: marketAClient.decoded.asks, - orderPayerTokenAccount: usdcVault, - coinVault: marketAClient.decoded.baseVault, - pcVault: marketAClient.decoded.quoteVault, - vaultSigner: ORDERBOOK_ENV.marketAVaultSigner, - }, - marketAuth, - usdcVault, - srmVault, - usdcMint: ORDERBOOK_ENV.usdc, - srmMint: ORDERBOOK_ENV.mintA, - swapProgram: SWAP_PID, - dexProgram: DEX_PID, - tokenProgram: TOKEN_PID, - instructions: SYSVAR_INSTRUCTIONS_PUBKEY, - rent: SYSVAR_RENT_PUBKEY, - }) - .rpc(); - - const srmVaultAfter = await SRM_TOKEN_CLIENT.getAccountInfo(srmVault); - const usdcVaultAfter = await USDC_TOKEN_CLIENT.getAccountInfo(usdcVault); - - assert.strictEqual(srmVaultBefore.amount.toNumber(), 0); - assert.strictEqual(srmVaultAfter.amount.toNumber(), 1152000000); - assert.strictEqual(usdcVaultBefore.amount.toNumber(), 7060634298); - assert.strictEqual(usdcVaultAfter.amount.toNumber(), 530863); - }); - - it("Distributes the tokens to categories", async () => { - const srmVaultBefore = await SRM_TOKEN_CLIENT.getAccountInfo(srmVault); - const treasuryBefore = await SRM_TOKEN_CLIENT.getAccountInfo(treasury); - const stakeBefore = await SRM_TOKEN_CLIENT.getAccountInfo(stake); - const mintInfoBefore = await SRM_TOKEN_CLIENT.getMintInfo(); - - await program.methods - .distribute() - .accounts({ - officer, - treasury, - stake, - srmVault, - srmMint: ORDERBOOK_ENV.mintA, - tokenProgram: TOKEN_PID, - dexProgram: DEX_PID, - }) - .rpc(); - - const srmVaultAfter = await SRM_TOKEN_CLIENT.getAccountInfo(srmVault); - const treasuryAfter = await SRM_TOKEN_CLIENT.getAccountInfo(treasury); - const stakeAfter = await SRM_TOKEN_CLIENT.getAccountInfo(stake); - const mintInfoAfter = await SRM_TOKEN_CLIENT.getMintInfo(); - - const beforeAmount = 1152000000; - assert.strictEqual(srmVaultBefore.amount.toNumber(), beforeAmount); - assert.strictEqual(srmVaultAfter.amount.toNumber(), 0); // Fully distributed. - assert.strictEqual( - stakeAfter.amount.toNumber(), - beforeAmount * (distribution.stake / 100.0) - ); - assert.strictEqual( - treasuryAfter.amount.toNumber(), - beforeAmount * (distribution.treasury / 100.0) - ); - // Check burn amount. - assert.strictEqual(mintInfoBefore.supply.toString(), "1000000000000000000"); - assert.strictEqual( - mintInfoBefore.supply.sub(mintInfoAfter.supply).toNumber(), - beforeAmount * (distribution.burn / 100.0) - ); - }); -}); diff --git a/tests/cfo/tests/utils/index.js b/tests/cfo/tests/utils/index.js deleted file mode 100644 index 63d21666..00000000 --- a/tests/cfo/tests/utils/index.js +++ /dev/null @@ -1,599 +0,0 @@ -// Boilerplate utils to bootstrap an orderbook for testing on a localnet. -// not super relevant to the point of the example, though may be useful to -// include into your own workspace for testing. -// -// TODO: Modernize all these apis. This is all quite clunky. - -const Token = require("@solana/spl-token").Token; -const TOKEN_PROGRAM_ID = require("@solana/spl-token").TOKEN_PROGRAM_ID; -const TokenInstructions = require("@project-serum/serum").TokenInstructions; -const { Market, OpenOrders } = require("@project-serum/serum"); -const DexInstructions = require("@project-serum/serum").DexInstructions; -const web3 = require("@project-serum/anchor").web3; -const Connection = web3.Connection; -const anchor = require("@project-serum/anchor"); -const BN = anchor.BN; -const serumCmn = require("@project-serum/common"); -const Account = web3.Account; -const Transaction = web3.Transaction; -const PublicKey = web3.PublicKey; -const SystemProgram = web3.SystemProgram; -const DEX_PID = new PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"); -const secret = JSON.parse( - require("fs").readFileSync("./scripts/market-maker.json") -); -const MARKET_MAKER = new Account(secret); - -async function initMarket({ provider }) { - // Setup mints with initial tokens owned by the provider. - const decimals = 6; - const [MINT_A, GOD_A] = await serumCmn.createMintAndVault( - provider, - new BN("1000000000000000000"), - undefined, - decimals - ); - const [MINT_B, GOD_B] = await serumCmn.createMintAndVault( - provider, - new BN("1000000000000000000"), - undefined, - decimals - ); - const [USDC, GOD_USDC] = await serumCmn.createMintAndVault( - provider, - new BN("1000000000000000000"), - undefined, - decimals - ); - - // Create a funded account to act as market maker. - const amount = new BN("10000000000000").muln(10 ** decimals); - const marketMaker = await fundAccount({ - provider, - mints: [ - { god: GOD_A, mint: MINT_A, amount, decimals }, - { god: GOD_B, mint: MINT_B, amount, decimals }, - { god: GOD_USDC, mint: USDC, amount, decimals }, - ], - }); - - // Setup A/USDC with resting orders. - const asks = [ - [6.041, 7.8], - [6.051, 72.3], - [6.055, 5.4], - [6.067, 15.7], - [6.077, 390.0], - [6.09, 24.0], - [6.11, 36.3], - [6.133, 300.0], - [6.167, 687.8], - ]; - const bids = [ - [6.004, 8.5], - [5.995, 12.9], - [5.987, 6.2], - [5.978, 15.3], - [5.965, 82.8], - [5.961, 25.4], - ]; - - [MARKET_A_USDC, marketAVaultSigner] = await setupMarket({ - baseMint: MINT_A, - quoteMint: USDC, - marketMaker: { - account: marketMaker.account, - baseToken: marketMaker.tokens[MINT_A.toString()], - quoteToken: marketMaker.tokens[USDC.toString()], - }, - bids, - asks, - provider, - }); - [MARKET_B_USDC, marketBVaultSigner] = await setupMarket({ - baseMint: MINT_B, - quoteMint: USDC, - marketMaker: { - account: marketMaker.account, - baseToken: marketMaker.tokens[MINT_B.toString()], - quoteToken: marketMaker.tokens[USDC.toString()], - }, - bids, - asks, - provider, - }); - - return { - marketA: MARKET_A_USDC, - marketAVaultSigner, - marketB: MARKET_B_USDC, - marketBVaultSigner, - marketMaker, - mintA: MINT_A, - mintB: MINT_B, - usdc: USDC, - godA: GOD_A, - godB: GOD_B, - godUsdc: GOD_USDC, - }; -} - -async function fundAccount({ provider, mints }) { - const marketMaker = { - tokens: {}, - account: MARKET_MAKER, - }; - - // Transfer lamports to market maker. - await provider.sendAndConfirm( - (() => { - const tx = new Transaction(); - tx.add( - SystemProgram.transfer({ - fromPubkey: provider.wallet.publicKey, - toPubkey: MARKET_MAKER.publicKey, - lamports: 100000000000, - }) - ); - return tx; - })() - ); - - // Transfer SPL tokens to the market maker. - for (let k = 0; k < mints.length; k += 1) { - const { mint, god, amount, decimals } = mints[k]; - let MINT_A = mint; - let GOD_A = god; - // Setup token accounts owned by the market maker. - const mintAClient = new Token( - provider.connection, - MINT_A, - TOKEN_PROGRAM_ID, - provider.wallet.payer // node only - ); - const marketMakerTokenA = await mintAClient.createAccount( - MARKET_MAKER.publicKey - ); - - await provider.sendAndConfirm( - (() => { - const tx = new Transaction(); - tx.add( - Token.createTransferCheckedInstruction( - TOKEN_PROGRAM_ID, - GOD_A, - MINT_A, - marketMakerTokenA, - provider.wallet.publicKey, - [], - amount, - decimals - ) - ); - return tx; - })() - ); - - marketMaker.tokens[mint.toString()] = marketMakerTokenA; - } - - return marketMaker; -} - -async function setupMarket({ - provider, - marketMaker, - baseMint, - quoteMint, - bids, - asks, -}) { - const [marketAPublicKey, vaultOwner] = await listMarket({ - connection: provider.connection, - wallet: provider.wallet, - baseMint: baseMint, - quoteMint: quoteMint, - baseLotSize: 100000, - quoteLotSize: 100, - dexProgramId: DEX_PID, - feeRateBps: 0, - }); - const MARKET_A_USDC = await Market.load( - provider.connection, - marketAPublicKey, - { commitment: "processed" }, - DEX_PID - ); - for (let k = 0; k < asks.length; k += 1) { - let ask = asks[k]; - const { transaction, signers } = - await MARKET_A_USDC.makePlaceOrderTransaction(provider.connection, { - owner: marketMaker.account, - payer: marketMaker.baseToken, - side: "sell", - price: ask[0], - size: ask[1], - orderType: "postOnly", - clientId: undefined, - openOrdersAddressKey: undefined, - openOrdersAccount: undefined, - feeDiscountPubkey: null, - selfTradeBehavior: "abortTransaction", - }); - await provider.sendAndConfirm( - transaction, - signers.concat(marketMaker.account) - ); - } - - for (let k = 0; k < bids.length; k += 1) { - let bid = bids[k]; - const { transaction, signers } = - await MARKET_A_USDC.makePlaceOrderTransaction(provider.connection, { - owner: marketMaker.account, - payer: marketMaker.quoteToken, - side: "buy", - price: bid[0], - size: bid[1], - orderType: "postOnly", - clientId: undefined, - openOrdersAddressKey: undefined, - openOrdersAccount: undefined, - feeDiscountPubkey: null, - selfTradeBehavior: "abortTransaction", - }); - await provider.sendAndConfirm( - transaction, - signers.concat(marketMaker.account) - ); - } - - return [MARKET_A_USDC, vaultOwner]; -} - -async function listMarket({ - connection, - wallet, - baseMint, - quoteMint, - baseLotSize, - quoteLotSize, - dexProgramId, - feeRateBps, -}) { - const market = new Account(); - const requestQueue = new Account(); - const eventQueue = new Account(); - const bids = new Account(); - const asks = new Account(); - const baseVault = new Account(); - const quoteVault = new Account(); - const quoteDustThreshold = new BN(100); - - const [vaultOwner, vaultSignerNonce] = await getVaultOwnerAndNonce( - market.publicKey, - dexProgramId - ); - - const tx1 = new Transaction(); - tx1.add( - SystemProgram.createAccount({ - fromPubkey: wallet.publicKey, - newAccountPubkey: baseVault.publicKey, - lamports: await connection.getMinimumBalanceForRentExemption(165), - space: 165, - programId: TOKEN_PROGRAM_ID, - }), - SystemProgram.createAccount({ - fromPubkey: wallet.publicKey, - newAccountPubkey: quoteVault.publicKey, - lamports: await connection.getMinimumBalanceForRentExemption(165), - space: 165, - programId: TOKEN_PROGRAM_ID, - }), - TokenInstructions.initializeAccount({ - account: baseVault.publicKey, - mint: baseMint, - owner: vaultOwner, - }), - TokenInstructions.initializeAccount({ - account: quoteVault.publicKey, - mint: quoteMint, - owner: vaultOwner, - }) - ); - - const tx2 = new Transaction(); - tx2.add( - SystemProgram.createAccount({ - fromPubkey: wallet.publicKey, - newAccountPubkey: market.publicKey, - lamports: await connection.getMinimumBalanceForRentExemption( - Market.getLayout(dexProgramId).span - ), - space: Market.getLayout(dexProgramId).span, - programId: dexProgramId, - }), - SystemProgram.createAccount({ - fromPubkey: wallet.publicKey, - newAccountPubkey: requestQueue.publicKey, - lamports: await connection.getMinimumBalanceForRentExemption(5120 + 12), - space: 5120 + 12, - programId: dexProgramId, - }), - SystemProgram.createAccount({ - fromPubkey: wallet.publicKey, - newAccountPubkey: eventQueue.publicKey, - lamports: await connection.getMinimumBalanceForRentExemption(262144 + 12), - space: 262144 + 12, - programId: dexProgramId, - }), - SystemProgram.createAccount({ - fromPubkey: wallet.publicKey, - newAccountPubkey: bids.publicKey, - lamports: await connection.getMinimumBalanceForRentExemption(65536 + 12), - space: 65536 + 12, - programId: dexProgramId, - }), - SystemProgram.createAccount({ - fromPubkey: wallet.publicKey, - newAccountPubkey: asks.publicKey, - lamports: await connection.getMinimumBalanceForRentExemption(65536 + 12), - space: 65536 + 12, - programId: dexProgramId, - }), - DexInstructions.initializeMarket({ - market: market.publicKey, - requestQueue: requestQueue.publicKey, - eventQueue: eventQueue.publicKey, - bids: bids.publicKey, - asks: asks.publicKey, - baseVault: baseVault.publicKey, - quoteVault: quoteVault.publicKey, - baseMint, - quoteMint, - baseLotSize: new BN(baseLotSize), - quoteLotSize: new BN(quoteLotSize), - feeRateBps, - vaultSignerNonce, - quoteDustThreshold, - programId: dexProgramId, - }) - ); - - const signedTransactions = await signTransactions({ - transactionsAndSigners: [ - { transaction: tx1, signers: [baseVault, quoteVault] }, - { - transaction: tx2, - signers: [market, requestQueue, eventQueue, bids, asks], - }, - ], - wallet, - connection, - }); - for (let signedTransaction of signedTransactions) { - await sendAndConfirmRawTransaction( - connection, - signedTransaction.serialize() - ); - } - const acc = await connection.getAccountInfo(market.publicKey); - - return [market.publicKey, vaultOwner]; -} - -async function signTransactions({ - transactionsAndSigners, - wallet, - connection, -}) { - const blockhash = (await connection.getRecentBlockhash("finalized")) - .blockhash; - transactionsAndSigners.forEach(({ transaction, signers = [] }) => { - transaction.recentBlockhash = blockhash; - transaction.setSigners( - wallet.publicKey, - ...signers.map((s) => s.publicKey) - ); - if (signers?.length > 0) { - transaction.partialSign(...signers); - } - }); - return await wallet.signAllTransactions( - transactionsAndSigners.map(({ transaction }) => transaction) - ); -} - -async function sendAndConfirmRawTransaction( - connection, - raw, - commitment = "processed" -) { - let tx = await connection.sendRawTransaction(raw, { - skipPreflight: true, - }); - return await connection.confirmTransaction(tx, commitment); -} - -async function getVaultOwnerAndNonce(marketPublicKey, dexProgramId = DEX_PID) { - const nonce = new BN(0); - while (nonce.toNumber() < 255) { - try { - const vaultOwner = await PublicKey.createProgramAddress( - [marketPublicKey.toBuffer(), nonce.toArrayLike(Buffer, "le", 8)], - dexProgramId - ); - return [vaultOwner, nonce]; - } catch (e) { - nonce.iaddn(1); - } - } - throw new Error("Unable to find nonce"); -} - -async function runTradeBot(market, provider, iterations = undefined) { - let marketClient = await Market.load( - provider.connection, - market, - { commitment: "processed" }, - DEX_PID - ); - const baseTokenUser1 = ( - await marketClient.getTokenAccountsByOwnerForMint( - provider.connection, - MARKET_MAKER.publicKey, - marketClient.baseMintAddress - ) - )[0].pubkey; - const quoteTokenUser1 = ( - await marketClient.getTokenAccountsByOwnerForMint( - provider.connection, - MARKET_MAKER.publicKey, - marketClient.quoteMintAddress - ) - )[0].pubkey; - - const baseTokenUser2 = ( - await marketClient.getTokenAccountsByOwnerForMint( - provider.connection, - provider.wallet.publicKey, - marketClient.baseMintAddress - ) - )[0].pubkey; - const quoteTokenUser2 = ( - await marketClient.getTokenAccountsByOwnerForMint( - provider.connection, - provider.wallet.publicKey, - marketClient.quoteMintAddress - ) - )[0].pubkey; - - const makerOpenOrdersUser1 = ( - await OpenOrders.findForMarketAndOwner( - provider.connection, - market, - MARKET_MAKER.publicKey, - DEX_PID - ) - )[0]; - makerOpenOrdersUser2 = ( - await OpenOrders.findForMarketAndOwner( - provider.connection, - market, - provider.wallet.publicKey, - DEX_PID - ) - )[0]; - - const price = 6.041; - const size = 700000.8; - - let maker = MARKET_MAKER; - let taker = provider.wallet.payer; - let baseToken = baseTokenUser1; - let quoteToken = quoteTokenUser2; - let makerOpenOrders = makerOpenOrdersUser1; - - let k = 1; - - while (true) { - if (iterations && k > iterations) { - break; - } - const clientId = new anchor.BN(k); - if (k % 5 === 0) { - if (maker.publicKey.equals(MARKET_MAKER.publicKey)) { - maker = provider.wallet.payer; - makerOpenOrders = makerOpenOrdersUser2; - taker = MARKET_MAKER; - baseToken = baseTokenUser2; - quoteToken = quoteTokenUser1; - } else { - maker = MARKET_MAKER; - makerOpenOrders = makerOpenOrdersUser1; - taker = provider.wallet.payer; - baseToken = baseTokenUser1; - quoteToken = quoteTokenUser2; - } - } - - // Post ask. - const { transaction: tx_ask, signers: sigs_ask } = - await marketClient.makePlaceOrderTransaction(provider.connection, { - owner: maker, - payer: baseToken, - side: "sell", - price, - size, - orderType: "postOnly", - clientId, - openOrdersAddressKey: undefined, - openOrdersAccount: undefined, - feeDiscountPubkey: null, - selfTradeBehavior: "abortTransaction", - }); - let txSig = await provider.sendAndConfirm(tx_ask, sigs_ask.concat(maker)); - console.log("Ask", txSig); - - // Take. - const { transaction: tx_bid, signers: sigs_bid } = - await marketClient.makePlaceOrderTransaction(provider.connection, { - owner: taker, - payer: quoteToken, - side: "buy", - price, - size, - orderType: "ioc", - clientId: undefined, - openOrdersAddressKey: undefined, - openOrdersAccount: undefined, - feeDiscountPubkey: null, - selfTradeBehavior: "abortTransaction", - }); - txSig = await provider.sendAndConfirm(tx_bid, sigs_bid.concat(taker)); - console.log("Bid", txSig); - - await sleep(1000); - - // Cancel anything remaining. - try { - txSig = await marketClient.cancelOrderByClientId( - provider.connection, - maker, - makerOpenOrders.address, - clientId - ); - console.log("Cancelled the rest", txSig); - await sleep(1000); - } catch (e) { - console.log("Unable to cancel order", e); - } - k += 1; - - // If the open orders account wasn't previously initialized, it is now. - if (makerOpenOrdersUser2 === undefined) { - makerOpenOrdersUser2 = ( - await OpenOrders.findForMarketAndOwner( - provider.connection, - market, - provider.wallet.publicKey, - DEX_PID - ) - )[0]; - } - } -} - -function sleep(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - -module.exports = { - fundAccount, - initMarket, - setupMarket, - DEX_PID, - getVaultOwnerAndNonce, - runTradeBot, -}; diff --git a/tests/cfo/tests/utils/stake.js b/tests/cfo/tests/utils/stake.js deleted file mode 100644 index 1a9d0035..00000000 --- a/tests/cfo/tests/utils/stake.js +++ /dev/null @@ -1,187 +0,0 @@ -const anchor = require("@project-serum/anchor"); -const serumCmn = require("@project-serum/common"); -const TokenInstructions = require("@project-serum/serum").TokenInstructions; -const utils = require("../../deps/stake/tests/utils"); - -const lockup = anchor.workspace.Lockup; -const registry = anchor.workspace.Registry; -const provider = anchor.AnchorProvider.env(); -// hack so we don't have to update serum-common library -// to the new AnchorProvider class and Provider interface -provider.send = provider.sendAndConfirm; - -let lockupAddress = null; -let mint = null; -let god = null; - -let registrarAccount = null; -let registrarSigner = null; -let nonce = null; -let poolMint = null; - -const registrar = new anchor.web3.Account(); -const rewardQ = new anchor.web3.Account(); -const withdrawalTimelock = new anchor.BN(4); -const stakeRate = new anchor.BN(2); -const rewardQLen = 170; -let member = null; - -let memberAccount = null; -let memberSigner = null; -let balances = null; -let balancesLocked = null; - -const WHITELIST_SIZE = 10; - -async function setupStakePool(mint, god) { - // Registry genesis. - const [_registrarSigner, _nonce] = - await anchor.web3.PublicKey.findProgramAddress( - [registrar.publicKey.toBuffer()], - registry.programId - ); - registrarSigner = _registrarSigner; - nonce = _nonce; - poolMint = await serumCmn.createMint(provider, registrarSigner); - - try { - // Init registry. - await registry.state.rpc.new({ - accounts: { lockupProgram: lockup.programId }, - }); - - // Init lockup. - await lockup.state.rpc.new({ - accounts: { - authority: provider.wallet.publicKey, - }, - }); - } catch (err) { - // Skip errors for convenience when developing locally, - // since the state constructors can only be called once. - } - - // Initialize stake pool. - await registry.rpc.initialize( - mint, - provider.wallet.publicKey, - nonce, - withdrawalTimelock, - stakeRate, - rewardQLen, - { - accounts: { - registrar: registrar.publicKey, - poolMint, - rewardEventQ: rewardQ.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [registrar, rewardQ], - instructions: [ - await registry.account.registrar.createInstruction(registrar), - await registry.account.rewardQueue.createInstruction(rewardQ, 8250), - ], - } - ); - registrarAccount = await registry.account.registrar.fetch( - registrar.publicKey - ); - console.log("Registrar", registrar.publicKey.toString()); - console.log("Wallet", registry.provider.wallet.publicKey.toString()); - // Create account for staker. - const seed = anchor.utils.sha256 - .hash(`${registrar.publicKey.toString()}:Member`) - .slice(0, 32); - member = await anchor.web3.PublicKey.createWithSeed( - registry.provider.wallet.publicKey, - seed, - registry.programId - ); - const [_memberSigner, nonce2] = - await anchor.web3.PublicKey.findProgramAddress( - [registrar.publicKey.toBuffer(), member.toBuffer()], - registry.programId - ); - memberSigner = _memberSigner; - const [mainTx, _balances] = await utils.createBalanceSandbox( - provider, - registrarAccount, - memberSigner - ); - const [lockedTx, _balancesLocked] = await utils.createBalanceSandbox( - provider, - registrarAccount, - memberSigner - ); - balances = _balances; - balancesLocked = _balancesLocked; - const tx = registry.transaction.createMember(nonce2, { - accounts: { - registrar: registrar.publicKey, - member: member, - beneficiary: provider.wallet.publicKey, - memberSigner, - balances, - balancesLocked, - tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - instructions: [ - anchor.web3.SystemProgram.createAccountWithSeed({ - fromPubkey: registry.provider.wallet.publicKey, - newAccountPubkey: member, - basePubkey: registry.provider.wallet.publicKey, - seed, - lamports: - await registry.provider.connection.getMinimumBalanceForRentExemption( - registry.account.member.size - ), - space: registry.account.member.size, - programId: registry.programId, - }), - ], - }); - const signers = [provider.wallet.payer]; - const allTxs = [mainTx, lockedTx, { tx, signers }]; - await provider.sendAll(allTxs); - memberAccount = await registry.account.member.fetch(member); - - // Deposit into stake program. - const depositAmount = new anchor.BN(120); - await registry.rpc.deposit(depositAmount, { - accounts: { - depositor: god, - depositorAuthority: provider.wallet.publicKey, - tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID, - vault: memberAccount.balances.vault, - beneficiary: provider.wallet.publicKey, - member: member, - }, - }); - - // Stake. - const stakeAmount = new anchor.BN(10); - await registry.rpc.stake(stakeAmount, false, { - accounts: { - // Stake instance. - registrar: registrar.publicKey, - rewardEventQ: rewardQ.publicKey, - poolMint, - // Member. - member: member, - beneficiary: provider.wallet.publicKey, - balances, - balancesLocked, - // Program signers. - memberSigner, - registrarSigner, - // Misc. - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, - tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID, - }, - }); -} - -module.exports = { - setupStakePool, -}; diff --git a/tests/chat/Anchor.toml b/tests/chat/Anchor.toml deleted file mode 100644 index ec4727cb..00000000 --- a/tests/chat/Anchor.toml +++ /dev/null @@ -1,11 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -chat = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[scripts] -test = "yarn run mocha -t 1000000 tests/" - -[features] diff --git a/tests/chat/Cargo.toml b/tests/chat/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/chat/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/chat/migrations/deploy.js b/tests/chat/migrations/deploy.js deleted file mode 100644 index 5e3df0dc..00000000 --- a/tests/chat/migrations/deploy.js +++ /dev/null @@ -1,12 +0,0 @@ -// Migrations are an early feature. Currently, they're nothing more than this -// single deploy script that's invoked from the CLI, injecting a provider -// configured from the workspace's Anchor.toml. - -const anchor = require("@project-serum/anchor"); - -module.exports = async function (provider) { - // Configure client to use the provider. - anchor.setProvider(provider); - - // Add your deploy script here. -}; diff --git a/tests/chat/package.json b/tests/chat/package.json deleted file mode 100644 index e61b3eaa..00000000 --- a/tests/chat/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "chat", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/chat/programs/chat/Cargo.toml b/tests/chat/programs/chat/Cargo.toml deleted file mode 100644 index 727c5596..00000000 --- a/tests/chat/programs/chat/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "chat" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "chat" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } diff --git a/tests/chat/programs/chat/Xargo.toml b/tests/chat/programs/chat/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/chat/programs/chat/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/chat/programs/chat/src/lib.rs b/tests/chat/programs/chat/src/lib.rs deleted file mode 100644 index 88494717..00000000 --- a/tests/chat/programs/chat/src/lib.rs +++ /dev/null @@ -1,113 +0,0 @@ -//! A simple chat program using a ring buffer to store messages. - -use anchor_lang::accounts::loader::Loader; -use anchor_lang::prelude::*; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -pub mod chat { - use super::*; - - pub fn create_user(ctx: Context, name: String) -> Result<()> { - ctx.accounts.user.name = name; - ctx.accounts.user.authority = *ctx.accounts.authority.key; - ctx.accounts.user.bump = *ctx.bumps.get("user").unwrap(); - Ok(()) - } - pub fn create_chat_room(ctx: Context, name: String) -> Result<()> { - let given_name = name.as_bytes(); - let mut name = [0u8; 280]; - name[..given_name.len()].copy_from_slice(given_name); - let mut chat = ctx.accounts.chat_room.load_init()?; - chat.name = name; - Ok(()) - } - pub fn send_message(ctx: Context, msg: String) -> Result<()> { - let mut chat = ctx.accounts.chat_room.load_mut()?; - chat.append({ - let src = msg.as_bytes(); - let mut data = [0u8; 280]; - data[..src.len()].copy_from_slice(src); - Message { - from: *ctx.accounts.user.to_account_info().key, - data, - } - }); - Ok(()) - } -} - -#[derive(Accounts)] -#[instruction(name: String)] -pub struct CreateUser<'info> { - #[account( - init, - seeds = [authority.key().as_ref()], - bump, - payer = authority, - space = 320, - )] - user: Account<'info, User>, - #[account(mut)] - authority: Signer<'info>, - system_program: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct CreateChatRoom<'info> { - #[account(zero)] - chat_room: Loader<'info, ChatRoom>, -} - -#[derive(Accounts)] -pub struct SendMessage<'info> { - #[account( - seeds = [authority.key().as_ref()], - bump = user.bump, - has_one = authority, - )] - user: Account<'info, User>, - authority: Signer<'info>, - #[account(mut)] - chat_room: Loader<'info, ChatRoom>, -} - -#[account] -pub struct User { - name: String, - authority: Pubkey, - bump: u8, -} - -#[account(zero_copy)] -pub struct ChatRoom { - head: u64, - tail: u64, - name: [u8; 280], // Human readable name (char bytes). - messages: [Message; 33607], // Leaves the account at 10,485,680 bytes. -} - -impl ChatRoom { - fn append(&mut self, msg: Message) { - self.messages[ChatRoom::index_of(self.head)] = msg; - if ChatRoom::index_of(self.head + 1) == ChatRoom::index_of(self.tail) { - self.tail += 1; - } - self.head += 1; - } - fn index_of(counter: u64) -> usize { - std::convert::TryInto::try_into(counter % 33607).unwrap() - } -} - -#[zero_copy] -pub struct Message { - pub from: Pubkey, - pub data: [u8; 280], -} - -#[error_code] -pub enum ErrorCode { - Unknown, -} diff --git a/tests/chat/tests/chat.js b/tests/chat/tests/chat.js deleted file mode 100644 index 08c2b4a6..00000000 --- a/tests/chat/tests/chat.js +++ /dev/null @@ -1,106 +0,0 @@ -const anchor = require("@project-serum/anchor"); -const { assert } = require("chai"); -const { PublicKey } = anchor.web3; - -describe("chat", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.env()); - - // Program client handle. - const program = anchor.workspace.Chat; - - // Chat room account. - const chatRoom = anchor.web3.Keypair.generate(); - - it("Creates a chat room", async () => { - await program.rpc.createChatRoom("Test Chat", { - accounts: { - chatRoom: chatRoom.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - instructions: [ - await program.account.chatRoom.createInstruction(chatRoom), - ], - signers: [chatRoom], - }); - - const chat = await program.account.chatRoom.fetch(chatRoom.publicKey); - const name = new TextDecoder("utf-8").decode(new Uint8Array(chat.name)); - assert.isTrue(name.startsWith("Test Chat")); // [u8; 280] => trailing zeros. - assert.lengthOf(chat.messages, 33607); - assert.strictEqual(chat.head.toNumber(), 0); - assert.strictEqual(chat.tail.toNumber(), 0); - }); - - it("Creates a user", async () => { - const authority = program.provider.wallet.publicKey; - const [user, bump] = await PublicKey.findProgramAddress( - [authority.toBuffer()], - program.programId - ); - await program.rpc.createUser("My User", { - accounts: { - user, - authority, - systemProgram: anchor.web3.SystemProgram.programId, - }, - }); - const account = await program.account.user.fetch(user); - assert.strictEqual(account.name, "My User"); - assert.isTrue(account.authority.equals(authority)); - }); - - it("Sends messages", async () => { - const authority = program.provider.wallet.publicKey; - const user = ( - await PublicKey.findProgramAddress( - [authority.toBuffer()], - program.programId - ) - )[0]; - - // Only send a couple messages so the test doesn't take an eternity. - const numMessages = 10; - - // Generate random message strings. - const messages = new Array(numMessages).fill("").map((msg) => { - return ( - Math.random().toString(36).substring(2, 15) + - Math.random().toString(36).substring(2, 15) - ); - }); - - // Send each message. - for (let k = 0; k < numMessages; k += 1) { - console.log("Sending message " + k); - await program.rpc.sendMessage(messages[k], { - accounts: { - user, - authority, - chatRoom: chatRoom.publicKey, - }, - }); - } - - // Check the chat room state is as expected. - const chat = await program.account.chatRoom.fetch(chatRoom.publicKey); - const name = new TextDecoder("utf-8").decode(new Uint8Array(chat.name)); - assert.isTrue(name.startsWith("Test Chat")); // [u8; 280] => trailing zeros. - assert.lengthOf(chat.messages, 33607); - assert.strictEqual(chat.head.toNumber(), numMessages); - assert.strictEqual(chat.tail.toNumber(), 0); - chat.messages.forEach((msg, idx) => { - if (idx < 10) { - const data = new TextDecoder("utf-8").decode(new Uint8Array(msg.data)); - console.log("Message", data); - assert.isTrue(msg.from.equals(user)); - assert.isTrue(data.startsWith(messages[idx])); - } else { - assert.strictEqual( - JSON.stringify(msg.data), - JSON.stringify(new Array(280).fill(0)) - ); - } - }); - }); -}); diff --git a/tests/composite/Anchor.toml b/tests/composite/Anchor.toml deleted file mode 100644 index 2983b520..00000000 --- a/tests/composite/Anchor.toml +++ /dev/null @@ -1,9 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -composite = "EHthziFziNoac9LBGxEaVN47Y3uUiRoXvqAiR6oes4iU" - -[scripts] -test = "yarn run mocha -t 1000000 tests/" diff --git a/tests/composite/Cargo.toml b/tests/composite/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/composite/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/composite/package.json b/tests/composite/package.json deleted file mode 100644 index 4a135f81..00000000 --- a/tests/composite/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "composite", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/composite/programs/composite/Cargo.toml b/tests/composite/programs/composite/Cargo.toml deleted file mode 100644 index d3504179..00000000 --- a/tests/composite/programs/composite/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "composite" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "composite" - -[features] -no-entrypoint = [] -cpi = ["no-entrypoint"] - -[dependencies] -anchor-lang = { path = "../../../../lang" } diff --git a/tests/composite/programs/composite/Xargo.toml b/tests/composite/programs/composite/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/composite/programs/composite/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/composite/programs/composite/src/lib.rs b/tests/composite/programs/composite/src/lib.rs deleted file mode 100644 index 770b4308..00000000 --- a/tests/composite/programs/composite/src/lib.rs +++ /dev/null @@ -1,64 +0,0 @@ -//! This example demonstrates the ability to compose together multiple -//! structs deriving `Accounts`. See `CompositeUpdate`, below. - -use anchor_lang::prelude::*; - -declare_id!("EHthziFziNoac9LBGxEaVN47Y3uUiRoXvqAiR6oes4iU"); - -#[program] -mod composite { - use super::*; - pub fn initialize(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn composite_update( - ctx: Context, - dummy_a: u64, - dummy_b: u64, - ) -> Result<()> { - let a = &mut ctx.accounts.foo.dummy_a; - let b = &mut ctx.accounts.bar.dummy_b; - - a.data = dummy_a; - b.data = dummy_b; - - Ok(()) - } -} - -#[derive(Accounts)] -pub struct Initialize<'info> { - #[account(zero)] - pub dummy_a: Account<'info, DummyA>, - #[account(zero)] - pub dummy_b: Account<'info, DummyB>, -} - -#[derive(Accounts)] -pub struct CompositeUpdate<'info> { - foo: Foo<'info>, - bar: Bar<'info>, -} - -#[derive(Accounts)] -pub struct Foo<'info> { - #[account(mut)] - pub dummy_a: Account<'info, DummyA>, -} - -#[derive(Accounts)] -pub struct Bar<'info> { - #[account(mut)] - pub dummy_b: Account<'info, DummyB>, -} - -#[account] -pub struct DummyA { - pub data: u64, -} - -#[account] -pub struct DummyB { - pub data: u64, -} diff --git a/tests/composite/tests/composite.js b/tests/composite/tests/composite.js deleted file mode 100644 index e7c3a76b..00000000 --- a/tests/composite/tests/composite.js +++ /dev/null @@ -1,50 +0,0 @@ -const { assert } = require("chai"); -const anchor = require("@project-serum/anchor"); - -describe("composite", () => { - const provider = anchor.AnchorProvider.local(); - - // Configure the client to use the local cluster. - anchor.setProvider(provider); - - it("Is initialized!", async () => { - const program = anchor.workspace.Composite; - - const dummyA = anchor.web3.Keypair.generate(); - const dummyB = anchor.web3.Keypair.generate(); - - const tx = await program.rpc.initialize({ - accounts: { - dummyA: dummyA.publicKey, - dummyB: dummyB.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [dummyA, dummyB], - instructions: [ - await program.account.dummyA.createInstruction(dummyA), - await program.account.dummyB.createInstruction(dummyB), - ], - }); - - await program.rpc.compositeUpdate( - new anchor.BN(1234), - new anchor.BN(4321), - { - accounts: { - foo: { - dummyA: dummyA.publicKey, - }, - bar: { - dummyB: dummyB.publicKey, - }, - }, - } - ); - - const dummyAAccount = await program.account.dummyA.fetch(dummyA.publicKey); - const dummyBAccount = await program.account.dummyB.fetch(dummyB.publicKey); - - assert.isTrue(dummyAAccount.data.eq(new anchor.BN(1234))); - assert.isTrue(dummyBAccount.data.eq(new anchor.BN(4321))); - }); -}); diff --git a/tests/cpi-returns/.gitignore b/tests/cpi-returns/.gitignore deleted file mode 100644 index 51448d4d..00000000 --- a/tests/cpi-returns/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ - -.anchor -.DS_Store -target -**/*.rs.bk -node_modules diff --git a/tests/cpi-returns/Anchor.toml b/tests/cpi-returns/Anchor.toml deleted file mode 100644 index 99bb4acb..00000000 --- a/tests/cpi-returns/Anchor.toml +++ /dev/null @@ -1,16 +0,0 @@ -[features] -seeds = false - -[programs.localnet] -callee = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" -caller = "HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L" - -[registry] -url = "https://anchor.projectserum.com" - -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[scripts] -test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" diff --git a/tests/cpi-returns/Cargo.toml b/tests/cpi-returns/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/cpi-returns/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/cpi-returns/migrations/deploy.ts b/tests/cpi-returns/migrations/deploy.ts deleted file mode 100644 index 5e3df0dc..00000000 --- a/tests/cpi-returns/migrations/deploy.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Migrations are an early feature. Currently, they're nothing more than this -// single deploy script that's invoked from the CLI, injecting a provider -// configured from the workspace's Anchor.toml. - -const anchor = require("@project-serum/anchor"); - -module.exports = async function (provider) { - // Configure client to use the provider. - anchor.setProvider(provider); - - // Add your deploy script here. -}; diff --git a/tests/cpi-returns/package.json b/tests/cpi-returns/package.json deleted file mode 100644 index 8ef4ee66..00000000 --- a/tests/cpi-returns/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "cpi-returns", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor run test-with-build" - } -} diff --git a/tests/cpi-returns/programs/callee/Cargo.toml b/tests/cpi-returns/programs/callee/Cargo.toml deleted file mode 100644 index c964a4aa..00000000 --- a/tests/cpi-returns/programs/callee/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "callee" -version = "0.1.0" -description = "Created with Anchor" -edition = "2018" - -[lib] -crate-type = ["cdylib", "lib"] -name = "callee" - -[features] -no-entrypoint = [] -no-idl = [] -no-log-ix-name = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] } diff --git a/tests/cpi-returns/programs/callee/Xargo.toml b/tests/cpi-returns/programs/callee/Xargo.toml deleted file mode 100644 index 475fb71e..00000000 --- a/tests/cpi-returns/programs/callee/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] diff --git a/tests/cpi-returns/programs/callee/src/lib.rs b/tests/cpi-returns/programs/callee/src/lib.rs deleted file mode 100644 index 611e0a74..00000000 --- a/tests/cpi-returns/programs/callee/src/lib.rs +++ /dev/null @@ -1,57 +0,0 @@ -use anchor_lang::prelude::*; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -pub mod callee { - use super::*; - - #[derive(AnchorSerialize, AnchorDeserialize)] - pub struct StructReturn { - pub value: u64, - } - - pub fn initialize(ctx: Context) -> Result<()> { - let account = &mut ctx.accounts.account; - account.value = 10; - Ok(()) - } - - pub fn return_u64(_ctx: Context) -> Result { - Ok(10) - } - - pub fn return_struct(_ctx: Context) -> Result { - let s = StructReturn { value: 11 }; - Ok(s) - } - - pub fn return_vec(_ctx: Context) -> Result> { - Ok(vec![12, 13, 14, 100]) - } - - // Used for testing views - pub fn return_u64_from_account(ctx: Context) -> Result { - let account = &ctx.accounts.account; - Ok(account.value) - } -} - -#[derive(Accounts)] -pub struct Initialize<'info> { - #[account(init, payer = user, space = 8 + 8)] - pub account: Account<'info, CpiReturnAccount>, - #[account(mut)] - pub user: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct CpiReturn<'info> { - pub account: Account<'info, CpiReturnAccount>, -} - -#[account] -pub struct CpiReturnAccount { - pub value: u64, -} diff --git a/tests/cpi-returns/programs/caller/Cargo.toml b/tests/cpi-returns/programs/caller/Cargo.toml deleted file mode 100644 index 2aaa5510..00000000 --- a/tests/cpi-returns/programs/caller/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "caller" -version = "0.1.0" -description = "Created with Anchor" -edition = "2018" - -[lib] -crate-type = ["cdylib", "lib"] -name = "caller" - -[features] -no-entrypoint = [] -no-idl = [] -no-log-ix-name = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] } -callee = { path = "../callee", features = ["cpi"] } diff --git a/tests/cpi-returns/programs/caller/Xargo.toml b/tests/cpi-returns/programs/caller/Xargo.toml deleted file mode 100644 index 475fb71e..00000000 --- a/tests/cpi-returns/programs/caller/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] diff --git a/tests/cpi-returns/programs/caller/src/lib.rs b/tests/cpi-returns/programs/caller/src/lib.rs deleted file mode 100644 index ff093e97..00000000 --- a/tests/cpi-returns/programs/caller/src/lib.rs +++ /dev/null @@ -1,75 +0,0 @@ -use anchor_lang::prelude::*; -use callee::cpi::accounts::CpiReturn; -use callee::program::Callee; -use callee::{self, CpiReturnAccount}; - -declare_id!("HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L"); - -#[program] -pub mod caller { - use super::*; - - #[derive(AnchorSerialize, AnchorDeserialize)] - pub struct Struct { - pub a: u64, - pub b: u64, - } - - pub fn cpi_call_return_u64(ctx: Context) -> Result<()> { - let cpi_program = ctx.accounts.cpi_return_program.to_account_info(); - let cpi_accounts = CpiReturn { - account: ctx.accounts.cpi_return.to_account_info(), - }; - let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); - let result = callee::cpi::return_u64(cpi_ctx)?; - let solana_return = result.get(); - anchor_lang::solana_program::log::sol_log_data(&[&solana_return.try_to_vec().unwrap()]); - Ok(()) - } - - pub fn cpi_call_return_struct(ctx: Context) -> Result<()> { - let cpi_program = ctx.accounts.cpi_return_program.to_account_info(); - let cpi_accounts = CpiReturn { - account: ctx.accounts.cpi_return.to_account_info(), - }; - let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); - let result = callee::cpi::return_struct(cpi_ctx)?; - let solana_return = result.get(); - anchor_lang::solana_program::log::sol_log_data(&[&solana_return.try_to_vec().unwrap()]); - Ok(()) - } - - pub fn cpi_call_return_vec(ctx: Context) -> Result<()> { - let cpi_program = ctx.accounts.cpi_return_program.to_account_info(); - let cpi_accounts = CpiReturn { - account: ctx.accounts.cpi_return.to_account_info(), - }; - let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); - let result = callee::cpi::return_vec(cpi_ctx)?; - let solana_return = result.get(); - anchor_lang::solana_program::log::sol_log_data(&[&solana_return.try_to_vec().unwrap()]); - Ok(()) - } - - pub fn return_u64(ctx: Context) -> Result { - Ok(99) - } - - pub fn return_struct(ctx: Context) -> Result { - Ok(Struct { a: 1, b: 2 }) - } - - pub fn return_vec(ctx: Context) -> Result> { - Ok(vec![1, 2, 3]) - } -} - -#[derive(Accounts)] -pub struct CpiReturnContext<'info> { - #[account(mut)] - pub cpi_return: Account<'info, CpiReturnAccount>, - pub cpi_return_program: Program<'info, Callee>, -} - -#[derive(Accounts)] -pub struct ReturnContext {} diff --git a/tests/cpi-returns/tests/cpi-return.ts b/tests/cpi-returns/tests/cpi-return.ts deleted file mode 100644 index 48bdc3a4..00000000 --- a/tests/cpi-returns/tests/cpi-return.ts +++ /dev/null @@ -1,220 +0,0 @@ -import assert from "assert"; -import * as anchor from "@project-serum/anchor"; -import * as borsh from "borsh"; -import { Program } from "@project-serum/anchor"; -import { Callee } from "../target/types/callee"; -import { Caller } from "../target/types/caller"; - -const { SystemProgram } = anchor.web3; - -describe("CPI return", () => { - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const callerProgram = anchor.workspace.Caller as Program; - const calleeProgram = anchor.workspace.Callee as Program; - - const getReturnLog = (confirmedTransaction) => { - const prefix = "Program return: "; - let log = confirmedTransaction.meta.logMessages.find((log) => - log.startsWith(prefix) - ); - log = log.slice(prefix.length); - const [key, data] = log.split(" ", 2); - const buffer = Buffer.from(data, "base64"); - return [key, data, buffer]; - }; - - const cpiReturn = anchor.web3.Keypair.generate(); - - const confirmOptions = { commitment: "confirmed" }; - - it("can initialize", async () => { - await calleeProgram.methods - .initialize() - .accounts({ - account: cpiReturn.publicKey, - user: provider.wallet.publicKey, - systemProgram: SystemProgram.programId, - }) - .signers([cpiReturn]) - .rpc(); - }); - - it("can return u64 from a cpi", async () => { - const tx = await callerProgram.methods - .cpiCallReturnU64() - .accounts({ - cpiReturn: cpiReturn.publicKey, - cpiReturnProgram: calleeProgram.programId, - }) - .rpc(confirmOptions); - let t = await provider.connection.getTransaction(tx, { - commitment: "confirmed", - }); - - const [key, data, buffer] = getReturnLog(t); - assert.equal(key, calleeProgram.programId); - - // Check for matching log on receive side - let receiveLog = t.meta.logMessages.find( - (log) => log == `Program data: ${data}` - ); - assert(receiveLog !== undefined); - - const reader = new borsh.BinaryReader(buffer); - assert.equal(reader.readU64().toNumber(), 10); - }); - - it("can make a non-cpi call to a function that returns a u64", async () => { - const tx = await calleeProgram.methods - .returnU64() - .accounts({ - account: cpiReturn.publicKey, - }) - .rpc(confirmOptions); - let t = await provider.connection.getTransaction(tx, { - commitment: "confirmed", - }); - const [key, , buffer] = getReturnLog(t); - assert.equal(key, calleeProgram.programId); - const reader = new borsh.BinaryReader(buffer); - assert.equal(reader.readU64().toNumber(), 10); - }); - - it("can return a struct from a cpi", async () => { - const tx = await callerProgram.methods - .cpiCallReturnStruct() - .accounts({ - cpiReturn: cpiReturn.publicKey, - cpiReturnProgram: calleeProgram.programId, - }) - .rpc(confirmOptions); - let t = await provider.connection.getTransaction(tx, { - commitment: "confirmed", - }); - - const [key, data, buffer] = getReturnLog(t); - assert.equal(key, calleeProgram.programId); - - // Check for matching log on receive side - let receiveLog = t.meta.logMessages.find( - (log) => log == `Program data: ${data}` - ); - assert(receiveLog !== undefined); - - // Deserialize the struct and validate - class Assignable { - constructor(properties) { - Object.keys(properties).map((key) => { - this[key] = properties[key]; - }); - } - } - class Data extends Assignable {} - const schema = new Map([ - [Data, { kind: "struct", fields: [["value", "u64"]] }], - ]); - const deserialized = borsh.deserialize(schema, Data, buffer); - assert(deserialized.value.toNumber() === 11); - }); - - it("can return a vec from a cpi", async () => { - const tx = await callerProgram.methods - .cpiCallReturnVec() - .accounts({ - cpiReturn: cpiReturn.publicKey, - cpiReturnProgram: calleeProgram.programId, - }) - .rpc(confirmOptions); - let t = await provider.connection.getTransaction(tx, { - commitment: "confirmed", - }); - - const [key, data, buffer] = getReturnLog(t); - assert.equal(key, calleeProgram.programId); - - // Check for matching log on receive side - let receiveLog = t.meta.logMessages.find( - (log) => log == `Program data: ${data}` - ); - assert(receiveLog !== undefined); - - const reader = new borsh.BinaryReader(buffer); - const array = reader.readArray(() => reader.readU8()); - assert.deepStrictEqual(array, [12, 13, 14, 100]); - }); - - it("sets a return value in idl", async () => { - const returnu64Instruction = calleeProgram._idl.instructions.find( - (f) => f.name == "returnU64" - ); - assert.equal(returnu64Instruction.returns, "u64"); - - const returnStructInstruction = calleeProgram._idl.instructions.find( - (f) => f.name == "returnStruct" - ); - assert.deepStrictEqual(returnStructInstruction.returns, { - defined: "StructReturn", - }); - }); - - it("can return a u64 via view", async () => { - assert(new anchor.BN(99).eq(await callerProgram.views.returnU64())); - // Via methods API - assert( - new anchor.BN(99).eq(await callerProgram.methods.returnU64().view()) - ); - }); - - it("can return a struct via view", async () => { - const struct = await callerProgram.views.returnStruct(); - assert(struct.a.eq(new anchor.BN(1))); - assert(struct.b.eq(new anchor.BN(2))); - // Via methods API - const struct2 = await callerProgram.methods.returnStruct().view(); - assert(struct2.a.eq(new anchor.BN(1))); - assert(struct2.b.eq(new anchor.BN(2))); - }); - - it("can return a vec via view", async () => { - const vec = await callerProgram.views.returnVec(); - assert(vec[0].eq(new anchor.BN(1))); - assert(vec[1].eq(new anchor.BN(2))); - assert(vec[2].eq(new anchor.BN(3))); - // Via methods API - const vec2 = await callerProgram.methods.returnVec().view(); - assert(vec2[0].eq(new anchor.BN(1))); - assert(vec2[1].eq(new anchor.BN(2))); - assert(vec2[2].eq(new anchor.BN(3))); - }); - - it("can return a u64 from an account via view", async () => { - const value = new anchor.BN(10); - assert( - value.eq( - await calleeProgram.methods - .returnU64FromAccount() - .accounts({ account: cpiReturn.publicKey }) - .view() - ) - ); - }); - - it("cant call view on mutable instruction", async () => { - assert.equal(calleeProgram.views.initialize, undefined); - try { - await calleeProgram.methods - .initialize() - .accounts({ - account: cpiReturn.publicKey, - user: provider.wallet.publicKey, - systemProgram: SystemProgram.programId, - }) - .signers([cpiReturn]) - .view(); - } catch (e) { - assert(e.message.includes("Method does not support views")); - } - }); -}); diff --git a/tests/cpi-returns/tsconfig.json b/tests/cpi-returns/tsconfig.json deleted file mode 100644 index cd5d2e3d..00000000 --- a/tests/cpi-returns/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "types": ["mocha", "chai"], - "typeRoots": ["./node_modules/@types"], - "lib": ["es2015"], - "module": "commonjs", - "target": "es6", - "esModuleInterop": true - } -} diff --git a/tests/custom-coder/Anchor.toml b/tests/custom-coder/Anchor.toml deleted file mode 100644 index 6ea244ca..00000000 --- a/tests/custom-coder/Anchor.toml +++ /dev/null @@ -1,15 +0,0 @@ -[programs.localnet] -custom_coder = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" -spl_token = "FmpfPa1LHEYRbueNMnwNVd2JvyQ89GXGWdyZEXNNKV8w" - -[registry] -url = "https://anchor.projectserum.com" - -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[scripts] -test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" - -[features] diff --git a/tests/custom-coder/Cargo.toml b/tests/custom-coder/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/custom-coder/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/custom-coder/migrations/deploy.ts b/tests/custom-coder/migrations/deploy.ts deleted file mode 100644 index 5e3df0dc..00000000 --- a/tests/custom-coder/migrations/deploy.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Migrations are an early feature. Currently, they're nothing more than this -// single deploy script that's invoked from the CLI, injecting a provider -// configured from the workspace's Anchor.toml. - -const anchor = require("@project-serum/anchor"); - -module.exports = async function (provider) { - // Configure client to use the provider. - anchor.setProvider(provider); - - // Add your deploy script here. -}; diff --git a/tests/custom-coder/package.json b/tests/custom-coder/package.json deleted file mode 100644 index 802bc74d..00000000 --- a/tests/custom-coder/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "custom-coder", - "version": "0.20.0", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/custom-coder/programs/custom-coder/Cargo.toml b/tests/custom-coder/programs/custom-coder/Cargo.toml deleted file mode 100644 index 181ad5e2..00000000 --- a/tests/custom-coder/programs/custom-coder/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "custom-coder" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "custom_coder" - -[features] -no-entrypoint = [] -no-idl = [] -no-log-ix-name = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = "0.20.0" diff --git a/tests/custom-coder/programs/custom-coder/Xargo.toml b/tests/custom-coder/programs/custom-coder/Xargo.toml deleted file mode 100644 index 475fb71e..00000000 --- a/tests/custom-coder/programs/custom-coder/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] diff --git a/tests/custom-coder/programs/custom-coder/src/lib.rs b/tests/custom-coder/programs/custom-coder/src/lib.rs deleted file mode 100644 index fa18cb41..00000000 --- a/tests/custom-coder/programs/custom-coder/src/lib.rs +++ /dev/null @@ -1,14 +0,0 @@ -use anchor_lang::prelude::*; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -pub mod custom_coder { - use super::*; - pub fn initialize(_ctx: Context, a: Option) -> ProgramResult { - Ok(()) - } -} - -#[derive(Accounts)] -pub struct Initialize {} diff --git a/tests/custom-coder/programs/spl-token/Cargo.toml b/tests/custom-coder/programs/spl-token/Cargo.toml deleted file mode 100644 index 925d3d55..00000000 --- a/tests/custom-coder/programs/spl-token/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "spl-token" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "spl_token" - -[features] -no-entrypoint = [] -no-idl = [] -no-log-ix-name = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = "0.20.0" diff --git a/tests/custom-coder/programs/spl-token/Xargo.toml b/tests/custom-coder/programs/spl-token/Xargo.toml deleted file mode 100644 index 475fb71e..00000000 --- a/tests/custom-coder/programs/spl-token/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] diff --git a/tests/custom-coder/programs/spl-token/src/lib.rs b/tests/custom-coder/programs/spl-token/src/lib.rs deleted file mode 100644 index fdd834c3..00000000 --- a/tests/custom-coder/programs/spl-token/src/lib.rs +++ /dev/null @@ -1,296 +0,0 @@ -use anchor_lang::prelude::*; - -declare_id!("FmpfPa1LHEYRbueNMnwNVd2JvyQ89GXGWdyZEXNNKV8w"); - -// This program is simply used to generate the IDL for the token program. -// -// Note that we manually add the COption type to the IDL after -// compiling. -// -#[program] -pub mod spl_token { - use super::*; - - pub fn initialize_mint( - ctx: Context, - decimals: u8, - mint_authority: Pubkey, - // freeze_authority: COption, - ) -> ProgramResult { - Ok(()) - } - - pub fn initialize_account(ctx: Context) -> ProgramResult { - Ok(()) - } - - pub fn initialize_multisig(ctx: Context, m: u8) -> ProgramResult { - Ok(()) - } - - pub fn transfer(ctx: Context, amount: u64) -> ProgramResult { - Ok(()) - } - - pub fn approve(ctx: Context, amount: u64) -> ProgramResult { - Ok(()) - } - - pub fn revoke(ctx: Context) -> ProgramResult { - Ok(()) - } - - pub fn set_authority( - ctx: Context, - authority_type: u8, - // new_authority: COption, - ) -> ProgramResult { - Ok(()) - } - - pub fn mint_to(ctx: Context, amount: u64) -> ProgramResult { - Ok(()) - } - - pub fn burn(ctx: Context, amount: u64) -> ProgramResult { - Ok(()) - } - - pub fn close_account(ctx: Context) -> ProgramResult { - Ok(()) - } - - pub fn freeze_account(ctx: Context) -> ProgramResult { - Ok(()) - } - - pub fn thaw_account(ctx: Context) -> ProgramResult { - Ok(()) - } - - pub fn transfer_checked( - ctx: Context, - amount: u64, - decimals: u8, - ) -> ProgramResult { - Ok(()) - } - - pub fn approve_checked( - ctx: Context, - amount: u64, - decimals: u8, - ) -> ProgramResult { - Ok(()) - } - - pub fn mint_to_checked( - ctx: Context, - amount: u64, - decimals: u8, - ) -> ProgramResult { - Ok(()) - } - - pub fn burn_checked(ctx: Context, amount: u64, decimals: u8) -> ProgramResult { - Ok(()) - } - - pub fn initialize_account_2( - ctx: Context, - authority: Pubkey, - ) -> ProgramResult { - Ok(()) - } - - pub fn sync_native(ctx: Context) -> ProgramResult { - Ok(()) - } - - pub fn initialize_account3( - ctx: Context, - authority: Pubkey, - ) -> ProgramResult { - Ok(()) - } - - pub fn initialize_multisig_2(ctx: Context, m: u8) -> ProgramResult { - Ok(()) - } - - pub fn initialize_mint_2( - ctx: Context, - decimals: u8, - mint_authority: Pubkey, - // freeze_authority: COption, - ) -> ProgramResult { - Ok(()) - } -} - -#[derive(Accounts)] -pub struct InitializeMint<'info> { - #[account(mut)] - mint: AccountInfo<'info>, - rent: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct InitializeAccount<'info> { - #[account(mut)] - account: AccountInfo<'info>, - mint: AccountInfo<'info>, - authority: AccountInfo<'info>, - rent: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct InitializeMultisig<'info> { - #[account(mut)] - account: AccountInfo<'info>, - rent: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct Transfer<'info> { - #[account(mut)] - source: AccountInfo<'info>, - #[account(mut)] - destination: AccountInfo<'info>, - authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct Approve<'info> { - #[account(mut)] - source: AccountInfo<'info>, - delegate: AccountInfo<'info>, - authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct Revoke<'info> { - #[account(mut)] - source: AccountInfo<'info>, - authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct SetAuthority<'info> { - #[account(mut)] - pub mint: AccountInfo<'info>, - pub authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct MintTo<'info> { - #[account(mut)] - pub mint: AccountInfo<'info>, - #[account(mut)] - pub to: AccountInfo<'info>, - pub authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct Burn<'info> { - #[account(mut)] - source: AccountInfo<'info>, - #[account(mut)] - mint: AccountInfo<'info>, - authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct CloseAccount<'info> { - #[account(mut)] - account: AccountInfo<'info>, - #[account(mut)] - destination: AccountInfo<'info>, - authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct FreezeAccount<'info> { - #[account(mut)] - account: AccountInfo<'info>, - mint: AccountInfo<'info>, - authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct ThawAccount<'info> { - #[account(mut)] - account: AccountInfo<'info>, - mint: AccountInfo<'info>, - authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct TransferChecked<'info> { - #[account(mut)] - source: AccountInfo<'info>, - mint: AccountInfo<'info>, - #[account(mut)] - destination: AccountInfo<'info>, - authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct ApproveChecked<'info> { - #[account(mut)] - source: AccountInfo<'info>, - mint: AccountInfo<'info>, - delegate: AccountInfo<'info>, - authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct MintToChecked<'info> { - #[account(mut)] - mint: AccountInfo<'info>, - #[account(mut)] - to: AccountInfo<'info>, - authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct BurnChecked<'info> { - #[account(mut)] - source: AccountInfo<'info>, - #[account(mut)] - mint: AccountInfo<'info>, - authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct InitializeAccount2<'info> { - #[account(mut)] - account: AccountInfo<'info>, - mint: AccountInfo<'info>, - rent: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct SyncNative<'info> { - #[account(mut)] - account: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct InitializeAccount3<'info> { - #[account(mut)] - account: AccountInfo<'info>, - mint: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct InitializeMultisig2<'info> { - #[account(mut)] - account: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct InitializeMint2<'info> { - #[account(mut)] - mint: AccountInfo<'info>, -} diff --git a/tests/custom-coder/tests/custom-coder.ts b/tests/custom-coder/tests/custom-coder.ts deleted file mode 100644 index 6777d673..00000000 --- a/tests/custom-coder/tests/custom-coder.ts +++ /dev/null @@ -1,137 +0,0 @@ -import * as anchor from "@project-serum/anchor"; -import { Spl } from "@project-serum/anchor"; -import { assert } from "chai"; -import BN from "bn.js"; -import { Keypair, SYSVAR_RENT_PUBKEY } from "@solana/web3.js"; - -describe("custom-coder", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.env()); - - // Client. - const program = Spl.token(); - - // Constants. - const mintKeypair = Keypair.generate(); - const aliceTokenKeypair = Keypair.generate(); - const bobTokenKeypair = Keypair.generate(); - const rent = SYSVAR_RENT_PUBKEY; - - it("Creates a mint", async () => { - await program.rpc.initializeMint( - 6, - program.provider.wallet.publicKey, - null, - { - accounts: { - mint: mintKeypair.publicKey, - rent, - }, - signers: [mintKeypair], - preInstructions: [ - await program.account.mint.createInstruction(mintKeypair), - ], - } - ); - const mintAccount = await program.account.mint.fetch(mintKeypair.publicKey); - assert.isTrue( - mintAccount.mintAuthority.equals(program.provider.wallet.publicKey) - ); - assert.isNull(mintAccount.freezeAuthority); - assert.strictEqual(mintAccount.decimals, 6); - assert.isTrue(mintAccount.isInitialized); - assert.strictEqual(mintAccount.supply.toNumber(), 0); - }); - - it("Creates a token account for alice", async () => { - await program.rpc.initializeAccount({ - accounts: { - account: aliceTokenKeypair.publicKey, - mint: mintKeypair.publicKey, - authority: program.provider.wallet.publicKey, - rent, - }, - signers: [aliceTokenKeypair], - preInstructions: [ - await program.account.token.createInstruction(aliceTokenKeypair), - ], - }); - const token = await program.account.token.fetch( - aliceTokenKeypair.publicKey - ); - assert.isTrue(token.authority.equals(program.provider.wallet.publicKey)); - assert.isTrue(token.mint.equals(mintKeypair.publicKey)); - assert.strictEqual(token.amount.toNumber(), 0); - assert.isNull(token.delegate); - assert.strictEqual(token.state, 1); - assert.isNull(token.isNative); - assert.strictEqual(token.delegatedAmount.toNumber(), 0); - assert.isNull(token.closeAuthority); - }); - - it("Mints a token to alice", async () => { - await program.rpc.mintTo(new BN(2), { - accounts: { - mint: mintKeypair.publicKey, - to: aliceTokenKeypair.publicKey, - authority: program.provider.wallet.publicKey, - }, - }); - - const token = await program.account.token.fetch( - aliceTokenKeypair.publicKey - ); - const mint = await program.account.mint.fetch(mintKeypair.publicKey); - assert.strictEqual(token.amount.toNumber(), 2); - assert.strictEqual(mint.supply.toNumber(), 2); - }); - - it("Creates a token for bob", async () => { - await program.rpc.initializeAccount({ - accounts: { - account: bobTokenKeypair.publicKey, - mint: mintKeypair.publicKey, - authority: program.provider.wallet.publicKey, - rent, - }, - signers: [bobTokenKeypair], - preInstructions: [ - await program.account.token.createInstruction(bobTokenKeypair), - ], - }); - }); - - it("Transfer a token from alice to bob", async () => { - await program.rpc.transfer(new BN(1), { - accounts: { - source: aliceTokenKeypair.publicKey, - destination: bobTokenKeypair.publicKey, - authority: program.provider.wallet.publicKey, - }, - }); - const aliceToken = await program.account.token.fetch( - aliceTokenKeypair.publicKey - ); - const bobToken = await program.account.token.fetch( - bobTokenKeypair.publicKey - ); - assert.strictEqual(aliceToken.amount.toNumber(), 1); - assert.strictEqual(bobToken.amount.toNumber(), 1); - }); - - it("Alice burns a token", async () => { - await program.rpc.burn(new BN(1), { - accounts: { - source: aliceTokenKeypair.publicKey, - mint: mintKeypair.publicKey, - authority: program.provider.wallet.publicKey, - }, - }); - const aliceToken = await program.account.token.fetch( - aliceTokenKeypair.publicKey - ); - const mint = await program.account.mint.fetch(mintKeypair.publicKey); - assert.strictEqual(aliceToken.amount.toNumber(), 0); - assert.strictEqual(mint.supply.toNumber(), 1); - }); -}); diff --git a/tests/custom-coder/tsconfig.json b/tests/custom-coder/tsconfig.json deleted file mode 100644 index cd5d2e3d..00000000 --- a/tests/custom-coder/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "types": ["mocha", "chai"], - "typeRoots": ["./node_modules/@types"], - "lib": ["es2015"], - "module": "commonjs", - "target": "es6", - "esModuleInterop": true - } -} diff --git a/tests/declare-id/Anchor.toml b/tests/declare-id/Anchor.toml deleted file mode 100644 index b933d629..00000000 --- a/tests/declare-id/Anchor.toml +++ /dev/null @@ -1,9 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -declare_id = "FJcF5c8HncdfAgjPjTH49GAEypkJCG2ZADh2xhduNi5B" - -[scripts] -test = "yarn run ts-mocha -t 1000000 tests/*.ts" diff --git a/tests/declare-id/Cargo.toml b/tests/declare-id/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/declare-id/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/declare-id/package.json b/tests/declare-id/package.json deleted file mode 100644 index d6abadf1..00000000 --- a/tests/declare-id/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "declare-id", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/declare-id/programs/declare-id/Cargo.toml b/tests/declare-id/programs/declare-id/Cargo.toml deleted file mode 100644 index 0bbbab61..00000000 --- a/tests/declare-id/programs/declare-id/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "declare-id" -version = "0.1.0" -description = "Created with Anchor" -edition = "2018" - -[lib] -crate-type = ["cdylib", "lib"] -name = "declare_id" - -[features] -no-entrypoint = [] -cpi = ["no-entrypoint"] - -[dependencies] -anchor-lang = { path = "../../../../lang" } diff --git a/tests/declare-id/programs/declare-id/Xargo.toml b/tests/declare-id/programs/declare-id/Xargo.toml deleted file mode 100644 index 475fb71e..00000000 --- a/tests/declare-id/programs/declare-id/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] diff --git a/tests/declare-id/programs/declare-id/src/lib.rs b/tests/declare-id/programs/declare-id/src/lib.rs deleted file mode 100644 index dba4a849..00000000 --- a/tests/declare-id/programs/declare-id/src/lib.rs +++ /dev/null @@ -1,17 +0,0 @@ -use anchor_lang::prelude::*; - -// Intentionally different program id than the one defined in Anchor.toml. -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -mod declare_id { - use super::*; - - pub fn initialize(_ctx: Context) -> Result<()> { - Ok(()) - } -} - -#[derive(Accounts)] -pub struct Initialize { -} diff --git a/tests/declare-id/tests/declare-id.ts b/tests/declare-id/tests/declare-id.ts deleted file mode 100644 index 4e0a206e..00000000 --- a/tests/declare-id/tests/declare-id.ts +++ /dev/null @@ -1,21 +0,0 @@ -import * as anchor from "@project-serum/anchor"; -import { AnchorError, Program } from "@project-serum/anchor"; -import splToken from "@solana/spl-token"; -import { DeclareId } from "../target/types/declare_id"; -import { assert } from "chai"; - -describe("declare_id", () => { - anchor.setProvider(anchor.AnchorProvider.local()); - const program = anchor.workspace.DeclareId as Program; - - it("throws error!", async () => { - try { - await program.rpc.initialize(); - assert.ok(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 4100); - } - }); -}); diff --git a/tests/declare-id/tsconfig.json b/tests/declare-id/tsconfig.json deleted file mode 100644 index 70c39f55..00000000 --- a/tests/declare-id/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "types": ["mocha", "chai", "node"], - "typeRoots": ["./node_modules/@types"], - "lib": ["es2015"], - "module": "commonjs", - "target": "es6", - "esModuleInterop": true - } -} diff --git a/tests/docs/Anchor.toml b/tests/docs/Anchor.toml deleted file mode 100644 index 82be99c6..00000000 --- a/tests/docs/Anchor.toml +++ /dev/null @@ -1,11 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -errors = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[scripts] -test = "yarn run mocha -t 1000000 tests/" - -[features] diff --git a/tests/docs/Cargo.toml b/tests/docs/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/docs/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/docs/package.json b/tests/docs/package.json deleted file mode 100644 index 03fe666e..00000000 --- a/tests/docs/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "errors", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/docs/programs/docs/Cargo.toml b/tests/docs/programs/docs/Cargo.toml deleted file mode 100644 index debe6017..00000000 --- a/tests/docs/programs/docs/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "docs" -version = "0.1.0" -description = "Created with Anchor" -edition = "2018" - -[lib] -crate-type = ["cdylib", "lib"] -name = "errors" - -[features] -no-entrypoint = [] -cpi = ["no-entrypoint"] - -[dependencies] -anchor-lang = { path = "../../../../lang" } diff --git a/tests/docs/programs/docs/Xargo.toml b/tests/docs/programs/docs/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/docs/programs/docs/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/docs/programs/docs/src/lib.rs b/tests/docs/programs/docs/src/lib.rs deleted file mode 100644 index 710a2fe0..00000000 --- a/tests/docs/programs/docs/src/lib.rs +++ /dev/null @@ -1,50 +0,0 @@ -//! This example enforces the missing documentation lint. -#![deny(missing_docs)] - -use anchor_lang::prelude::*; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -/// Program for testing that the `missing_docs` lint can be applied. -#[program] -mod docs { - use super::*; - - /// Hello. - pub fn hello(_ctx: Context) -> Result<()> { - err!(MyError::Hello) - } -} - -/// Hello accounts. -#[derive(Accounts)] -pub struct Hello<'info> { - /// Rent sysvar. - /// Multi line docs. - pub rent: Sysvar<'info, Rent>, - /// Composite accounts test. - /// Multiple lines supported. - /// You can also include "double quotes". - pub other: HelloComposite<'info>, -} - -/// Hello accounts. -#[derive(Accounts)] -pub struct HelloComposite<'info> { - /// Rent sysvar 2. - pub rent2: Sysvar<'info, Rent>, -} - -/// MyError. -#[error_code] -pub enum MyError { - /// test - #[msg("This is an error message clients will automatically display")] - Hello, - /// test2 - HelloNoMsg = 123, - /// test3 - HelloNext, - /// test4 - HelloCustom, -} diff --git a/ts/tests/error.spec.ts b/tests/error.spec.ts similarity index 100% rename from ts/tests/error.spec.ts rename to tests/error.spec.ts diff --git a/tests/errors/Anchor.toml b/tests/errors/Anchor.toml deleted file mode 100644 index 6a6719b9..00000000 --- a/tests/errors/Anchor.toml +++ /dev/null @@ -1,12 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -errors = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[scripts] -test = "yarn run ts-mocha -t 1000000 tests/*.ts" - -[features] -seeds = false diff --git a/tests/errors/Cargo.toml b/tests/errors/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/errors/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/errors/package.json b/tests/errors/package.json deleted file mode 100644 index 03fe666e..00000000 --- a/tests/errors/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "errors", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/errors/programs/errors/Cargo.toml b/tests/errors/programs/errors/Cargo.toml deleted file mode 100644 index 9834876c..00000000 --- a/tests/errors/programs/errors/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "errors" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "errors" - -[features] -no-entrypoint = [] -cpi = ["no-entrypoint"] - -[dependencies] -anchor-lang = { path = "../../../../lang" } diff --git a/tests/errors/programs/errors/Xargo.toml b/tests/errors/programs/errors/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/errors/programs/errors/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/errors/programs/errors/src/lib.rs b/tests/errors/programs/errors/src/lib.rs deleted file mode 100644 index edc4c755..00000000 --- a/tests/errors/programs/errors/src/lib.rs +++ /dev/null @@ -1,214 +0,0 @@ -//! This example demonstrates how custom errors and associated error messsages -//! can be defined and transparently propagated to clients. - -use anchor_lang::prelude::*; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -mod errors { - use super::*; - - pub fn hello(_ctx: Context) -> Result<()> { - err!(MyError::Hello) - } - - pub fn hello_no_msg(_ctx: Context) -> Result<()> { - err!(MyError::HelloNoMsg) - } - - pub fn hello_next(_ctx: Context) -> Result<()> { - err!(MyError::HelloNext) - } - - pub fn test_require(_ctx: Context) -> Result<()> { - require!(false, MyError::Hello); - Ok(()) - } - - pub fn test_err(_ctx: Context) -> Result<()> { - err!(MyError::Hello) - } - - pub fn test_program_error(_ctx: Context) -> Result<()> { - Err(ProgramError::InvalidAccountData.into()) - } - - pub fn test_program_error_with_source(_ctx: Context) -> Result<()> { - Err(Error::from(ProgramError::InvalidAccountData).with_source(source!())) - } - - pub fn mut_error(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn has_one_error(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn signer_error(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn raw_custom_error(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn account_not_initialized_error(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn account_owned_by_wrong_program_error( - _ctx: Context, - ) -> Result<()> { - Ok(()) - } - - pub fn require_eq(_ctx: Context) -> Result<()> { - require_eq!(5241, 124124124, MyError::ValueMismatch); - Ok(()) - } - - pub fn require_eq_default_error(_ctx: Context) -> Result<()> { - require_eq!(5241, 124124124); - Ok(()) - } - - pub fn require_neq(_ctx: Context) -> Result<()> { - require_neq!(500, 500, MyError::ValueMatch); - Ok(()) - } - - pub fn require_neq_default_error(_ctx: Context) -> Result<()> { - require_neq!(500, 500); - Ok(()) - } - - pub fn require_keys_eq(ctx: Context) -> Result<()> { - require_keys_eq!( - ctx.accounts.some_account.key(), - *ctx.program_id, - MyError::ValueMismatch - ); - Ok(()) - } - - pub fn require_keys_eq_default_error(ctx: Context) -> Result<()> { - require_keys_eq!(ctx.accounts.some_account.key(), *ctx.program_id); - Ok(()) - } - - pub fn require_keys_neq(ctx: Context) -> Result<()> { - require_keys_neq!( - ctx.accounts.some_account.key(), - *ctx.program_id, - MyError::ValueMatch - ); - Ok(()) - } - - pub fn require_keys_neq_default_error(ctx: Context) -> Result<()> { - require_keys_neq!(ctx.accounts.some_account.key(), *ctx.program_id); - Ok(()) - } - - pub fn require_gt(_ctx: Context) -> Result<()> { - require_gt!(5, 10, MyError::ValueLessOrEqual); - Ok(()) - } - - pub fn require_gt_default_error(_ctx: Context) -> Result<()> { - require_gt!(10, 10); - Ok(()) - } - - pub fn require_gte(_ctx: Context) -> Result<()> { - require_gte!(5, 10, MyError::ValueLess); - Ok(()) - } - - pub fn require_gte_default_error(_ctx: Context) -> Result<()> { - require_gte!(5, 10); - Ok(()) - } -} - -#[derive(Accounts)] -pub struct Hello {} - -#[derive(Accounts)] -pub struct MutError<'info> { - #[account(mut)] - my_account: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct HasOneError<'info> { - #[account(zero, has_one = owner)] - my_account: Account<'info, HasOneAccount>, - owner: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct SignerError<'info> { - my_account: Signer<'info>, -} - -#[account] -pub struct HasOneAccount { - owner: Pubkey, -} - -#[derive(Accounts)] -pub struct RawCustomError<'info> { - #[account(constraint = *my_account.key == ID @ MyError::HelloCustom)] - my_account: AccountInfo<'info>, -} - -#[account] -pub struct AnyAccount {} - -#[derive(Accounts)] -pub struct AccountNotInitializedError<'info> { - not_initialized_account: Account<'info, AnyAccount>, -} - -#[derive(Accounts)] -pub struct AccountOwnedByWrongProgramError<'info> { - pub wrong_account: Account<'info, AnyAccount>, -} - -#[derive(Accounts)] -pub struct RequireEq {} - -#[derive(Accounts)] -pub struct RequireNeq {} - -#[derive(Accounts)] -pub struct RequireGt {} - -#[derive(Accounts)] -pub struct RequireGte {} - -#[derive(Accounts)] -pub struct RequireKeysEq<'info> { - pub some_account: UncheckedAccount<'info>, -} - -#[derive(Accounts)] -pub struct RequireKeysNeq<'info> { - pub some_account: UncheckedAccount<'info>, -} - -#[error_code] -pub enum MyError { - #[msg("This is an error message clients will automatically display")] - Hello, - HelloNoMsg = 123, - HelloNext, - HelloCustom, - ValueMismatch, - ValueMatch, - ValueLess, - ValueLessOrEqual, -} diff --git a/tests/errors/tests/errors.ts b/tests/errors/tests/errors.ts deleted file mode 100644 index ffd4a135..00000000 --- a/tests/errors/tests/errors.ts +++ /dev/null @@ -1,625 +0,0 @@ -import * as anchor from "@project-serum/anchor"; -import { - Program, - BN, - IdlAccounts, - AnchorError, - ProgramError, -} from "@project-serum/anchor"; -import { - PublicKey, - Keypair, - SystemProgram, - Transaction, - TransactionInstruction, -} from "@solana/web3.js"; -import { TOKEN_PROGRAM_ID, Token } from "@solana/spl-token"; -import { assert, expect } from "chai"; -import { Errors } from "../target/types/errors"; - -// sleep to allow logs to come in -const sleep = (ms) => - new Promise((resolve) => { - setTimeout(() => resolve(0), ms); - }); - -const withLogTest = async (callback, expectedLogs) => { - let logTestOk = false; - const listener = anchor.getProvider().connection.onLogs( - "all", - (logs) => { - const index = logs.logs.findIndex( - (logLine) => logLine === expectedLogs[0] - ); - if (index === -1) { - console.log("Expected: "); - console.log(expectedLogs); - console.log("Actual: "); - console.log(logs); - } else { - const actualLogs = logs.logs.slice(index, index + expectedLogs.length); - for (let i = 0; i < expectedLogs.length; i++) { - if (actualLogs[i] !== expectedLogs[i]) { - console.log("Expected: "); - console.log(expectedLogs); - console.log("Actual: "); - console.log(logs); - return; - } - } - logTestOk = true; - } - }, - "recent" - ); - try { - await callback(); - } catch (err) { - anchor.getProvider().connection.removeOnLogsListener(listener); - throw err; - } - anchor.getProvider().connection.removeOnLogsListener(listener); - assert.isTrue(logTestOk); -}; - -describe("errors", () => { - // Configure the client to use the local cluster. - const localProvider = anchor.AnchorProvider.local(); - localProvider.opts.skipPreflight = true; - // processed failed tx do not result in AnchorErrors in the client - // because we cannot get logs for them (only through overkill `onLogs`) - localProvider.opts.commitment = "confirmed"; - anchor.setProvider(localProvider); - - const program = anchor.workspace.Errors as Program; - - it("Emits a Hello error", async () => { - await withLogTest(async () => { - try { - const tx = await program.rpc.hello(); - assert.ok(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - const errMsg = - "This is an error message clients will automatically display"; - const fullErrMsg = - "AnchorError thrown in programs/errors/src/lib.rs:13. Error Code: Hello. Error Number: 6000. Error Message: This is an error message clients will automatically display."; - assert.strictEqual(err.toString(), fullErrMsg); - assert.strictEqual(err.error.errorMessage, errMsg); - assert.strictEqual(err.error.errorCode.number, 6000); - assert.strictEqual( - err.program.toString(), - program.programId.toString() - ); - expect(err.error.origin).to.deep.equal({ - file: "programs/errors/src/lib.rs", - line: 13, - }); - } - }, [ - "Program log: AnchorError thrown in programs/errors/src/lib.rs:13. Error Code: Hello. Error Number: 6000. Error Message: This is an error message clients will automatically display.", - ]); - }); - - it("Emits a Hello error via require!", async () => { - try { - const tx = await program.rpc.testRequire(); - assert.ok(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - const errMsg = - "This is an error message clients will automatically display"; - assert.strictEqual(err.error.errorMessage, errMsg); - assert.strictEqual(err.error.errorCode.number, 6000); - assert.strictEqual(err.error.errorCode.code, "Hello"); - } - }); - - it("Emits a Hello error via err!", async () => { - try { - const tx = await program.rpc.testErr(); - assert.ok(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - const errMsg = - "This is an error message clients will automatically display"; - assert.strictEqual(err.error.errorMessage, errMsg); - assert.strictEqual(err.error.errorCode.number, 6000); - } - }); - - it("Logs a ProgramError", async () => { - await withLogTest(async () => { - try { - const tx = await program.rpc.testProgramError(); - assert.ok(false); - } catch (err) { - expect(err.programErrorStack.map((pk) => pk.toString())).to.deep.equal([ - program.programId.toString(), - ]); - expect(err.program.toString()).to.equal(program.programId.toString()); - } - }, [ - "Program log: ProgramError occurred. Error Code: InvalidAccountData. Error Number: 17179869184. Error Message: An account's data contents was invalid.", - ]); - }); - - it("Logs a ProgramError with source", async () => { - await withLogTest(async () => { - try { - const tx = await program.rpc.testProgramErrorWithSource(); - assert.ok(false); - } catch (err) { - expect(err.programErrorStack.map((pk) => pk.toString())).to.deep.equal([ - program.programId.toString(), - ]); - } - }, [ - "Program log: ProgramError thrown in programs/errors/src/lib.rs:38. Error Code: InvalidAccountData. Error Number: 17179869184. Error Message: An account's data contents was invalid.", - ]); - }); - - it("Emits a HelloNoMsg error", async () => { - try { - const tx = await program.rpc.helloNoMsg(); - assert.ok(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorMessage, "HelloNoMsg"); - assert.strictEqual(err.error.errorCode.number, 6123); - } - }); - - it("Emits a HelloNext error", async () => { - try { - const tx = await program.rpc.helloNext(); - assert.ok(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorMessage, "HelloNext"); - assert.strictEqual(err.error.errorCode.number, 6124); - } - }); - - it("Emits a mut error", async () => { - await withLogTest(async () => { - try { - const tx = await program.rpc.mutError({ - accounts: { - myAccount: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - }); - assert.ok(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual( - err.error.errorMessage, - "A mut constraint was violated" - ); - assert.strictEqual(err.error.errorCode.number, 2000); - assert.strictEqual(err.error.origin, "my_account"); - } - }, [ - "Program log: AnchorError caused by account: my_account. Error Code: ConstraintMut. Error Number: 2000. Error Message: A mut constraint was violated.", - ]); - }); - - it("Emits a has one error", async () => { - await withLogTest(async () => { - try { - const account = new Keypair(); - const tx = await program.rpc.hasOneError({ - accounts: { - myAccount: account.publicKey, - owner: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - // this initializes the account.owner variable with Pubkey::default - instructions: [ - await program.account.hasOneAccount.createInstruction(account), - ], - signers: [account], - }); - assert.ok(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual( - err.error.errorMessage, - "A has one constraint was violated" - ); - assert.strictEqual(err.error.errorCode.number, 2001); - assert.strictEqual(err.error.errorCode.code, "ConstraintHasOne"); - assert.strictEqual(err.error.origin, "my_account"); - assert.strictEqual( - err.program.toString(), - program.programId.toString() - ); - expect( - err.error.comparedValues.map((pk) => pk.toString()) - ).to.deep.equal([ - "11111111111111111111111111111111", - "SysvarRent111111111111111111111111111111111", - ]); - } - }, [ - "Program log: AnchorError caused by account: my_account. Error Code: ConstraintHasOne. Error Number: 2001. Error Message: A has one constraint was violated.", - "Program log: Left:", - "Program log: 11111111111111111111111111111111", - "Program log: Right:", - "Program log: SysvarRent111111111111111111111111111111111", - ]); - }); - - // This test uses a raw transaction and provider instead of a program - // instance since the client won't allow one to send a transaction - // with an invalid signer account. - it("Emits a signer error", async () => { - let signature; - const listener = anchor - .getProvider() - .connection.onLogs("all", (logs) => (signature = logs.signature)); - try { - const tx = new Transaction(); - tx.add( - new TransactionInstruction({ - keys: [ - { - pubkey: anchor.web3.SYSVAR_RENT_PUBKEY, - isWritable: false, - isSigner: false, - }, - ], - programId: program.programId, - data: program.coder.instruction.encode("signer_error", {}), - }) - ); - await program.provider.sendAndConfirm(tx); - assert.ok(false); - } catch (err) { - anchor.getProvider().connection.removeOnLogsListener(listener); - const errMsg = `Error: Raw transaction ${signature} failed ({"err":{"InstructionError":[0,{"Custom":3010}]}})`; - assert.strictEqual(err.toString(), errMsg); - } finally { - anchor.getProvider().connection.removeOnLogsListener(listener); - } - }); - - it("Emits a raw custom error", async () => { - try { - const tx = await program.rpc.rawCustomError({ - accounts: { - myAccount: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - }); - assert.ok(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - const errMsg = "HelloCustom"; - assert.strictEqual(err.error.errorMessage, errMsg); - assert.strictEqual(err.error.errorCode.number, 6125); - } - }); - - it("Emits a account not initialized error", async () => { - await withLogTest(async () => { - try { - const tx = await program.rpc.accountNotInitializedError({ - accounts: { - notInitializedAccount: new anchor.web3.Keypair().publicKey, - }, - }); - assert.fail( - "Unexpected success in creating a transaction that should have fail with `AccountNotInitialized` error" - ); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - const errMsg = - "The program expected this account to be already initialized"; - assert.strictEqual(err.error.errorMessage, errMsg); - } - }, [ - "Program log: AnchorError caused by account: not_initialized_account. Error Code: AccountNotInitialized. Error Number: 3012. Error Message: The program expected this account to be already initialized.", - ]); - }); - - it("Emits an AccountOwnedByWrongProgram error", async () => { - let client = await Token.createMint( - program.provider.connection, - program.provider.wallet.payer, - program.provider.wallet.publicKey, - program.provider.wallet.publicKey, - 9, - TOKEN_PROGRAM_ID - ); - - await withLogTest(async () => { - try { - const tx = await program.rpc.accountOwnedByWrongProgramError({ - accounts: { - wrongAccount: client.publicKey, - }, - }); - assert.fail( - "Unexpected success in creating a transaction that should have failed with `AccountOwnedByWrongProgram` error" - ); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - const errMsg = - "The given account is owned by a different program than expected"; - assert.strictEqual(err.error.errorMessage, errMsg); - } - }, [ - "Program log: AnchorError caused by account: wrong_account. Error Code: AccountOwnedByWrongProgram. Error Number: 3007. Error Message: The given account is owned by a different program than expected.", - "Program log: Left:", - "Program log: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - "Program log: Right:", - "Program log: Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS", - ]); - }); - - it("Emits a ValueMismatch error via require_eq", async () => { - await withLogTest(async () => { - try { - const tx = await program.rpc.requireEq(); - assert.fail( - "Unexpected success in creating a transaction that should have failed with `ValueMismatch` error" - ); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 6126); - expect(err.error.comparedValues).to.deep.equal(["5241", "124124124"]); - } - }, [ - "Program log: AnchorError thrown in programs/errors/src/lib.rs:68. Error Code: ValueMismatch. Error Number: 6126. Error Message: ValueMismatch.", - "Program log: Left: 5241", - "Program log: Right: 124124124", - ]); - }); - - it("Emits a RequireEqViolated error via require_eq", async () => { - await withLogTest(async () => { - try { - const tx = await program.rpc.requireEqDefaultError(); - assert.fail( - "Unexpected success in creating a transaction that should have failed with `ValueMismatch` error" - ); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2501); - } - }, [ - "Program log: AnchorError thrown in programs/errors/src/lib.rs:73. Error Code: RequireEqViolated. Error Number: 2501. Error Message: A require_eq expression was violated.", - "Program log: Left: 5241", - "Program log: Right: 124124124", - ]); - }); - - it("Emits a ValueMatch error via require_neq", async () => { - await withLogTest(async () => { - try { - const tx = await program.rpc.requireNeq(); - assert.fail( - "Unexpected success in creating a transaction that should have failed with `ValueMatch` error" - ); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 6127); - } - }, [ - "Program log: AnchorError thrown in programs/errors/src/lib.rs:78. Error Code: ValueMatch. Error Number: 6127. Error Message: ValueMatch.", - "Program log: Left: 500", - "Program log: Right: 500", - ]); - }); - - it("Emits a RequireNeqViolated error via require_neq", async () => { - await withLogTest(async () => { - try { - const tx = await program.rpc.requireNeqDefaultError(); - assert.fail( - "Unexpected success in creating a transaction that should have failed with `RequireNeqViolated` error" - ); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2503); - } - }, [ - "Program log: AnchorError thrown in programs/errors/src/lib.rs:83. Error Code: RequireNeqViolated. Error Number: 2503. Error Message: A require_neq expression was violated.", - "Program log: Left: 500", - "Program log: Right: 500", - ]); - }); - - it("Emits a ValueMismatch error via require_keys_eq", async () => { - const someAccount = anchor.web3.Keypair.generate().publicKey; - await withLogTest(async () => { - try { - const tx = await program.rpc.requireKeysEq({ - accounts: { - someAccount, - }, - }); - assert.fail( - "Unexpected success in creating a transaction that should have failed with `ValueMismatch` error" - ); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 6126); - } - }, [ - "Program log: AnchorError thrown in programs/errors/src/lib.rs:88. Error Code: ValueMismatch. Error Number: 6126. Error Message: ValueMismatch.", - "Program log: Left:", - `Program log: ${someAccount}`, - "Program log: Right:", - `Program log: ${program.programId}`, - ]); - }); - - it("Emits a RequireKeysEqViolated error via require_keys_eq", async () => { - const someAccount = anchor.web3.Keypair.generate().publicKey; - await withLogTest(async () => { - try { - const tx = await program.rpc.requireKeysEqDefaultError({ - accounts: { - someAccount, - }, - }); - assert.fail( - "Unexpected success in creating a transaction that should have failed with `ValueMismatch` error" - ); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2502); - } - }, [ - "Program log: AnchorError thrown in programs/errors/src/lib.rs:97. Error Code: RequireKeysEqViolated. Error Number: 2502. Error Message: A require_keys_eq expression was violated.", - "Program log: Left:", - `Program log: ${someAccount}`, - "Program log: Right:", - `Program log: ${program.programId}`, - ]); - }); - - it("Emits a ValueMatch error via require_keys_neq", async () => { - const someAccount = program.programId; - await withLogTest(async () => { - try { - const tx = await program.rpc.requireKeysNeq({ - accounts: { - someAccount, - }, - }); - assert.fail( - "Unexpected success in creating a transaction that should have failed with `ValueMatch` error" - ); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 6127); - } - }, [ - "Program log: AnchorError thrown in programs/errors/src/lib.rs:102. Error Code: ValueMatch. Error Number: 6127. Error Message: ValueMatch.", - "Program log: Left:", - `Program log: ${someAccount}`, - "Program log: Right:", - `Program log: ${program.programId}`, - ]); - }); - - it("Emits a RequireKeysNeqViolated error via require_keys_neq", async () => { - const someAccount = program.programId; - await withLogTest(async () => { - try { - const tx = await program.rpc.requireKeysNeqDefaultError({ - accounts: { - someAccount, - }, - }); - assert.fail( - "Unexpected success in creating a transaction that should have failed with `RequireKeysNeqViolated` error" - ); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2504); - } - }, [ - "Program log: AnchorError thrown in programs/errors/src/lib.rs:111. Error Code: RequireKeysNeqViolated. Error Number: 2504. Error Message: A require_keys_neq expression was violated.", - "Program log: Left:", - `Program log: ${someAccount}`, - "Program log: Right:", - `Program log: ${program.programId}`, - ]); - }); - - it("Emits a ValueLessOrEqual error via require_gt", async () => { - await withLogTest(async () => { - try { - const tx = await program.rpc.requireGt(); - assert.fail( - "Unexpected success in creating a transaction that should have failed with `ValueLessOrEqual` error" - ); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 6129); - } - }, [ - "Program log: AnchorError thrown in programs/errors/src/lib.rs:116. Error Code: ValueLessOrEqual. Error Number: 6129. Error Message: ValueLessOrEqual.", - "Program log: Left: 5", - "Program log: Right: 10", - ]); - }); - - it("Emits a RequireGtViolated error via require_gt", async () => { - await withLogTest(async () => { - try { - const tx = await program.rpc.requireGtDefaultError(); - assert.fail( - "Unexpected success in creating a transaction that should have failed with `RequireGtViolated` error" - ); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2505); - } - }, [ - "Program log: AnchorError thrown in programs/errors/src/lib.rs:121. Error Code: RequireGtViolated. Error Number: 2505. Error Message: A require_gt expression was violated.", - "Program log: Left: 10", - "Program log: Right: 10", - ]); - }); - - it("Emits a ValueLess error via require_gte", async () => { - await withLogTest(async () => { - try { - const tx = await program.rpc.requireGte(); - assert.fail( - "Unexpected success in creating a transaction that should have failed with `ValueLess` error" - ); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 6128); - } - }, [ - "Program log: AnchorError thrown in programs/errors/src/lib.rs:126. Error Code: ValueLess. Error Number: 6128. Error Message: ValueLess.", - "Program log: Left: 5", - "Program log: Right: 10", - ]); - }); - - it("Emits a RequireGteViolated error via require_gte", async () => { - await withLogTest(async () => { - try { - const tx = await program.rpc.requireGteDefaultError(); - assert.fail( - "Unexpected success in creating a transaction that should have failed with `RequireGteViolated` error" - ); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2506); - } - }, [ - "Program log: AnchorError thrown in programs/errors/src/lib.rs:131. Error Code: RequireGteViolated. Error Number: 2506. Error Message: A require_gte expression was violated.", - "Program log: Left: 5", - "Program log: Right: 10", - ]); - }); -}); diff --git a/tests/errors/tsconfig.json b/tests/errors/tsconfig.json deleted file mode 100644 index 70c39f55..00000000 --- a/tests/errors/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "types": ["mocha", "chai", "node"], - "typeRoots": ["./node_modules/@types"], - "lib": ["es2015"], - "module": "commonjs", - "target": "es6", - "esModuleInterop": true - } -} diff --git a/tests/escrow/Anchor.toml b/tests/escrow/Anchor.toml deleted file mode 100644 index 6511f7c0..00000000 --- a/tests/escrow/Anchor.toml +++ /dev/null @@ -1,11 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -escrow = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[scripts] -test = "yarn run ts-mocha -t 1000000 tests/*.ts" - -[features] diff --git a/tests/escrow/Cargo.toml b/tests/escrow/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/escrow/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/escrow/package.json b/tests/escrow/package.json deleted file mode 100644 index 43c63b54..00000000 --- a/tests/escrow/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "escrow", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/escrow/programs/escrow/Cargo.toml b/tests/escrow/programs/escrow/Cargo.toml deleted file mode 100644 index f640f051..00000000 --- a/tests/escrow/programs/escrow/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "escrow" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "escrow" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } -anchor-spl = { path = "../../../../spl" } -spl-token = { version = "3.1.1", features = ["no-entrypoint"] } diff --git a/tests/escrow/programs/escrow/Xargo.toml b/tests/escrow/programs/escrow/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/escrow/programs/escrow/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/escrow/programs/escrow/src/lib.rs b/tests/escrow/programs/escrow/src/lib.rs deleted file mode 100644 index 2f3e80bc..00000000 --- a/tests/escrow/programs/escrow/src/lib.rs +++ /dev/null @@ -1,240 +0,0 @@ -//! An example of an escrow program, inspired by PaulX tutorial seen here -//! https://paulx.dev/blog/2021/01/14/programming-on-solana-an-introduction/ -//! This example has some changes to implementation, but more or less should be the same overall -//! Also gives examples on how to use some newer anchor features and CPI -//! -//! User (Initializer) constructs an escrow deal: -//! - SPL token (X) they will offer and amount -//! - SPL token (Y) count they want in return and amount -//! - Program will take ownership of initializer's token X account -//! -//! Once this escrow is initialised, either: -//! 1. User (Taker) can call the exchange function to exchange their Y for X -//! - This will close the escrow account and no longer be usable -//! OR -//! 2. If no one has exchanged, the initializer can close the escrow account -//! - Initializer will get back ownership of their token X account - -use anchor_lang::prelude::*; -use anchor_spl::token::{self, SetAuthority, Token, TokenAccount, Transfer}; -use spl_token::instruction::AuthorityType; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -pub mod escrow { - use super::*; - - const ESCROW_PDA_SEED: &[u8] = b"escrow"; - - pub fn initialize_escrow( - ctx: Context, - initializer_amount: u64, - taker_amount: u64, - ) -> Result<()> { - ctx.accounts.escrow_account.initializer_key = *ctx.accounts.initializer.key; - ctx.accounts - .escrow_account - .initializer_deposit_token_account = *ctx - .accounts - .initializer_deposit_token_account - .to_account_info() - .key; - ctx.accounts - .escrow_account - .initializer_receive_token_account = *ctx - .accounts - .initializer_receive_token_account - .to_account_info() - .key; - ctx.accounts.escrow_account.initializer_amount = initializer_amount; - ctx.accounts.escrow_account.taker_amount = taker_amount; - - let (pda, _bump_seed) = Pubkey::find_program_address(&[ESCROW_PDA_SEED], ctx.program_id); - token::set_authority(ctx.accounts.into(), AuthorityType::AccountOwner, Some(pda))?; - Ok(()) - } - - pub fn cancel_escrow(ctx: Context) -> Result<()> { - let (_pda, bump_seed) = Pubkey::find_program_address(&[ESCROW_PDA_SEED], ctx.program_id); - let seeds = &[&ESCROW_PDA_SEED[..], &[bump_seed]]; - - token::set_authority( - ctx.accounts - .into_set_authority_context() - .with_signer(&[&seeds[..]]), - AuthorityType::AccountOwner, - Some(ctx.accounts.escrow_account.initializer_key), - )?; - - Ok(()) - } - - pub fn exchange(ctx: Context) -> Result<()> { - // Transferring from initializer to taker - let (_pda, bump_seed) = Pubkey::find_program_address(&[ESCROW_PDA_SEED], ctx.program_id); - let seeds = &[&ESCROW_PDA_SEED[..], &[bump_seed]]; - - token::transfer( - ctx.accounts - .into_transfer_to_taker_context() - .with_signer(&[&seeds[..]]), - ctx.accounts.escrow_account.initializer_amount, - )?; - - token::transfer( - ctx.accounts.into_transfer_to_initializer_context(), - ctx.accounts.escrow_account.taker_amount, - )?; - - token::set_authority( - ctx.accounts - .into_set_authority_context() - .with_signer(&[&seeds[..]]), - AuthorityType::AccountOwner, - Some(ctx.accounts.escrow_account.initializer_key), - )?; - - Ok(()) - } -} - -#[derive(Accounts)] -#[instruction(initializer_amount: u64)] -pub struct InitializeEscrow<'info> { - #[account(mut)] - pub initializer: Signer<'info>, - #[account( - mut, - constraint = initializer_deposit_token_account.amount >= initializer_amount - )] - pub initializer_deposit_token_account: Account<'info, TokenAccount>, - pub initializer_receive_token_account: Account<'info, TokenAccount>, - #[account(init, payer = initializer, space = 8 + EscrowAccount::LEN)] - pub escrow_account: Account<'info, EscrowAccount>, - pub system_program: Program<'info, System>, - pub token_program: Program<'info, Token>, -} - -#[derive(Accounts)] -pub struct Exchange<'info> { - #[account(signer)] - pub taker: AccountInfo<'info>, - #[account(mut)] - pub taker_deposit_token_account: Account<'info, TokenAccount>, - #[account(mut)] - pub taker_receive_token_account: Account<'info, TokenAccount>, - #[account(mut)] - pub pda_deposit_token_account: Account<'info, TokenAccount>, - #[account(mut)] - pub initializer_receive_token_account: Account<'info, TokenAccount>, - #[account(mut)] - pub initializer_main_account: AccountInfo<'info>, - #[account( - mut, - constraint = escrow_account.taker_amount <= taker_deposit_token_account.amount, - constraint = escrow_account.initializer_deposit_token_account == *pda_deposit_token_account.to_account_info().key, - constraint = escrow_account.initializer_receive_token_account == *initializer_receive_token_account.to_account_info().key, - constraint = escrow_account.initializer_key == *initializer_main_account.key, - close = initializer_main_account - )] - pub escrow_account: Account<'info, EscrowAccount>, - pub pda_account: AccountInfo<'info>, - pub token_program: Program<'info, Token>, -} - -#[derive(Accounts)] -pub struct CancelEscrow<'info> { - pub initializer: AccountInfo<'info>, - #[account(mut)] - pub pda_deposit_token_account: Account<'info, TokenAccount>, - pub pda_account: AccountInfo<'info>, - #[account( - mut, - constraint = escrow_account.initializer_key == *initializer.key, - constraint = escrow_account.initializer_deposit_token_account == *pda_deposit_token_account.to_account_info().key, - close = initializer - )] - pub escrow_account: Account<'info, EscrowAccount>, - pub token_program: Program<'info, Token>, -} - -#[account] -pub struct EscrowAccount { - pub initializer_key: Pubkey, - pub initializer_deposit_token_account: Pubkey, - pub initializer_receive_token_account: Pubkey, - pub initializer_amount: u64, - pub taker_amount: u64, -} - -impl EscrowAccount { - pub const LEN: usize = 32 + 32 + 32 + 8 + 8; -} - -impl<'info> From<&mut InitializeEscrow<'info>> - for CpiContext<'_, '_, '_, 'info, SetAuthority<'info>> -{ - fn from(accounts: &mut InitializeEscrow<'info>) -> Self { - let cpi_accounts = SetAuthority { - account_or_mint: accounts - .initializer_deposit_token_account - .to_account_info() - .clone(), - current_authority: accounts.initializer.to_account_info().clone(), - }; - let cpi_program = accounts.token_program.to_account_info(); - CpiContext::new(cpi_program, cpi_accounts) - } -} - -impl<'info> CancelEscrow<'info> { - fn into_set_authority_context(&self) -> CpiContext<'_, '_, '_, 'info, SetAuthority<'info>> { - let cpi_accounts = SetAuthority { - account_or_mint: self.pda_deposit_token_account.to_account_info().clone(), - current_authority: self.pda_account.clone(), - }; - let cpi_program = self.token_program.to_account_info(); - CpiContext::new(cpi_program, cpi_accounts) - } -} - -impl<'info> Exchange<'info> { - fn into_set_authority_context(&self) -> CpiContext<'_, '_, '_, 'info, SetAuthority<'info>> { - let cpi_accounts = SetAuthority { - account_or_mint: self.pda_deposit_token_account.to_account_info().clone(), - current_authority: self.pda_account.clone(), - }; - let cpi_program = self.token_program.to_account_info(); - CpiContext::new(cpi_program, cpi_accounts) - } -} - -impl<'info> Exchange<'info> { - fn into_transfer_to_taker_context(&self) -> CpiContext<'_, '_, '_, 'info, Transfer<'info>> { - let cpi_accounts = Transfer { - from: self.pda_deposit_token_account.to_account_info().clone(), - to: self.taker_receive_token_account.to_account_info().clone(), - authority: self.pda_account.clone(), - }; - let cpi_program = self.token_program.to_account_info(); - CpiContext::new(cpi_program, cpi_accounts) - } -} - -impl<'info> Exchange<'info> { - fn into_transfer_to_initializer_context( - &self, - ) -> CpiContext<'_, '_, '_, 'info, Transfer<'info>> { - let cpi_accounts = Transfer { - from: self.taker_deposit_token_account.to_account_info().clone(), - to: self - .initializer_receive_token_account - .to_account_info() - .clone(), - authority: self.taker.clone(), - }; - let cpi_program = self.token_program.to_account_info(); - CpiContext::new(cpi_program, cpi_accounts) - } -} diff --git a/tests/escrow/tests/escrow.ts b/tests/escrow/tests/escrow.ts deleted file mode 100644 index c12ae81c..00000000 --- a/tests/escrow/tests/escrow.ts +++ /dev/null @@ -1,246 +0,0 @@ -import * as anchor from "@project-serum/anchor"; -import { Program, BN, IdlAccounts } from "@project-serum/anchor"; -import { PublicKey, Keypair, SystemProgram } from "@solana/web3.js"; -import { TOKEN_PROGRAM_ID, Token } from "@solana/spl-token"; -import { assert } from "chai"; -import { Escrow } from "../target/types/escrow"; - -type EscrowAccount = IdlAccounts["escrowAccount"]; - -describe("escrow", () => { - const provider = anchor.AnchorProvider.env(); - anchor.setProvider(provider); - - const program = anchor.workspace.Escrow as Program; - - let mintA: Token = null; - let mintB: Token = null; - let initializerTokenAccountA: PublicKey = null; - let initializerTokenAccountB: PublicKey = null; - let takerTokenAccountA: PublicKey = null; - let takerTokenAccountB: PublicKey = null; - let pda: PublicKey = null; - - const takerAmount = 1000; - const initializerAmount = 500; - - const escrowAccount = Keypair.generate(); - const payer = Keypair.generate(); - const mintAuthority = Keypair.generate(); - - it("Initialise escrow state", async () => { - // Airdropping tokens to a payer. - await provider.connection.confirmTransaction( - await provider.connection.requestAirdrop(payer.publicKey, 10000000000), - "confirmed" - ); - - mintA = await Token.createMint( - provider.connection, - payer, - mintAuthority.publicKey, - null, - 0, - TOKEN_PROGRAM_ID - ); - - mintB = await Token.createMint( - provider.connection, - payer, - mintAuthority.publicKey, - null, - 0, - TOKEN_PROGRAM_ID - ); - - initializerTokenAccountA = await mintA.createAccount( - provider.wallet.publicKey - ); - takerTokenAccountA = await mintA.createAccount(provider.wallet.publicKey); - - initializerTokenAccountB = await mintB.createAccount( - provider.wallet.publicKey - ); - takerTokenAccountB = await mintB.createAccount(provider.wallet.publicKey); - - await mintA.mintTo( - initializerTokenAccountA, - mintAuthority.publicKey, - [mintAuthority], - initializerAmount - ); - - await mintB.mintTo( - takerTokenAccountB, - mintAuthority.publicKey, - [mintAuthority], - takerAmount - ); - - let _initializerTokenAccountA = await mintA.getAccountInfo( - initializerTokenAccountA - ); - let _takerTokenAccountB = await mintB.getAccountInfo(takerTokenAccountB); - - assert.strictEqual( - _initializerTokenAccountA.amount.toNumber(), - initializerAmount - ); - assert.strictEqual(_takerTokenAccountB.amount.toNumber(), takerAmount); - }); - - it("Initialize escrow", async () => { - await program.rpc.initializeEscrow( - new BN(initializerAmount), - new BN(takerAmount), - { - accounts: { - initializer: provider.wallet.publicKey, - initializerDepositTokenAccount: initializerTokenAccountA, - initializerReceiveTokenAccount: initializerTokenAccountB, - escrowAccount: escrowAccount.publicKey, - systemProgram: SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [escrowAccount], - } - ); - - // Get the PDA that is assigned authority to token account. - const [_pda, _nonce] = await PublicKey.findProgramAddress( - [Buffer.from(anchor.utils.bytes.utf8.encode("escrow"))], - program.programId - ); - - pda = _pda; - - let _initializerTokenAccountA = await mintA.getAccountInfo( - initializerTokenAccountA - ); - - let _escrowAccount: EscrowAccount = - await program.account.escrowAccount.fetch(escrowAccount.publicKey); - - // Check that the new owner is the PDA. - assert.isTrue(_initializerTokenAccountA.owner.equals(pda)); - - // Check that the values in the escrow account match what we expect. - assert.isTrue( - _escrowAccount.initializerKey.equals(provider.wallet.publicKey) - ); - assert.strictEqual( - _escrowAccount.initializerAmount.toNumber(), - initializerAmount - ); - assert.strictEqual(_escrowAccount.takerAmount.toNumber(), takerAmount); - assert.isTrue( - _escrowAccount.initializerDepositTokenAccount.equals( - initializerTokenAccountA - ) - ); - assert.isTrue( - _escrowAccount.initializerReceiveTokenAccount.equals( - initializerTokenAccountB - ) - ); - }); - - it("Exchange escrow", async () => { - await program.rpc.exchange({ - accounts: { - taker: provider.wallet.publicKey, - takerDepositTokenAccount: takerTokenAccountB, - takerReceiveTokenAccount: takerTokenAccountA, - pdaDepositTokenAccount: initializerTokenAccountA, - initializerReceiveTokenAccount: initializerTokenAccountB, - initializerMainAccount: provider.wallet.publicKey, - escrowAccount: escrowAccount.publicKey, - pdaAccount: pda, - tokenProgram: TOKEN_PROGRAM_ID, - }, - }); - - let _takerTokenAccountA = await mintA.getAccountInfo(takerTokenAccountA); - let _takerTokenAccountB = await mintB.getAccountInfo(takerTokenAccountB); - let _initializerTokenAccountA = await mintA.getAccountInfo( - initializerTokenAccountA - ); - let _initializerTokenAccountB = await mintB.getAccountInfo( - initializerTokenAccountB - ); - - // Check that the initializer gets back ownership of their token account. - assert.isTrue(_takerTokenAccountA.owner.equals(provider.wallet.publicKey)); - - assert.strictEqual( - _takerTokenAccountA.amount.toNumber(), - initializerAmount - ); - assert.strictEqual(_initializerTokenAccountA.amount.toNumber(), 0); - assert.strictEqual( - _initializerTokenAccountB.amount.toNumber(), - takerAmount - ); - assert.strictEqual(_takerTokenAccountB.amount.toNumber(), 0); - }); - - let newEscrow = Keypair.generate(); - - it("Initialize escrow and cancel escrow", async () => { - // Put back tokens into initializer token A account. - await mintA.mintTo( - initializerTokenAccountA, - mintAuthority.publicKey, - [mintAuthority], - initializerAmount - ); - - await program.rpc.initializeEscrow( - new BN(initializerAmount), - new BN(takerAmount), - { - accounts: { - initializer: provider.wallet.publicKey, - initializerDepositTokenAccount: initializerTokenAccountA, - initializerReceiveTokenAccount: initializerTokenAccountB, - escrowAccount: newEscrow.publicKey, - systemProgram: SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - }, - signers: [newEscrow], - } - ); - - let _initializerTokenAccountA = await mintA.getAccountInfo( - initializerTokenAccountA - ); - - // Check that the new owner is the PDA. - assert.isTrue(_initializerTokenAccountA.owner.equals(pda)); - - // Cancel the escrow. - await program.rpc.cancelEscrow({ - accounts: { - initializer: provider.wallet.publicKey, - pdaDepositTokenAccount: initializerTokenAccountA, - pdaAccount: pda, - escrowAccount: newEscrow.publicKey, - tokenProgram: TOKEN_PROGRAM_ID, - }, - }); - - // Check the final owner should be the provider public key. - _initializerTokenAccountA = await mintA.getAccountInfo( - initializerTokenAccountA - ); - assert.isTrue( - _initializerTokenAccountA.owner.equals(provider.wallet.publicKey) - ); - - // Check all the funds are still there. - assert.strictEqual( - _initializerTokenAccountA.amount.toNumber(), - initializerAmount - ); - }); -}); diff --git a/tests/escrow/tsconfig.json b/tests/escrow/tsconfig.json deleted file mode 100644 index 8c20b223..00000000 --- a/tests/escrow/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "types": ["mocha", "chai", "node"], - "typeRoots": ["./node_modules/@types"], - "lib": ["es2015"], - "module": "commonjs", - "target": "es6", - "esModuleInterop": true - } -} diff --git a/ts/tests/events.spec.ts b/tests/events.spec.ts similarity index 100% rename from ts/tests/events.spec.ts rename to tests/events.spec.ts diff --git a/tests/events/Anchor.toml b/tests/events/Anchor.toml deleted file mode 100644 index 8a154d14..00000000 --- a/tests/events/Anchor.toml +++ /dev/null @@ -1,9 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -events = "2dhGsWUzy5YKUsjZdLHLmkNpUDAXkNa9MYWsPc4Ziqzy" - -[scripts] -test = "yarn run mocha -t 1000000 tests/" diff --git a/tests/events/Cargo.toml b/tests/events/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/events/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/events/migrations/deploy.js b/tests/events/migrations/deploy.js deleted file mode 100644 index 5e3df0dc..00000000 --- a/tests/events/migrations/deploy.js +++ /dev/null @@ -1,12 +0,0 @@ -// Migrations are an early feature. Currently, they're nothing more than this -// single deploy script that's invoked from the CLI, injecting a provider -// configured from the workspace's Anchor.toml. - -const anchor = require("@project-serum/anchor"); - -module.exports = async function (provider) { - // Configure client to use the provider. - anchor.setProvider(provider); - - // Add your deploy script here. -}; diff --git a/tests/events/package.json b/tests/events/package.json deleted file mode 100644 index 629b613c..00000000 --- a/tests/events/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "events", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/events/programs/events/Cargo.toml b/tests/events/programs/events/Cargo.toml deleted file mode 100644 index cfa78b6a..00000000 --- a/tests/events/programs/events/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "events" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "events" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } diff --git a/tests/events/programs/events/Xargo.toml b/tests/events/programs/events/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/events/programs/events/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/events/programs/events/src/lib.rs b/tests/events/programs/events/src/lib.rs deleted file mode 100644 index ef17b7b4..00000000 --- a/tests/events/programs/events/src/lib.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! This example demonstrates how to emit an event, which can be -//! subscribed to by a client. - -use anchor_lang::prelude::*; - -declare_id!("2dhGsWUzy5YKUsjZdLHLmkNpUDAXkNa9MYWsPc4Ziqzy"); - -#[program] -pub mod events { - use super::*; - pub fn initialize(_ctx: Context) -> Result<()> { - emit!(MyEvent { - data: 5, - label: "hello".to_string(), - }); - Ok(()) - } - - pub fn test_event(_ctx: Context) -> Result<()> { - emit!(MyOtherEvent { - data: 6, - label: "bye".to_string(), - }); - Ok(()) - } -} - -#[derive(Accounts)] -pub struct Initialize {} - -#[derive(Accounts)] -pub struct TestEvent {} - -#[event] -pub struct MyEvent { - pub data: u64, - #[index] - pub label: String, -} - -#[event] -pub struct MyOtherEvent { - pub data: u64, - #[index] - pub label: String, -} diff --git a/tests/events/tests/events.js b/tests/events/tests/events.js deleted file mode 100644 index 348408cb..00000000 --- a/tests/events/tests/events.js +++ /dev/null @@ -1,61 +0,0 @@ -const anchor = require("@project-serum/anchor"); -const { assert } = require("chai"); - -describe("events", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.env()); - const program = anchor.workspace.Events; - - it("Is initialized!", async () => { - let listener = null; - - let [event, slot] = await new Promise((resolve, _reject) => { - listener = program.addEventListener("MyEvent", (event, slot) => { - resolve([event, slot]); - }); - program.rpc.initialize(); - }); - await program.removeEventListener(listener); - - assert.isAbove(slot, 0); - assert.strictEqual(event.data.toNumber(), 5); - assert.strictEqual(event.label, "hello"); - }); - - it("Multiple events", async () => { - // Sleep so we don't get this transaction has already been processed. - await sleep(2000); - - let listenerOne = null; - let listenerTwo = null; - - let [eventOne, slotOne] = await new Promise((resolve, _reject) => { - listenerOne = program.addEventListener("MyEvent", (event, slot) => { - resolve([event, slot]); - }); - program.rpc.initialize(); - }); - - let [eventTwo, slotTwo] = await new Promise((resolve, _reject) => { - listenerTwo = program.addEventListener("MyOtherEvent", (event, slot) => { - resolve([event, slot]); - }); - program.rpc.testEvent(); - }); - - await program.removeEventListener(listenerOne); - await program.removeEventListener(listenerTwo); - - assert.isAbove(slotOne, 0); - assert.strictEqual(eventOne.data.toNumber(), 5); - assert.strictEqual(eventOne.label, "hello"); - - assert.isAbove(slotTwo, 0); - assert.strictEqual(eventTwo.data.toNumber(), 6); - assert.strictEqual(eventTwo.label, "bye"); - }); -}); - -function sleep(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} diff --git a/tests/floats/Anchor.toml b/tests/floats/Anchor.toml deleted file mode 100644 index 7fcee42a..00000000 --- a/tests/floats/Anchor.toml +++ /dev/null @@ -1,12 +0,0 @@ -[programs.localnet] -floats = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[registry] -url = "https://anchor.projectserum.com" - -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[scripts] -test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" diff --git a/tests/floats/Cargo.toml b/tests/floats/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/floats/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/floats/migrations/deploy.ts b/tests/floats/migrations/deploy.ts deleted file mode 100644 index 5e3df0dc..00000000 --- a/tests/floats/migrations/deploy.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Migrations are an early feature. Currently, they're nothing more than this -// single deploy script that's invoked from the CLI, injecting a provider -// configured from the workspace's Anchor.toml. - -const anchor = require("@project-serum/anchor"); - -module.exports = async function (provider) { - // Configure client to use the provider. - anchor.setProvider(provider); - - // Add your deploy script here. -}; diff --git a/tests/floats/package.json b/tests/floats/package.json deleted file mode 100644 index eed612d9..00000000 --- a/tests/floats/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "floats", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } - } diff --git a/tests/floats/programs/floats/Cargo.toml b/tests/floats/programs/floats/Cargo.toml deleted file mode 100644 index c9a4a2b4..00000000 --- a/tests/floats/programs/floats/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "floats" -version = "0.1.0" -description = "Created with Anchor" -edition = "2018" - -[lib] -crate-type = ["cdylib", "lib"] -name = "floats" - -[features] -no-entrypoint = [] -no-idl = [] -no-log-ix-name = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } diff --git a/tests/floats/programs/floats/Xargo.toml b/tests/floats/programs/floats/Xargo.toml deleted file mode 100644 index 475fb71e..00000000 --- a/tests/floats/programs/floats/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] diff --git a/tests/floats/programs/floats/src/lib.rs b/tests/floats/programs/floats/src/lib.rs deleted file mode 100644 index 22c64234..00000000 --- a/tests/floats/programs/floats/src/lib.rs +++ /dev/null @@ -1,51 +0,0 @@ -use anchor_lang::prelude::*; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -pub mod floats { - use super::*; - - pub fn create(ctx: Context, data_f32: f32, data_f64: f64) -> Result<()> { - let account = &mut ctx.accounts.account; - let authority = &mut ctx.accounts.authority; - - account.data_f32 = data_f32; - account.data_f64 = data_f64; - account.authority = authority.key(); - - Ok(()) - } - - pub fn update(ctx: Context, data_f32: f32, data_f64: f64) -> Result<()> { - let account = &mut ctx.accounts.account; - - account.data_f32 = data_f32; - account.data_f64 = data_f64; - - Ok(()) - } -} - -#[derive(Accounts)] -pub struct Create<'info> { - #[account(init, payer = authority, space = 8 + 8 + 4 + 32)] - pub account: Account<'info, FloatDataAccount>, - #[account(mut)] - pub authority: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct Update<'info> { - #[account(mut, has_one = authority)] - pub account: Account<'info, FloatDataAccount>, - pub authority: Signer<'info>, -} - -#[account] -pub struct FloatDataAccount { - pub data_f64: f64, - pub data_f32: f32, - pub authority: Pubkey, -} diff --git a/tests/floats/tests/floats.ts b/tests/floats/tests/floats.ts deleted file mode 100644 index ff0e0b7c..00000000 --- a/tests/floats/tests/floats.ts +++ /dev/null @@ -1,67 +0,0 @@ -import * as anchor from "@project-serum/anchor"; -import { Program, getProvider } from "@project-serum/anchor"; -import { Keypair, SystemProgram } from "@solana/web3.js"; -import { Floats } from "../target/types/floats"; -import { assert } from "chai"; - -describe("floats", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.env()); - - const program = anchor.workspace.Floats as Program; - - it("Creates an account with float data", async () => { - const accountKeypair = Keypair.generate(); - - await program.methods - .create(1.0, 2.0) - .accounts({ - account: accountKeypair.publicKey, - authority: getProvider().wallet.publicKey, - systemProgram: SystemProgram.programId, - }) - .signers([accountKeypair]) - .rpc(); - - const account = await program.account.floatDataAccount.fetch( - accountKeypair.publicKey - ); - - assert.strictEqual(account.dataF32, 1.0); - assert.strictEqual(account.dataF64, 2.0); - }); - - it("Updates an account with float data", async () => { - const accountKeypair = Keypair.generate(); - const authorityPublicKey = getProvider().wallet.publicKey; - - await program.methods - .create(1.0, 2.0) - .accounts({ - account: accountKeypair.publicKey, - authority: authorityPublicKey, - systemProgram: SystemProgram.programId, - }) - .signers([accountKeypair]) - .rpc(); - - let account = await program.account.floatDataAccount.fetch( - accountKeypair.publicKey - ); - - await program.methods - .update(3.0, 4.0) - .accounts({ - account: accountKeypair.publicKey, - authority: authorityPublicKey, - }) - .rpc(); - - account = await program.account.floatDataAccount.fetch( - accountKeypair.publicKey - ); - - assert.strictEqual(account.dataF32, 3.0); - assert.strictEqual(account.dataF64, 4.0); - }); -}); diff --git a/tests/floats/tsconfig.json b/tests/floats/tsconfig.json deleted file mode 100644 index cd5d2e3d..00000000 --- a/tests/floats/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "types": ["mocha", "chai"], - "typeRoots": ["./node_modules/@types"], - "lib": ["es2015"], - "module": "commonjs", - "target": "es6", - "esModuleInterop": true - } -} diff --git a/tests/ido-pool/Anchor.toml b/tests/ido-pool/Anchor.toml deleted file mode 100644 index f38b98b0..00000000 --- a/tests/ido-pool/Anchor.toml +++ /dev/null @@ -1,11 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -ido_pool = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[scripts] -test = "yarn run mocha -t 1000000 tests/" - -[features] diff --git a/tests/ido-pool/Cargo.toml b/tests/ido-pool/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/ido-pool/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/ido-pool/migrations/deploy.js b/tests/ido-pool/migrations/deploy.js deleted file mode 100644 index 5e3df0dc..00000000 --- a/tests/ido-pool/migrations/deploy.js +++ /dev/null @@ -1,12 +0,0 @@ -// Migrations are an early feature. Currently, they're nothing more than this -// single deploy script that's invoked from the CLI, injecting a provider -// configured from the workspace's Anchor.toml. - -const anchor = require("@project-serum/anchor"); - -module.exports = async function (provider) { - // Configure client to use the provider. - anchor.setProvider(provider); - - // Add your deploy script here. -}; diff --git a/tests/ido-pool/package.json b/tests/ido-pool/package.json deleted file mode 100644 index adc626d9..00000000 --- a/tests/ido-pool/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "ido-pool", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/ido-pool/programs/ido-pool/Cargo.toml b/tests/ido-pool/programs/ido-pool/Cargo.toml deleted file mode 100644 index 0a54b960..00000000 --- a/tests/ido-pool/programs/ido-pool/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "ido-pool" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "ido_pool" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } -anchor-spl = { path = "../../../../spl" } diff --git a/tests/ido-pool/programs/ido-pool/Xargo.toml b/tests/ido-pool/programs/ido-pool/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/ido-pool/programs/ido-pool/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/ido-pool/programs/ido-pool/src/lib.rs b/tests/ido-pool/programs/ido-pool/src/lib.rs deleted file mode 100644 index 4bb0cc6b..00000000 --- a/tests/ido-pool/programs/ido-pool/src/lib.rs +++ /dev/null @@ -1,684 +0,0 @@ -//! An IDO pool program implementing the Mango Markets token sale design here: -//! https://docs.mango.markets/litepaper#token-sale. -// #![warn(clippy::all)] - -use anchor_lang::prelude::*; -use anchor_spl::token::{self, Burn, CloseAccount, Mint, MintTo, Token, TokenAccount, Transfer}; - -use std::ops::Deref; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -const DECIMALS: u8 = 6; - -#[program] -pub mod ido_pool { - use super::*; - - #[access_control(validate_ido_times(ido_times))] - pub fn initialize_pool( - ctx: Context, - ido_name: String, - _bumps: PoolBumps, // No longer used. - num_ido_tokens: u64, - ido_times: IdoTimes, - ) -> Result<()> { - msg!("INITIALIZE POOL"); - - let ido_account = &mut ctx.accounts.ido_account; - - let name_bytes = ido_name.as_bytes(); - let mut name_data = [b' '; 10]; - name_data[..name_bytes.len()].copy_from_slice(name_bytes); - - ido_account.ido_name = name_data; - ido_account.bumps = PoolBumps { - ido_account: *ctx.bumps.get("ido_account").unwrap(), - redeemable_mint: *ctx.bumps.get("redeemable_mint").unwrap(), - pool_watermelon: *ctx.bumps.get("pool_watermelon").unwrap(), - pool_usdc: *ctx.bumps.get("pool_usdc").unwrap(), - }; - ido_account.ido_authority = ctx.accounts.ido_authority.key(); - - ido_account.usdc_mint = ctx.accounts.usdc_mint.key(); - ido_account.redeemable_mint = ctx.accounts.redeemable_mint.key(); - ido_account.watermelon_mint = ctx.accounts.watermelon_mint.key(); - ido_account.pool_usdc = ctx.accounts.pool_usdc.key(); - ido_account.pool_watermelon = ctx.accounts.pool_watermelon.key(); - - ido_account.num_ido_tokens = num_ido_tokens; - ido_account.ido_times = ido_times; - - // Transfer Watermelon from ido_authority to pool account. - let cpi_accounts = Transfer { - from: ctx.accounts.ido_authority_watermelon.to_account_info(), - to: ctx.accounts.pool_watermelon.to_account_info(), - authority: ctx.accounts.ido_authority.to_account_info(), - }; - let cpi_program = ctx.accounts.token_program.to_account_info(); - let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); - token::transfer(cpi_ctx, num_ido_tokens)?; - - Ok(()) - } - - #[access_control(unrestricted_phase(&ctx.accounts.ido_account))] - pub fn init_user_redeemable(ctx: Context) -> Result<()> { - msg!("INIT USER REDEEMABLE"); - Ok(()) - } - - #[access_control(unrestricted_phase(&ctx.accounts.ido_account))] - pub fn exchange_usdc_for_redeemable( - ctx: Context, - amount: u64, - ) -> Result<()> { - msg!("EXCHANGE USDC FOR REDEEMABLE"); - // While token::transfer will check this, we prefer a verbose err msg. - if ctx.accounts.user_usdc.amount < amount { - return err!(ErrorCode::LowUsdc); - } - - // Transfer user's USDC to pool USDC account. - let cpi_accounts = Transfer { - from: ctx.accounts.user_usdc.to_account_info(), - to: ctx.accounts.pool_usdc.to_account_info(), - authority: ctx.accounts.user_authority.to_account_info(), - }; - let cpi_program = ctx.accounts.token_program.to_account_info(); - let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); - token::transfer(cpi_ctx, amount)?; - - // Mint Redeemable to user Redeemable account. - let ido_name = ctx.accounts.ido_account.ido_name.as_ref(); - let seeds = &[ - ido_name.trim_ascii_whitespace(), - &[ctx.accounts.ido_account.bumps.ido_account], - ]; - let signer = &[&seeds[..]]; - let cpi_accounts = MintTo { - mint: ctx.accounts.redeemable_mint.to_account_info(), - to: ctx.accounts.user_redeemable.to_account_info(), - authority: ctx.accounts.ido_account.to_account_info(), - }; - let cpi_program = ctx.accounts.token_program.to_account_info(); - let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); - token::mint_to(cpi_ctx, amount)?; - - Ok(()) - } - - #[access_control(withdraw_phase(&ctx.accounts.ido_account))] - pub fn init_escrow_usdc(ctx: Context) -> Result<()> { - msg!("INIT ESCROW USDC"); - Ok(()) - } - - #[access_control(withdraw_phase(&ctx.accounts.ido_account))] - pub fn exchange_redeemable_for_usdc( - ctx: Context, - amount: u64, - ) -> Result<()> { - msg!("EXCHANGE REDEEMABLE FOR USDC"); - // While token::burn will check this, we prefer a verbose err msg. - if ctx.accounts.user_redeemable.amount < amount { - return err!(ErrorCode::LowRedeemable); - } - - let ido_name = ctx.accounts.ido_account.ido_name.as_ref(); - let seeds = &[ - ido_name.trim_ascii_whitespace(), - &[ctx.accounts.ido_account.bumps.ido_account], - ]; - let signer = &[&seeds[..]]; - - // Burn the user's redeemable tokens. - let cpi_accounts = Burn { - mint: ctx.accounts.redeemable_mint.to_account_info(), - from: ctx.accounts.user_redeemable.to_account_info(), - authority: ctx.accounts.ido_account.to_account_info(), - }; - let cpi_program = ctx.accounts.token_program.to_account_info(); - let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); - token::burn(cpi_ctx, amount)?; - - // Transfer USDC from pool account to the user's escrow account. - let cpi_accounts = Transfer { - from: ctx.accounts.pool_usdc.to_account_info(), - to: ctx.accounts.escrow_usdc.to_account_info(), - authority: ctx.accounts.ido_account.to_account_info(), - }; - let cpi_program = ctx.accounts.token_program.to_account_info(); - let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); - token::transfer(cpi_ctx, amount)?; - - Ok(()) - } - - #[access_control(ido_over(&ctx.accounts.ido_account))] - pub fn exchange_redeemable_for_watermelon( - ctx: Context, - amount: u64, - ) -> Result<()> { - msg!("EXCHANGE REDEEMABLE FOR WATERMELON"); - // While token::burn will check this, we prefer a verbose err msg. - if ctx.accounts.user_redeemable.amount < amount { - return err!(ErrorCode::LowRedeemable); - } - - // Calculate watermelon tokens due. - let watermelon_amount = (amount as u128) - .checked_mul(ctx.accounts.pool_watermelon.amount as u128) - .unwrap() - .checked_div(ctx.accounts.redeemable_mint.supply as u128) - .unwrap(); - - let ido_name = ctx.accounts.ido_account.ido_name.as_ref(); - let seeds = &[ - ido_name.trim_ascii_whitespace(), - &[ctx.accounts.ido_account.bumps.ido_account], - ]; - let signer = &[&seeds[..]]; - - // Burn the user's redeemable tokens. - let cpi_accounts = Burn { - mint: ctx.accounts.redeemable_mint.to_account_info(), - from: ctx.accounts.user_redeemable.to_account_info(), - authority: ctx.accounts.ido_account.to_account_info(), - }; - let cpi_program = ctx.accounts.token_program.to_account_info(); - let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); - token::burn(cpi_ctx, amount)?; - - // Transfer Watermelon from pool account to user. - let cpi_accounts = Transfer { - from: ctx.accounts.pool_watermelon.to_account_info(), - to: ctx.accounts.user_watermelon.to_account_info(), - authority: ctx.accounts.ido_account.to_account_info(), - }; - let cpi_program = ctx.accounts.token_program.to_account_info(); - let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); - token::transfer(cpi_ctx, watermelon_amount as u64)?; - - // Send rent back to user if account is empty - ctx.accounts.user_redeemable.reload()?; - if ctx.accounts.user_redeemable.amount == 0 { - let cpi_accounts = CloseAccount { - account: ctx.accounts.user_redeemable.to_account_info(), - destination: ctx.accounts.user_authority.clone(), - authority: ctx.accounts.ido_account.to_account_info(), - }; - let cpi_program = ctx.accounts.token_program.to_account_info(); - let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); - token::close_account(cpi_ctx)?; - } - - Ok(()) - } - - #[access_control(ido_over(&ctx.accounts.ido_account))] - pub fn withdraw_pool_usdc(ctx: Context) -> Result<()> { - msg!("WITHDRAW POOL USDC"); - // Transfer total USDC from pool account to ido_authority account. - let ido_name = ctx.accounts.ido_account.ido_name.as_ref(); - let seeds = &[ - ido_name.trim_ascii_whitespace(), - &[ctx.accounts.ido_account.bumps.ido_account], - ]; - let signer = &[&seeds[..]]; - let cpi_accounts = Transfer { - from: ctx.accounts.pool_usdc.to_account_info(), - to: ctx.accounts.ido_authority_usdc.to_account_info(), - authority: ctx.accounts.ido_account.to_account_info(), - }; - let cpi_program = ctx.accounts.token_program.to_account_info(); - let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); - token::transfer(cpi_ctx, ctx.accounts.pool_usdc.amount)?; - - Ok(()) - } - - #[access_control(escrow_over(&ctx.accounts.ido_account))] - pub fn withdraw_from_escrow(ctx: Context, amount: u64) -> Result<()> { - msg!("WITHDRAW FROM ESCROW"); - // While token::transfer will check this, we prefer a verbose err msg. - if ctx.accounts.escrow_usdc.amount < amount { - return err!(ErrorCode::LowUsdc); - } - - let ido_name = ctx.accounts.ido_account.ido_name.as_ref(); - let seeds = &[ - ido_name.trim_ascii_whitespace(), - &[ctx.accounts.ido_account.bumps.ido_account], - ]; - let signer = &[&seeds[..]]; - - // Transfer USDC from user's escrow account to user's USDC account. - let cpi_accounts = Transfer { - from: ctx.accounts.escrow_usdc.to_account_info(), - to: ctx.accounts.user_usdc.to_account_info(), - authority: ctx.accounts.ido_account.to_account_info(), - }; - let cpi_program = ctx.accounts.token_program.to_account_info(); - let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); - token::transfer(cpi_ctx, amount)?; - - // Send rent back to user if account is empty - ctx.accounts.escrow_usdc.reload()?; - if ctx.accounts.escrow_usdc.amount == 0 { - let cpi_accounts = CloseAccount { - account: ctx.accounts.escrow_usdc.to_account_info(), - destination: ctx.accounts.user_authority.clone(), - authority: ctx.accounts.ido_account.to_account_info(), - }; - let cpi_program = ctx.accounts.token_program.to_account_info(); - let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); - token::close_account(cpi_ctx)?; - } - - Ok(()) - } -} - -#[derive(Accounts)] -#[instruction(ido_name: String, bumps: PoolBumps)] -pub struct InitializePool<'info> { - // IDO Authority accounts - #[account(mut)] - pub ido_authority: Signer<'info>, - // Watermelon Doesn't have to be an ATA because it could be DAO controlled - #[account(mut, - constraint = ido_authority_watermelon.owner == ido_authority.key(), - constraint = ido_authority_watermelon.mint == watermelon_mint.key())] - pub ido_authority_watermelon: Box>, - // IDO Accounts - #[account(init, - seeds = [ido_name.as_bytes()], - bump, - payer = ido_authority, - space = IdoAccount::LEN + 8 - )] - pub ido_account: Box>, - // TODO Confirm USDC mint address on mainnet or leave open as an option for other stables - #[account(constraint = usdc_mint.decimals == DECIMALS)] - pub usdc_mint: Box>, - #[account(init, - mint::decimals = DECIMALS, - mint::authority = ido_account, - seeds = [ido_name.as_bytes(), b"redeemable_mint".as_ref()], - bump, - payer = ido_authority - )] - pub redeemable_mint: Box>, - #[account(constraint = watermelon_mint.key() == ido_authority_watermelon.mint)] - pub watermelon_mint: Box>, - #[account(init, - token::mint = watermelon_mint, - token::authority = ido_account, - seeds = [ido_name.as_bytes(), b"pool_watermelon"], - bump, - payer = ido_authority - )] - pub pool_watermelon: Box>, - #[account(init, - token::mint = usdc_mint, - token::authority = ido_account, - seeds = [ido_name.as_bytes(), b"pool_usdc"], - bump, - payer = ido_authority - )] - pub pool_usdc: Box>, - // Programs and Sysvars - pub system_program: Program<'info, System>, - pub token_program: Program<'info, Token>, - pub rent: Sysvar<'info, Rent>, -} - -#[derive(Accounts)] -pub struct InitUserRedeemable<'info> { - // User Accounts - #[account(mut)] - pub user_authority: Signer<'info>, - #[account(init, - token::mint = redeemable_mint, - token::authority = ido_account, - seeds = [user_authority.key().as_ref(), - ido_account.ido_name.as_ref().trim_ascii_whitespace(), - b"user_redeemable"], - bump, - payer = user_authority - )] - pub user_redeemable: Box>, - // IDO Accounts - #[account(seeds = [ido_account.ido_name.as_ref().trim_ascii_whitespace()], - bump = ido_account.bumps.ido_account)] - pub ido_account: Box>, - #[account(seeds = [ido_account.ido_name.as_ref().trim_ascii_whitespace(), b"redeemable_mint"], - bump = ido_account.bumps.redeemable_mint)] - pub redeemable_mint: Box>, - // Programs and Sysvars - pub system_program: Program<'info, System>, - pub token_program: Program<'info, Token>, - pub rent: Sysvar<'info, Rent>, -} - -#[derive(Accounts)] -pub struct ExchangeUsdcForRedeemable<'info> { - // User Accounts - pub user_authority: Signer<'info>, - // TODO replace these with the ATA constraints when possible - #[account(mut, - constraint = user_usdc.owner == user_authority.key(), - constraint = user_usdc.mint == usdc_mint.key())] - pub user_usdc: Box>, - #[account(mut, - seeds = [user_authority.key().as_ref(), - ido_account.ido_name.as_ref().trim_ascii_whitespace(), - b"user_redeemable"], - bump)] - pub user_redeemable: Box>, - // IDO Accounts - #[account(seeds = [ido_account.ido_name.as_ref().trim_ascii_whitespace()], - bump = ido_account.bumps.ido_account, - has_one = usdc_mint)] - pub ido_account: Box>, - pub usdc_mint: Box>, - #[account(mut, - seeds = [ido_account.ido_name.as_ref().trim_ascii_whitespace(), b"redeemable_mint"], - bump = ido_account.bumps.redeemable_mint)] - pub redeemable_mint: Box>, - #[account(mut, - seeds = [ido_account.ido_name.as_ref().trim_ascii_whitespace(), b"pool_usdc"], - bump = ido_account.bumps.pool_usdc)] - pub pool_usdc: Box>, - // Programs and Sysvars - pub token_program: Program<'info, Token>, -} - -#[derive(Accounts)] -pub struct InitEscrowUsdc<'info> { - // User Accounts - #[account(mut)] - pub user_authority: Signer<'info>, - #[account(init, - token::mint = usdc_mint, - token::authority = ido_account, - seeds = [user_authority.key().as_ref(), - ido_account.ido_name.as_ref().trim_ascii_whitespace(), - b"escrow_usdc"], - bump, - payer = user_authority - )] - pub escrow_usdc: Box>, - #[account(seeds = [ido_account.ido_name.as_ref().trim_ascii_whitespace()], - bump = ido_account.bumps.ido_account, - has_one = usdc_mint)] - pub ido_account: Box>, - pub usdc_mint: Box>, - // Programs and Sysvars - pub system_program: Program<'info, System>, - pub token_program: Program<'info, Token>, - pub rent: Sysvar<'info, Rent>, -} - -#[derive(Accounts)] -pub struct ExchangeRedeemableForUsdc<'info> { - // User Accounts - pub user_authority: Signer<'info>, - #[account(mut, - seeds = [user_authority.key().as_ref(), - ido_account.ido_name.as_ref().trim_ascii_whitespace(), - b"escrow_usdc"], - bump)] - pub escrow_usdc: Box>, - #[account(mut, - seeds = [user_authority.key().as_ref(), - ido_account.ido_name.as_ref().trim_ascii_whitespace(), - b"user_redeemable"], - bump)] - pub user_redeemable: Box>, - // IDO Accounts - #[account(seeds = [ido_account.ido_name.as_ref().trim_ascii_whitespace()], - bump = ido_account.bumps.ido_account, - has_one = usdc_mint)] - pub ido_account: Box>, - pub usdc_mint: Box>, - pub watermelon_mint: Box>, - #[account(mut, - seeds = [ido_account.ido_name.as_ref().trim_ascii_whitespace(), b"redeemable_mint"], - bump = ido_account.bumps.redeemable_mint)] - pub redeemable_mint: Box>, - #[account(mut, - seeds = [ido_account.ido_name.as_ref().trim_ascii_whitespace(), b"pool_usdc"], - bump = ido_account.bumps.pool_usdc)] - pub pool_usdc: Box>, - // Programs and Sysvars - pub token_program: Program<'info, Token>, -} - -#[derive(Accounts)] -pub struct ExchangeRedeemableForWatermelon<'info> { - // User does not have to sign, this allows anyone to redeem on their behalf - // and prevents forgotten / leftover redeemable tokens in the IDO pool. - pub payer: Signer<'info>, - // User Accounts - #[account(mut)] // Sol rent from empty redeemable account is refunded to the user - pub user_authority: AccountInfo<'info>, - // TODO replace with ATA constraints - #[account(mut, - constraint = user_watermelon.owner == user_authority.key(), - constraint = user_watermelon.mint == watermelon_mint.key())] - pub user_watermelon: Box>, - #[account(mut, - seeds = [user_authority.key().as_ref(), - ido_account.ido_name.as_ref().trim_ascii_whitespace(), - b"user_redeemable"], - bump)] - pub user_redeemable: Box>, - // IDO Accounts - #[account(seeds = [ido_account.ido_name.as_ref().trim_ascii_whitespace()], - bump = ido_account.bumps.ido_account, - has_one = watermelon_mint)] - pub ido_account: Box>, - pub watermelon_mint: Box>, - #[account(mut, - seeds = [ido_account.ido_name.as_ref().trim_ascii_whitespace(), b"redeemable_mint"], - bump = ido_account.bumps.redeemable_mint)] - pub redeemable_mint: Box>, - #[account(mut, - seeds = [ido_account.ido_name.as_ref().trim_ascii_whitespace(), b"pool_watermelon"], - bump = ido_account.bumps.pool_watermelon)] - pub pool_watermelon: Box>, - // Programs and Sysvars - pub token_program: Program<'info, Token>, -} - -#[derive(Accounts)] -pub struct WithdrawPoolUsdc<'info> { - // IDO Authority Accounts - pub ido_authority: Signer<'info>, - // Doesn't need to be an ATA because it might be a DAO account - #[account(mut, - constraint = ido_authority_usdc.owner == ido_authority.key(), - constraint = ido_authority_usdc.mint == usdc_mint.key())] - pub ido_authority_usdc: Box>, - // IDO Accounts - #[account(seeds = [ido_account.ido_name.as_ref().trim_ascii_whitespace()], - bump = ido_account.bumps.ido_account, - has_one = ido_authority, - has_one = usdc_mint, - has_one = watermelon_mint)] - pub ido_account: Box>, - pub usdc_mint: Box>, - pub watermelon_mint: Box>, - #[account(mut, - seeds = [ido_account.ido_name.as_ref().trim_ascii_whitespace(), b"pool_usdc"], - bump = ido_account.bumps.pool_usdc)] - pub pool_usdc: Box>, - // Program and Sysvars - pub token_program: Program<'info, Token>, -} - -#[derive(Accounts)] -pub struct WithdrawFromEscrow<'info> { - // User does not have to sign, this allows anyone to redeem on their behalf - // and prevents forgotten / leftover USDC in the IDO pool. - pub payer: Signer<'info>, - // User Accounts - #[account(mut)] - pub user_authority: AccountInfo<'info>, - #[account(mut, - constraint = user_usdc.owner == user_authority.key(), - constraint = user_usdc.mint == usdc_mint.key())] - pub user_usdc: Box>, - #[account(mut, - seeds = [user_authority.key().as_ref(), - ido_account.ido_name.as_ref().trim_ascii_whitespace(), - b"escrow_usdc"], - bump)] - pub escrow_usdc: Box>, - // IDO Accounts - #[account(seeds = [ido_account.ido_name.as_ref().trim_ascii_whitespace()], - bump = ido_account.bumps.ido_account, - has_one = usdc_mint)] - pub ido_account: Box>, - pub usdc_mint: Box>, - // Programs and Sysvars - pub token_program: Program<'info, Token>, -} - -#[account] -pub struct IdoAccount { - pub ido_name: [u8; 10], // Setting an arbitrary max of ten characters in the ido name. // 10 - pub bumps: PoolBumps, // 4 - pub ido_authority: Pubkey, // 32 - - pub usdc_mint: Pubkey, // 32 - pub redeemable_mint: Pubkey, // 32 - pub watermelon_mint: Pubkey, // 32 - pub pool_usdc: Pubkey, // 32 - pub pool_watermelon: Pubkey, // 32 - - pub num_ido_tokens: u64, // 8 - pub ido_times: IdoTimes, // 32 -} - -impl IdoAccount { - pub const LEN: usize = 10 + 4 + 32 + 5 * 32 + 8 + 32; -} - -#[derive(AnchorSerialize, AnchorDeserialize, Default, Clone, Copy)] -pub struct IdoTimes { - pub start_ido: i64, // 8 - pub end_deposits: i64, // 8 - pub end_ido: i64, // 8 - pub end_escrow: i64, // 8 -} - -#[derive(AnchorSerialize, AnchorDeserialize, Default, Clone)] -pub struct PoolBumps { - pub ido_account: u8, // 1 - pub redeemable_mint: u8, // 1 - pub pool_watermelon: u8, // 1 - pub pool_usdc: u8, // 1 -} - -#[error_code] -pub enum ErrorCode { - #[msg("IDO must start in the future")] - IdoFuture, - #[msg("IDO times are non-sequential")] - SeqTimes, - #[msg("IDO has not started")] - StartIdoTime, - #[msg("Deposits period has ended")] - EndDepositsTime, - #[msg("IDO has ended")] - EndIdoTime, - #[msg("IDO has not finished yet")] - IdoNotOver, - #[msg("Escrow period has not finished yet")] - EscrowNotOver, - #[msg("Insufficient USDC")] - LowUsdc, - #[msg("Insufficient redeemable tokens")] - LowRedeemable, - #[msg("USDC total and redeemable total don't match")] - UsdcNotEqRedeem, - #[msg("Given nonce is invalid")] - InvalidNonce, -} - -// Access control modifiers. - -// Asserts the IDO starts in the future. -fn validate_ido_times(ido_times: IdoTimes) -> Result<()> { - let clock = Clock::get()?; - if ido_times.start_ido <= clock.unix_timestamp { - return err!(ErrorCode::IdoFuture); - } - if !(ido_times.start_ido < ido_times.end_deposits - && ido_times.end_deposits < ido_times.end_ido - && ido_times.end_ido < ido_times.end_escrow) - { - return err!(ErrorCode::SeqTimes); - } - Ok(()) -} - -// Asserts the IDO is still accepting deposits. -fn unrestricted_phase(ido_account: &IdoAccount) -> Result<()> { - let clock = Clock::get()?; - if clock.unix_timestamp <= ido_account.ido_times.start_ido { - return err!(ErrorCode::StartIdoTime); - } else if ido_account.ido_times.end_deposits <= clock.unix_timestamp { - return err!(ErrorCode::EndDepositsTime); - } - Ok(()) -} - -// Asserts the IDO has started but not yet finished. -fn withdraw_phase(ido_account: &IdoAccount) -> Result<()> { - let clock = Clock::get()?; - if clock.unix_timestamp <= ido_account.ido_times.start_ido { - return err!(ErrorCode::StartIdoTime); - } else if ido_account.ido_times.end_ido <= clock.unix_timestamp { - return err!(ErrorCode::EndIdoTime); - } - Ok(()) -} - -// Asserts the IDO sale period has ended. -fn ido_over(ido_account: &IdoAccount) -> Result<()> { - let clock = Clock::get()?; - if clock.unix_timestamp <= ido_account.ido_times.end_ido { - return err!(ErrorCode::IdoNotOver); - } - Ok(()) -} - -fn escrow_over(ido_account: &IdoAccount) -> Result<()> { - let clock = Clock::get()?; - if clock.unix_timestamp <= ido_account.ido_times.end_escrow { - return err!(ErrorCode::EscrowNotOver); - } - Ok(()) -} - -/// Trait to allow trimming ascii whitespace from a &[u8]. -pub trait TrimAsciiWhitespace { - /// Trim ascii whitespace (based on `is_ascii_whitespace()`) from the - /// start and end of a slice. - fn trim_ascii_whitespace(&self) -> &[u8]; -} - -impl> TrimAsciiWhitespace for T { - fn trim_ascii_whitespace(&self) -> &[u8] { - let from = match self.iter().position(|x| !x.is_ascii_whitespace()) { - Some(i) => i, - None => return &self[0..0], - }; - let to = self.iter().rposition(|x| !x.is_ascii_whitespace()).unwrap(); - &self[from..=to] - } -} diff --git a/tests/ido-pool/tests/ido-pool.js b/tests/ido-pool/tests/ido-pool.js deleted file mode 100644 index 03533c1a..00000000 --- a/tests/ido-pool/tests/ido-pool.js +++ /dev/null @@ -1,607 +0,0 @@ -const anchor = require("@project-serum/anchor"); -const { assert } = require("chai"); -const { - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - Token, -} = require("@solana/spl-token"); -const { - sleep, - getTokenAccount, - createMint, - createTokenAccount, -} = require("./utils"); -const { token } = require("@project-serum/anchor/dist/cjs/utils"); - -describe("ido-pool", () => { - const provider = anchor.AnchorProvider.local(); - - // Configure the client to use the local cluster. - anchor.setProvider(provider); - - const program = anchor.workspace.IdoPool; - - // All mints default to 6 decimal places. - const watermelonIdoAmount = new anchor.BN(5000000); - - // These are all of the variables we assume exist in the world already and - // are available to the client. - let usdcMintAccount = null; - let usdcMint = null; - let watermelonMintAccount = null; - let watermelonMint = null; - let idoAuthorityUsdc = null; - let idoAuthorityWatermelon = null; - - it("Initializes the state-of-the-world", async () => { - usdcMintAccount = await createMint(provider); - watermelonMintAccount = await createMint(provider); - usdcMint = usdcMintAccount.publicKey; - watermelonMint = watermelonMintAccount.publicKey; - idoAuthorityUsdc = await createTokenAccount( - provider, - usdcMint, - provider.wallet.publicKey - ); - idoAuthorityWatermelon = await createTokenAccount( - provider, - watermelonMint, - provider.wallet.publicKey - ); - // Mint Watermelon tokens that will be distributed from the IDO pool. - await watermelonMintAccount.mintTo( - idoAuthorityWatermelon, - provider.wallet.publicKey, - [], - watermelonIdoAmount.toString() - ); - idoAuthority_watermelon_account = await getTokenAccount( - provider, - idoAuthorityWatermelon - ); - assert.isTrue( - idoAuthority_watermelon_account.amount.eq(watermelonIdoAmount) - ); - }); - - // These are all variables the client will need to create in order to - // initialize the IDO pool - let idoTimes; - let idoName = "test_ido"; - - it("Initializes the IDO pool", async () => { - let bumps = new PoolBumps(); - - const [idoAccount, idoAccountBump] = - await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from(idoName)], - program.programId - ); - bumps.idoAccount = idoAccountBump; - - const [redeemableMint, redeemableMintBump] = - await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from(idoName), Buffer.from("redeemable_mint")], - program.programId - ); - bumps.redeemableMint = redeemableMintBump; - - const [poolWatermelon, poolWatermelonBump] = - await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from(idoName), Buffer.from("pool_watermelon")], - program.programId - ); - bumps.poolWatermelon = poolWatermelonBump; - - const [poolUsdc, poolUsdcBump] = - await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from(idoName), Buffer.from("pool_usdc")], - program.programId - ); - bumps.poolUsdc = poolUsdcBump; - - idoTimes = new IdoTimes(); - const nowBn = new anchor.BN(Date.now() / 1000); - idoTimes.startIdo = nowBn.add(new anchor.BN(5)); - idoTimes.endDeposits = nowBn.add(new anchor.BN(10)); - idoTimes.endIdo = nowBn.add(new anchor.BN(15)); - idoTimes.endEscrow = nowBn.add(new anchor.BN(16)); - - await program.rpc.initializePool( - idoName, - bumps, - watermelonIdoAmount, - idoTimes, - { - accounts: { - idoAuthority: provider.wallet.publicKey, - idoAuthorityWatermelon, - idoAccount, - watermelonMint, - usdcMint, - redeemableMint, - poolWatermelon, - poolUsdc, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - } - ); - - idoAuthorityWatermelonAccount = await getTokenAccount( - provider, - idoAuthorityWatermelon - ); - assert.isTrue(idoAuthorityWatermelonAccount.amount.eq(new anchor.BN(0))); - }); - - // We're going to need to start using the associated program account for creating token accounts - // if not in testing, then definitely in production. - - let userUsdc = null; - // 10 usdc - const firstDeposit = new anchor.BN(10_000_349); - - it("Exchanges user USDC for redeemable tokens", async () => { - // Wait until the IDO has opened. - if (Date.now() < idoTimes.startIdo.toNumber() * 1000) { - await sleep(idoTimes.startIdo.toNumber() * 1000 - Date.now() + 2000); - } - - const [idoAccount] = await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from(idoName)], - program.programId - ); - - const [redeemableMint] = await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from(idoName), Buffer.from("redeemable_mint")], - program.programId - ); - - const [poolUsdc] = await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from(idoName), Buffer.from("pool_usdc")], - program.programId - ); - - userUsdc = await Token.getAssociatedTokenAddress( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - usdcMint, - program.provider.wallet.publicKey - ); - // Get the instructions to add to the RPC call - let createUserUsdcInstr = Token.createAssociatedTokenAccountInstruction( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - usdcMint, - userUsdc, - program.provider.wallet.publicKey, - program.provider.wallet.publicKey - ); - let createUserUsdcTrns = new anchor.web3.Transaction().add( - createUserUsdcInstr - ); - await provider.sendAndConfirm(createUserUsdcTrns); - await usdcMintAccount.mintTo( - userUsdc, - provider.wallet.publicKey, - [], - firstDeposit.toString() - ); - - // Check if we inited correctly - userUsdcAccount = await getTokenAccount(provider, userUsdc); - assert.isTrue(userUsdcAccount.amount.eq(firstDeposit)); - - const [userRedeemable] = await anchor.web3.PublicKey.findProgramAddress( - [ - provider.wallet.publicKey.toBuffer(), - Buffer.from(idoName), - Buffer.from("user_redeemable"), - ], - program.programId - ); - - try { - const tx = await program.rpc.exchangeUsdcForRedeemable(firstDeposit, { - accounts: { - userAuthority: provider.wallet.publicKey, - userUsdc, - userRedeemable, - idoAccount, - usdcMint, - redeemableMint, - watermelonMint, - poolUsdc, - tokenProgram: TOKEN_PROGRAM_ID, - }, - instructions: [ - program.instruction.initUserRedeemable({ - accounts: { - userAuthority: provider.wallet.publicKey, - userRedeemable, - idoAccount, - redeemableMint, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - }), - ], - }); - } catch (err) { - console.log("This is the error message", err.toString()); - } - poolUsdcAccount = await getTokenAccount(provider, poolUsdc); - assert.isTrue(poolUsdcAccount.amount.eq(firstDeposit)); - userRedeemableAccount = await getTokenAccount(provider, userRedeemable); - assert.isTrue(userRedeemableAccount.amount.eq(firstDeposit)); - }); - - // 23 usdc - const secondDeposit = new anchor.BN(23_000_672); - let totalPoolUsdc, secondUserKeypair, secondUserUsdc; - - it("Exchanges a second users USDC for redeemable tokens", async () => { - const [idoAccount] = await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from(idoName)], - program.programId - ); - - const [redeemableMint] = await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from(idoName), Buffer.from("redeemable_mint")], - program.programId - ); - - const [poolUsdc] = await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from(idoName), Buffer.from("pool_usdc")], - program.programId - ); - - secondUserKeypair = anchor.web3.Keypair.generate(); - - transferSolInstr = anchor.web3.SystemProgram.transfer({ - fromPubkey: provider.wallet.publicKey, - lamports: 100_000_000_000, // 100 sol - toPubkey: secondUserKeypair.publicKey, - }); - secondUserUsdc = await Token.getAssociatedTokenAddress( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - usdcMint, - secondUserKeypair.publicKey - ); - createSecondUserUsdcInstr = Token.createAssociatedTokenAccountInstruction( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - usdcMint, - secondUserUsdc, - secondUserKeypair.publicKey, - provider.wallet.publicKey - ); - let createSecondUserUsdcTrns = new anchor.web3.Transaction(); - createSecondUserUsdcTrns.add(transferSolInstr); - createSecondUserUsdcTrns.add(createSecondUserUsdcInstr); - await provider.sendAndConfirm(createSecondUserUsdcTrns); - await usdcMintAccount.mintTo( - secondUserUsdc, - provider.wallet.publicKey, - [], - secondDeposit.toString() - ); - - // Checking the transfer went through - secondUserUsdcAccount = await getTokenAccount(provider, secondUserUsdc); - assert.isTrue(secondUserUsdcAccount.amount.eq(secondDeposit)); - - const [secondUserRedeemable] = - await anchor.web3.PublicKey.findProgramAddress( - [ - secondUserKeypair.publicKey.toBuffer(), - Buffer.from(idoName), - Buffer.from("user_redeemable"), - ], - program.programId - ); - - await program.rpc.exchangeUsdcForRedeemable(secondDeposit, { - accounts: { - userAuthority: secondUserKeypair.publicKey, - userUsdc: secondUserUsdc, - userRedeemable: secondUserRedeemable, - idoAccount, - usdcMint, - redeemableMint, - watermelonMint, - poolUsdc, - tokenProgram: TOKEN_PROGRAM_ID, - }, - instructions: [ - program.instruction.initUserRedeemable({ - accounts: { - userAuthority: secondUserKeypair.publicKey, - userRedeemable: secondUserRedeemable, - idoAccount, - redeemableMint, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - }), - ], - signers: [secondUserKeypair], - }); - - secondUserRedeemableAccount = await getTokenAccount( - provider, - secondUserRedeemable - ); - assert.isTrue(secondUserRedeemableAccount.amount.eq(secondDeposit)); - - totalPoolUsdc = firstDeposit.add(secondDeposit); - poolUsdcAccount = await getTokenAccount(provider, poolUsdc); - assert.isTrue(poolUsdcAccount.amount.eq(totalPoolUsdc)); - }); - - const firstWithdrawal = new anchor.BN(2_000_000); - - it("Exchanges user Redeemable tokens for USDC", async () => { - const [idoAccount] = await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from(idoName)], - program.programId - ); - - const [redeemableMint] = await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from(idoName), Buffer.from("redeemable_mint")], - program.programId - ); - - const [poolUsdc] = await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from(idoName), Buffer.from("pool_usdc")], - program.programId - ); - - const [userRedeemable] = await anchor.web3.PublicKey.findProgramAddress( - [ - provider.wallet.publicKey.toBuffer(), - Buffer.from(idoName), - Buffer.from("user_redeemable"), - ], - program.programId - ); - - const [escrowUsdc] = await anchor.web3.PublicKey.findProgramAddress( - [ - provider.wallet.publicKey.toBuffer(), - Buffer.from(idoName), - Buffer.from("escrow_usdc"), - ], - program.programId - ); - - await program.rpc.exchangeRedeemableForUsdc(firstWithdrawal, { - accounts: { - userAuthority: provider.wallet.publicKey, - escrowUsdc, - userRedeemable, - idoAccount, - usdcMint, - redeemableMint, - watermelonMint, - poolUsdc, - tokenProgram: TOKEN_PROGRAM_ID, - }, - instructions: [ - program.instruction.initEscrowUsdc({ - accounts: { - userAuthority: provider.wallet.publicKey, - escrowUsdc, - idoAccount, - usdcMint, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - }), - ], - }); - - totalPoolUsdc = totalPoolUsdc.sub(firstWithdrawal); - poolUsdcAccount = await getTokenAccount(provider, poolUsdc); - assert.isTrue(poolUsdcAccount.amount.eq(totalPoolUsdc)); - escrowUsdcAccount = await getTokenAccount(provider, escrowUsdc); - assert.isTrue(escrowUsdcAccount.amount.eq(firstWithdrawal)); - }); - - it("Exchanges user Redeemable tokens for watermelon", async () => { - // Wait until the IDO has ended. - if (Date.now() < idoTimes.endIdo.toNumber() * 1000) { - await sleep(idoTimes.endIdo.toNumber() * 1000 - Date.now() + 3000); - } - - const [idoAccount] = await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from(idoName)], - program.programId - ); - - const [poolWatermelon] = await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from(idoName), Buffer.from("pool_watermelon")], - program.programId - ); - - const [redeemableMint] = await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from(idoName), Buffer.from("redeemable_mint")], - program.programId - ); - - const [userRedeemable] = await anchor.web3.PublicKey.findProgramAddress( - [ - provider.wallet.publicKey.toBuffer(), - Buffer.from(idoName), - Buffer.from("user_redeemable"), - ], - program.programId - ); - - let firstUserRedeemable = firstDeposit.sub(firstWithdrawal); - // TODO we've been lazy here and not used an ATA as we did with USDC - userWatermelon = await createTokenAccount( - provider, - watermelonMint, - provider.wallet.publicKey - ); - - await program.rpc.exchangeRedeemableForWatermelon(firstUserRedeemable, { - accounts: { - payer: provider.wallet.publicKey, - userAuthority: provider.wallet.publicKey, - userWatermelon, - userRedeemable, - idoAccount, - watermelonMint, - redeemableMint, - poolWatermelon, - tokenProgram: TOKEN_PROGRAM_ID, - }, - }); - - poolWatermelonAccount = await getTokenAccount(provider, poolWatermelon); - let redeemedWatermelon = firstUserRedeemable - .mul(watermelonIdoAmount) - .div(totalPoolUsdc); - let remainingWatermelon = watermelonIdoAmount.sub(redeemedWatermelon); - assert.isTrue(poolWatermelonAccount.amount.eq(remainingWatermelon)); - userWatermelonAccount = await getTokenAccount(provider, userWatermelon); - assert.isTrue(userWatermelonAccount.amount.eq(redeemedWatermelon)); - }); - - it("Exchanges second user's Redeemable tokens for watermelon", async () => { - const [idoAccount] = await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from(idoName)], - program.programId - ); - - const [redeemableMint] = await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from(idoName), Buffer.from("redeemable_mint")], - program.programId - ); - - const [secondUserRedeemable] = - await anchor.web3.PublicKey.findProgramAddress( - [ - secondUserKeypair.publicKey.toBuffer(), - Buffer.from(idoName), - Buffer.from("user_redeemable"), - ], - program.programId - ); - - const [poolWatermelon] = await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from(idoName), Buffer.from("pool_watermelon")], - program.programId - ); - - secondUserWatermelon = await createTokenAccount( - provider, - watermelonMint, - secondUserKeypair.publicKey - ); - - await program.rpc.exchangeRedeemableForWatermelon(secondDeposit, { - accounts: { - payer: provider.wallet.publicKey, - userAuthority: secondUserKeypair.publicKey, - userWatermelon: secondUserWatermelon, - userRedeemable: secondUserRedeemable, - idoAccount, - watermelonMint, - redeemableMint, - poolWatermelon, - tokenProgram: TOKEN_PROGRAM_ID, - }, - }); - - poolWatermelonAccount = await getTokenAccount(provider, poolWatermelon); - assert.isTrue(poolWatermelonAccount.amount.eq(new anchor.BN(0))); - }); - - it("Withdraws total USDC from pool account", async () => { - const [idoAccount] = await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from(idoName)], - program.programId - ); - - const [poolUsdc] = await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from(idoName), Buffer.from("pool_usdc")], - program.programId - ); - - await program.rpc.withdrawPoolUsdc({ - accounts: { - idoAuthority: provider.wallet.publicKey, - idoAuthorityUsdc, - idoAccount, - usdcMint, - watermelonMint, - poolUsdc, - tokenProgram: TOKEN_PROGRAM_ID, - }, - }); - - poolUsdcAccount = await getTokenAccount(provider, poolUsdc); - assert.isTrue(poolUsdcAccount.amount.eq(new anchor.BN(0))); - idoAuthorityUsdcAccount = await getTokenAccount(provider, idoAuthorityUsdc); - assert.isTrue(idoAuthorityUsdcAccount.amount.eq(totalPoolUsdc)); - }); - - it("Withdraws USDC from the escrow account after waiting period is over", async () => { - // Wait until the escrow period is over. - if (Date.now() < idoTimes.endEscrow.toNumber() * 1000 + 1000) { - await sleep(idoTimes.endEscrow.toNumber() * 1000 - Date.now() + 4000); - } - - const [idoAccount] = await anchor.web3.PublicKey.findProgramAddress( - [Buffer.from(idoName)], - program.programId - ); - - const [escrowUsdc] = await anchor.web3.PublicKey.findProgramAddress( - [ - provider.wallet.publicKey.toBuffer(), - Buffer.from(idoName), - Buffer.from("escrow_usdc"), - ], - program.programId - ); - - await program.rpc.withdrawFromEscrow(firstWithdrawal, { - accounts: { - payer: provider.wallet.publicKey, - userAuthority: provider.wallet.publicKey, - userUsdc, - escrowUsdc, - idoAccount, - usdcMint, - tokenProgram: TOKEN_PROGRAM_ID, - }, - }); - - userUsdcAccount = await getTokenAccount(provider, userUsdc); - assert.isTrue(userUsdcAccount.amount.eq(firstWithdrawal)); - }); - - function PoolBumps() { - this.idoAccount; - this.redeemableMint; - this.poolWatermelon; - this.poolUsdc; - } - - function IdoTimes() { - this.startIdo; - this.endDeposts; - this.endIdo; - this.endEscrow; - } -}); diff --git a/tests/ido-pool/tests/utils/index.js b/tests/ido-pool/tests/utils/index.js deleted file mode 100644 index 7356d406..00000000 --- a/tests/ido-pool/tests/utils/index.js +++ /dev/null @@ -1,54 +0,0 @@ -const spl = require("@solana/spl-token"); -const anchor = require("@project-serum/anchor"); -const serumCmn = require("@project-serum/common"); -const TokenInstructions = require("@project-serum/serum").TokenInstructions; - -// TODO: remove this constant once @project-serum/serum uses the same version -// of @solana/web3.js as anchor (or switch packages). -const TOKEN_PROGRAM_ID = new anchor.web3.PublicKey( - TokenInstructions.TOKEN_PROGRAM_ID.toString() -); - -// Our own sleep function. -function sleep(ms) { - console.log("Sleeping for", ms / 1000, "seconds"); - return new Promise((resolve) => setTimeout(resolve, ms)); -} - -async function getTokenAccount(provider, addr) { - return await serumCmn.getTokenAccount(provider, addr); -} - -async function createMint(provider, authority) { - if (authority === undefined) { - authority = provider.wallet.publicKey; - } - const mint = await spl.Token.createMint( - provider.connection, - provider.wallet.payer, - authority, - null, - 6, - TOKEN_PROGRAM_ID - ); - return mint; -} - -async function createTokenAccount(provider, mint, owner) { - const token = new spl.Token( - provider.connection, - mint, - TOKEN_PROGRAM_ID, - provider.wallet.payer - ); - let vault = await token.createAccount(owner); - return vault; -} - -module.exports = { - TOKEN_PROGRAM_ID, - sleep, - getTokenAccount, - createTokenAccount, - createMint, -}; diff --git a/tests/interface/Anchor.toml b/tests/interface/Anchor.toml deleted file mode 100644 index 3a313174..00000000 --- a/tests/interface/Anchor.toml +++ /dev/null @@ -1,12 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -counter = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" -counter_auth = "Aws2XRVHjNqCUbMmaU245ojT2DBJFYX58KVo2YySEeeP" - -[scripts] -test = "yarn run mocha -t 1000000 tests/" - -[features] diff --git a/tests/interface/Cargo.toml b/tests/interface/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/interface/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/interface/package.json b/tests/interface/package.json deleted file mode 100644 index ded2b0e0..00000000 --- a/tests/interface/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "interface", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/interface/programs/counter-auth/Cargo.toml b/tests/interface/programs/counter-auth/Cargo.toml deleted file mode 100644 index 62fdaefb..00000000 --- a/tests/interface/programs/counter-auth/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "counter-auth" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "counter_auth" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } -counter = { path = "../counter", features = ["cpi"] } diff --git a/tests/interface/programs/counter-auth/Xargo.toml b/tests/interface/programs/counter-auth/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/interface/programs/counter-auth/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/interface/programs/counter-auth/src/lib.rs b/tests/interface/programs/counter-auth/src/lib.rs deleted file mode 100644 index 544c74a0..00000000 --- a/tests/interface/programs/counter-auth/src/lib.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! counter-auth is an example of a program *implementing* an external program -//! interface. Here the `counter::Auth` trait, where we only allow a count -//! to be incremented if it changes the counter from odd -> even or even -> odd. -//! Creative, I know. :P. - -use anchor_lang::prelude::*; -use counter::Auth; - -declare_id!("Aws2XRVHjNqCUbMmaU245ojT2DBJFYX58KVo2YySEeeP"); - -#[program] -pub mod counter_auth { - use super::*; - - #[state] - pub struct CounterAuth; - - impl<'info> Auth<'info, Empty> for CounterAuth { - fn is_authorized(_ctx: Context, current: u64, new: u64) -> Result<()> { - if current % 2 == 0 { - if new % 2 == 0 { - return Err(ProgramError::Custom(15000).into()); // Arbitrary error code. - } - } else { - if new % 2 == 1 { - return Err(ProgramError::Custom(16000).into()); // Arbitrary error code. - } - } - Ok(()) - } - } -} - -#[derive(Accounts)] -pub struct Empty {} diff --git a/tests/interface/programs/counter/Cargo.toml b/tests/interface/programs/counter/Cargo.toml deleted file mode 100644 index 23326e74..00000000 --- a/tests/interface/programs/counter/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "counter" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "counter" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } diff --git a/tests/interface/programs/counter/Xargo.toml b/tests/interface/programs/counter/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/interface/programs/counter/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/interface/programs/counter/src/lib.rs b/tests/interface/programs/counter/src/lib.rs deleted file mode 100644 index b3f28b39..00000000 --- a/tests/interface/programs/counter/src/lib.rs +++ /dev/null @@ -1,73 +0,0 @@ -//! counter is an example program that depends on an external interface -//! that another program (here counter-auth/src/lib.rs) must implement. This allows -//! our program to depend on another program, without knowing anything about it -//! other than that it implements the `Auth` trait. -//! -//! Here, we have a counter, where, in order to set the count, the `Auth` -//! program must first approve the transaction. - -use anchor_lang::prelude::*; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -pub mod counter { - use super::*; - - #[state] - pub struct Counter { - pub count: u64, - pub auth_program: Pubkey, - } - - impl Counter { - pub fn new(_ctx: Context, auth_program: Pubkey) -> Result { - Ok(Self { - count: 0, - auth_program, - }) - } - - #[access_control(SetCount::accounts(&self, &ctx))] - pub fn set_count(&mut self, ctx: Context, new_count: u64) -> Result<()> { - // Ask the auth program if we should approve the transaction. - let cpi_program = ctx.accounts.auth_program.clone(); - let cpi_ctx = CpiContext::new(cpi_program, Empty {}); - auth::is_authorized(cpi_ctx, self.count, new_count)?; - - // Approved, so update. - self.count = new_count; - Ok(()) - } - } -} - -#[derive(Accounts)] -pub struct Empty {} - -#[derive(Accounts)] -pub struct SetCount<'info> { - auth_program: AccountInfo<'info>, -} - -impl<'info> SetCount<'info> { - // Auxiliary account validation requiring program inputs. As a convention, - // we separate it from the business logic of the instruction handler itself. - pub fn accounts(counter: &Counter, ctx: &Context) -> Result<()> { - if ctx.accounts.auth_program.key != &counter.auth_program { - return err!(ErrorCode::InvalidAuthProgram); - } - Ok(()) - } -} - -#[interface] -pub trait Auth<'info, T: Accounts<'info>> { - fn is_authorized(ctx: Context, current: u64, new: u64) -> Result<()>; -} - -#[error_code] -pub enum ErrorCode { - #[msg("Invalid auth program.")] - InvalidAuthProgram, -} diff --git a/tests/interface/tests/interface.js b/tests/interface/tests/interface.js deleted file mode 100644 index bbc3549e..00000000 --- a/tests/interface/tests/interface.js +++ /dev/null @@ -1,46 +0,0 @@ -const anchor = require("@project-serum/anchor"); -const { assert } = require("chai"); -const nativeAssert = require("assert"); - -describe("interface", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.env()); - - const counter = anchor.workspace.Counter; - const counterAuth = anchor.workspace.CounterAuth; - it("Is initialized!", async () => { - await counter.state.rpc.new(counterAuth.programId); - - const stateAccount = await counter.state.fetch(); - assert.isTrue(stateAccount.count.eq(new anchor.BN(0))); - assert.isTrue(stateAccount.authProgram.equals(counterAuth.programId)); - }); - - it("Should fail to go from even to even", async () => { - await nativeAssert.rejects( - async () => { - await counter.state.rpc.setCount(new anchor.BN(4), { - accounts: { - authProgram: counterAuth.programId, - }, - }); - }, - (err) => { - if (err.toString().split("custom program error: 0x3a98").length !== 2) { - return false; - } - return true; - } - ); - }); - - it("Should succeed to go from even to odd", async () => { - await counter.state.rpc.setCount(new anchor.BN(3), { - accounts: { - authProgram: counterAuth.programId, - }, - }); - const stateAccount = await counter.state.fetch(); - assert.isTrue(stateAccount.count.eq(new anchor.BN(3))); - }); -}); diff --git a/tests/lockup/Anchor.toml b/tests/lockup/Anchor.toml deleted file mode 100644 index 0e20f4ad..00000000 --- a/tests/lockup/Anchor.toml +++ /dev/null @@ -1,12 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -lockup = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" -registry = "HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L" - -[scripts] -test = "yarn run mocha -t 1000000 tests/" - -[features] diff --git a/tests/lockup/Cargo.toml b/tests/lockup/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/lockup/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/lockup/docs/lockups.md b/tests/lockup/docs/lockups.md deleted file mode 100644 index 65727fcd..00000000 --- a/tests/lockup/docs/lockups.md +++ /dev/null @@ -1,127 +0,0 @@ -# Lockups - -WARNING: All code related to Lockups is unaudited. Use at your own risk. - -## Introduction - -The **Lockup** program provides a simple mechanism to lockup tokens -of any mint, and release those funds over time as defined by a vesting schedule. -Although these lockups track a target **beneficiary**, who will eventually receive the -funds upon vesting, a proper deployment of the program will ensure this **beneficiary** -can never actually retrieve tokens before vesting. Funds are *never* in an SPL -token wallet owned by a user, and are completely program controlled. - -## Accounts - -There is a single account type used by the program. - -* `Vesting` - An account defining a vesting schedule, realization condition, and vault holding the tokens to be released over time. - -## Creating a Vesting Account - -Lockup occurs when tokens are transferred into the program creating a **Vesting** -account on behalf of a **beneficiary** via the `CreateVesting` instruction. -There are three parameters to specify: - -* Start timestamp - unix timestamp (in seconds) of the time when vesting begins. -* End timestamp - unix timestamp (in seconds) of the time when all tokens will unlock. -* Period count - the amount of times vesting should occur. -* Deposit amount - the total amount to vest. -* Realizer - the program defining if and when vested tokens can be distributed to a beneficiary. - -Together these parameters form a linearly unlocked vesting schedule. For example, -if one wanted to lock 100 SPL tokens that unlocked twice, 50 each time, over the next year, one -would use the following parameters (in JavaScript). - -```javascript -const startTimestamp = Date.now()/1000; -const endTimestamp = Date.now()/1000 + 60*60*24*365; -const periodCount = 2; -const depositAmount = 100 * 10**6; // 6 decimal places. -const realizer = null; // No realizer in this example. -``` - -From these five parameters, one can deduce the total amount vested at any given time. - -Once created, a **Vesting** account's schedule cannot be mutated. - -## Withdrawing from a Vesting Account - -Withdrawing is straightforward. Simply invoke the `Withdraw` instruction, specifying an -amount to withdraw from a **Vesting** account. The **beneficiary** of the -**Vesting** account must sign the transaction, but if enough time has passed for an -amount to be vested, and, if the funds are indeed held in the lockup program's vault -(a point mentioned below) then the program will release the funds. - -## Realizing Locked Tokens - -Optionally, vesting accounts can be created with a `realizer` program, which is -a program implementing the lockup program's `RealizeLock` trait. In -addition to the vesting schedule, a `realizer` program determines if and when a -beneficiary can ever seize control over locked funds. It's effectively a function -returning a boolean: is realized or not. - -The uses cases for a realizer are application specific. -For example, in the case of the staking program, when a vesting account is distributed as a reward, -the staking program sets itself as the realizor, ensuring that the only way for the vesting account -to be realized is if the beneficiary completely unstakes and incurs the unbonding timelock alongside -any other consequences of unstaking (e.g., the inability to vote on governance proposals). -This implies that, if one never unstakes, one never receives locked token rewards, adding -an additional consideration when managing one's stake. - -If no such `realizer` exists, tokens are realized upon account creation. - -## Whitelisted Programs - -Although funds cannot be freely withdrawn prior to vesting, they can be sent to/from -other programs that are part of a **Whitelist**. These programs are completely trusted. -Any bug or flaw in the design of a whitelisted program can lead to locked tokens being released -ahead of schedule, so it's important to take great care when whitelisting any program. - -This of course begs the question, who approves the whitelist? The **Lockup** program doesn't -care. There simply exists an **authority** key that can, for example, be a democratic multisig, -a single admin, or the zero address--in which case the authority ceases to exist, as the -program will reject transactions signing from that address. Although the **authority** can never -move a **Vesting** account's funds, whoever controls the **authority** key -controls the whitelist. So when using the **Lockup** program, one should always be -cognizant of its whitelist governance, which ultimately anchors one's trust in the program, -if any at all. - -## Creating a Whitelisted Program - -To create a whitelisted program that receives withdrawals/deposits from/to the Lockup program, -one needs to implement the whitelist transfer interface, which assumes nothing about the -`instruction_data` but requires accounts to be provided in a specific [order](https://github.com/project-serum/serum-dex/blob/master/registry/program/src/deposit.rs#L18). - -Take staking locked tokens as a working example. - -### Staking Locked Tokens - -Suppose you have a vesting account with some funds you want to stake. - -First, one must add the staking **Registry** as a whitelisted program, so that the Lockup program -allows the movement of funds. This is done by the `WhitelistAdd` instruction. - -Once whitelisted, **Vesting** accounts can transfer funds out of the **Lockup** program and -into the **Registry** program by invoking the **Lockup** program's `WhitelistWithdraw` -instruction, which, other than access control, simply relays the instruction from the -**Lockup** program to the **Registry** program along with accounts, signing the -Cross-Program-Invocation (CPI) with the **Lockup**'s program-derived-address to allow -the transfer of funds, which ultimately is done by the **Registry**. *It is the Registry's responsibility -to track where these funds came from, keep them locked, and eventually send them back.* - -When creating this instruction on the client, there are two parameters to provide: -the maximum `amount` available for transfer and the opaque CPI `instruction_data`. -In the example, here, it would be the Borsh serialized instruction data for the -**Registry**'s `Deposit` instruction. - -The other direction follows, similarly. One invokes the `WhitelistDeposit` instruction -on the **LockupProgram**, relaying the transaction to the **Registry**, which ultimately -transfer funds back into the lockup program on behalf of the **Vesting** account. - -## Major version upgrades. - -Assuming the `authority` account is set on the **Lockup** program, one can use this Whitelist -mechanism to do major version upgrades of the lockup program. One can whitelist the -new **Lockup** program, and then all **Vesting** accounts would invidiually perform the migration -by transferring their funds to the new proigram via the `WhitelistWithdraw` instruction. diff --git a/tests/lockup/docs/staking.md b/tests/lockup/docs/staking.md deleted file mode 100644 index 627c3726..00000000 --- a/tests/lockup/docs/staking.md +++ /dev/null @@ -1,193 +0,0 @@ -# Staking - -WARNING: All code related to staking is unaudited. Use at your own risk. - -## Introduction - -The **Registry** program provides an on-chain mechanism for a group of stakers to - -* Share rewards proprtionally amongst a staking pool -* Govern on chain protocols with stake weighted voting -* Stake and earn locked tokens - -The program makes little assumptions about the form of stake or rewards. -In the same way you can make a new SPL token with its own mint, you can create a new stake -pool. Although the token being staked must be a predefined mint upon pool initialization, -rewards on a particular pool can be arbitrary SPL tokens, or, in the case of locked rewards, -program controlled accounts. -Rewards can come from an arbitrary -wallet, e.g. automatically from a fee earning program, -or manually from a wallet owned by an individual. The specifics are token and protocol -dependent. - -Similarly, the specifics of governance are not assumed by the staking program. However, a -governance system can use this program as a primitive to implement stake weighted voting. - -Here staking is covered at somewhat of a low level with the goal of allowing one -to understand, contribute to, or modify the code. - -## Accounts - -Accounts are the pieces of state owned by a Solana program. For reference while reading, here are all -accounts used by the **Registry** program. - -* `Registrar` - Analagous to an SPL token `Mint`, the `Registrar` defines a staking instance. It has its own pool, and it's own set of rewards distributed amongst its own set of stakers. -* `Member` - Analogous to an SPL token `Account`, `Member` accounts represent a **beneficiary**'s (i.e. a wallet's) stake state. This account has several vaults, all of which represent the funds belonging to an individual user. -* `PendingWithdrawal` - A transfer out of the staking pool (poorly named since it's not a withdrawal out of the program. But a withdrawal out of the staking pool and into a `Member`'s freely available balances). -* `RewardVendor` - A reward that has been dropped onto stakers and is distributed pro rata to staked `Member` beneficiaries. -* `RewardEventQueue` - A ring buffer of all rewards available to stakers. Each entry is the address of a `RewardVendor`. - -## Creating a member account. - -Before being able to enter the stake pool, one must create a **Member** account with the -**Registrar**, providing identity to the **Registry** program. By default, each member has -four types of token vaults making up a set of balances owned by the program on behalf of a -**Member**: - -* Available balances: a zero-interest earning token account with no restrictions. -* Pending: unstaked funds incurring an unbonding timelock. -* Stake: the total amount of tokens staked. -* Stake pool token: the total amount of pool tokens created from staking (`stake = stake-pool-token * stake-pool-token-price`). - -Each of these vaults provide a unit of balance isolation unique to a **Member**. -That is, although the stake program appears to provide a pooling mechanism, funds between -**Member** accounts are not commingled. They do not share SPL token accounts, and the only -way for funds to move is for a **Member**'s beneficiary to authorize instructions that either exit the -system or move funds between a **Member**'s own vaults. - -## Depositing and Withdrawing. - -Funds initially enter and exit the program through the `Deposit` and `Withdraw` instructions, -which transfer funds into and out of the **available balances** vault. -As the name suggests, all funds in this vault are freely available, unrestricted, and -earn zero interest. The vault is purely a gateway for funds to enter the program. - -## Staking. - -Once deposited, a **Member** beneficiary invokes the `Stake` instruction to transfer funds from -their **available-balances-vault** to one's **stake-vault**, creating newly minted -**stake-pool-tokens** as proof of the stake deposit. These new tokens represent -one's proportional right to all rewards distributed to the staking pool and are offered -by the **Registry** program at a fixed price, e.g., of 500 SPL tokens. - -## Unstaking - -Once staked, funds cannot be immediately withdrawn. Rather, the **Registrar** will enforce -a one week timelock before funds are released. Upon executing the `StartUnstake` -instruction, three operations execute. 1) The given amount of stake pool tokens will be burned. -2) Staked funds proportional to the stake pool tokens burned will be transferred from the -**Member**'s **stake-vault** to the **Member**'s **pending-vault**. 3) A `PendingWithdrawal` -account will be created as proof of the stake withdrawal, stamping the current block's -`unix_timestamp` onto the account. When the timelock period ends, a **Member** can invoke the -`EndUnstake` instruction to complete the transfer out of the `pending-vault` and -into the `available-balances`, providing the previously printed `PendingWithdrawal` -receipt to the program as proof that the timelock has passed. At this point, the exit -from the stake pool is complete, and the funds are ready to be used again. - -## Reward Design Motivation - -Feel free to skip this section and jump to the **Reward Vendors** section if you want to -just see how rewards work. - -One could imagine several ways to drop rewards onto a staking pool, each with there own downsides. -Of course what you want is, for a given reward amount, to atomically snapshot the state -of the staking pool and to distribute it proportionally to all stake holders. Effectively, -an on chain program such as - -```python -for account in stake_pool: - account.token_amount += total_reward * (account.stake_pool_token.amount / stake_pool_token.supply) - ``` - -Surprisingly, such a mechanism is not immediately obvious. - -First, the above program is a non starter. Not only does the SPL token -program not have the ability to iterate through all accounts for a given mint within a program, -but, since Solana transactions require the specification of all accounts being accessed -in a transaction (this is how it achieves parallelism), such a transaction's size would be -well over the limit. So modifying global state atomically in a single transaction is out of the -question. - -So if you can't do this on chain, one can try doing it off chain. One could write an program to -snapshot the pool state, and just airdrop tokens onto the pool. This works, but -adds an additional layer of trust. Who snapshots the pool state? At what time? -How do you know they calculated the rewards correctly? What happens if my reward was not given? -This is not auditable or verifiable. And if you want to answer these questions, requires -complex off-chain protocols that require either fancy cryptography or effectively -recreating a BFT system off chain. - -Another solution considerered was to use a uniswap-style AMM pool (without the swapping). -This has a lot of advantages. First it's easy to reason about and implement in a single transaction. -To drop rewards gloablly onto the pool, one can deposit funds directly into the pool, in which case -the reward is automatically received by owners of the staking pool token upon redemption, a process -known as "gulping"--since dropping rewards increases the total value of the pool -while their proportion of the pool remained constant. - -However, there are enough downsides with using an AMM style pool to offset the convience. -Unfortunately, it loses the nice balance isolation property **Member** accounts have, because -tokens have to be pooled into the same vault, which is an additional security concern that could -easily lead to loss of funds, e.g., if there's a bug in the redemption calculation. Moreover, dropping -arbitrary tokens onto the pool is a challenge. Not only do you have to create new pool vaults for -every new token you drop onto the pool, but you also need to have stakers purchase those tokens to enter -the pool, effectively requiring one to stake other unintended tokens. An additional oddity is that -as rewards are dropped onto the pool, the price to enter the pool monotonically increases. Remember, entering this -type of pool requires "creating" pool tokens, i.e., depositing enough tokens so that you don't dilute -any other member. So if a single pool token represents one SPL token. And if an additional SPL token is dropped onto every -member of the pool, all the existing member's shares are now worth two SPL tokens. So to enter the pool without -dilution, one would have to "create" at a price of 2 SPL tokens per share. This means that rewarding -stakers becomes more expensive over time. One could of course solve this problem by implementing -arbitrary `n:m` pool token splits, which leads right back to the problem of mutating global account -state for an SPL token. - -Furthermore, dropping arbitrary program accounts as rewards hasn't even been covered, for example, -locked token rewards, which of course can't be dropped directly onto an AMM style pool, since they are not tokens. -So, if one did go with an AMM style pool, one would need a separate mechanism for handling more general rewards like -locked token accounts. Ideally, there would be a single mechanism for both. - -## Reward Vendors - -Instead of trying to *push* rewards to users via a direct transfer or airdrop, one can use a *polling* model -where users effectively event source a log on demand, providing a proof one is eligible for the reward. - -When a reward is created, the program must do two things: - -1) Create a **Reward Vendor** account with an associated token vault holding the reward. -2) Assign the **Reward Vendor** the next available position in a **Reward Event Queue**. Then, to retrieve -a reward, a staker invokes the `ClaimReward` command, providing a proof that the funds were -staked at the time of the reward being dropped, and in response, the program transfers or, -some might say, *vends* the proportion of the dropped reward to the polling **Member**. The -operation completes by incrementing the **Member**'s queue cursor, ensuring that a given -reward can only be processed once. - -This allows the program to drop rewards on the stake pool in a way that is -on chain and verifiable. Of course, it requires an external trigger, some account willing to -transfer funds to a new **RewardVendor**, but that is outside of the scope of the staking -program. The reward dropper can be an off chain BFT committee, or it can be an on-chain multisig. -It can be a charitable individual, or funds can flow directly from a fee paying program such as the DEX, -which itself can create a Reward Vendor from fees collected. It doesn't matter to the **Registry** program. - -Note that this solution also allows for rewards to be denominated in any token, not just the token being staked. -Since rewards are paid out by the vendor immediately and to a token account of the **Member**'s -choosing, it *just works*. Even more, this extends to arbitrary program accounts, particularly -**Locked** tokens. A **Reward Vendor** needs to additionally know the accounts and instruction data -to relay to the program, but otherwise, the mechanism is the same. The details of **Locked** tokens will -be explained in an additional document. - -### Realizing Locked Rewards - -In addition to a vesting schedule, locked rewards are subject to a realization condition defined by the -staking program. Specifically, locked tokens are **realized** upon completely unstaking. So if one never -unstakes and incurs the unbonding timelock, one never receives locked token rewards. - -## Misc - -### Member Accounts - -This document describes 4 vault types belonging to **Member** accounts. -However there are two types of balance groups: locked and unlocked. -As a result, there are really 8 vaults for each **Member**, 4 types of vaults in 2 separate sets, -each isolated from the other, so that locked tokens don't get mixed with unlocked tokens. - -## Future Work - -* Arbitrary program accounts as rewards. With the current design, it should be straightforward to generalize locked token rewards to arbitrary program accounts from arbitrary programs. diff --git a/tests/lockup/migrations/deploy.js b/tests/lockup/migrations/deploy.js deleted file mode 100644 index c1ac6f1d..00000000 --- a/tests/lockup/migrations/deploy.js +++ /dev/null @@ -1,177 +0,0 @@ -// deploy.js is a simple deploy script to initialize a program. This is run -// immediately after a deploy. - -const serumCmn = require("@project-serum/common"); -const anchor = require("@project-serum/anchor"); -const PublicKey = anchor.web3.PublicKey; - -module.exports = async function (provider) { - // Configure client to use the provider. - anchor.setProvider(provider); - - // Setup genesis state. - const registrarConfigs = await genesis(provider); - - // Program clients. - const lockup = anchor.workspace.Lockup; - const registry = anchor.workspace.Registry; - - // Registry state constructor. - await registry.state.rpc.new({ - accounts: { - lockupProgram: lockup.programId, - }, - }); - - // Lockup state constructor. - await lockup.state.rpc.new({ - accounts: { - authority: provider.wallet.publicKey, - }, - }); - - // Delete the default whitelist entries. - const defaultEntry = { programId: new anchor.web3.PublicKey.default() }; - await lockup.state.rpc.whitelistDelete(defaultEntry, { - accounts: { - authority: provider.wallet.publicKey, - }, - }); - - // Whitelist the registry. - await lockup.state.rpc.whitelistAdd( - { programId: registry.programId }, - { - accounts: { - authority: provider.wallet.publicKey, - }, - } - ); - - // Initialize all registrars. - const cfgKeys = Object.keys(registrarConfigs); - for (let k = 0; k < cfgKeys.length; k += 1) { - let r = registrarConfigs[cfgKeys[k]]; - const registrar = await registrarInit( - registry, - r.withdrawalTimelock, - r.stakeRate, - r.rewardQLen, - new anchor.web3.PublicKey(r.mint) - ); - r["registrar"] = registrar.toString(); - } - - // Generate code for whitelisting on UIs. - const code = generateCode(registry, lockup, registrarConfigs); - console.log("Generated whitelisted UI addresses:", code); -}; - -function generateCode(registry, lockup, registrarConfigs) { - const registrars = Object.keys(registrarConfigs) - .map((cfg) => `${cfg}: new PublicKey('${registrarConfigs[cfg].registrar}')`) - .join(","); - - const mints = Object.keys(registrarConfigs) - .map((cfg) => `${cfg}: new PublicKey('${registrarConfigs[cfg].mint}')`) - .join(","); - - return `{ -registryProgramId: new PublicKey('${registry.programId}'), -lockupProgramId: new PublicKey('${lockup.programId}'), -registrars: { ${registrars} }, -mints: { ${mints} }, - }`; -} - -async function genesis(provider) { - if ( - provider.connection._rpcEndpoint === "https://api.mainnet-beta.solana.com" - ) { - return { - srm: { - withdrawalTimelock: 60 * 60 * 24 * 7, // 1 week. - stakeRate: 500 * 10 ** 6, // 500 SRM. - rewardQLen: 150, - mint: "SRMuApVNdxXokk5GT7XD5cUUgXMBCoAz2LHeuAoKWRt", - }, - msrm: { - withdrawalTimelock: 60 * 60 * 24 * 7, // 1 week. - stakeRate: 1, - rewardQLen: 150, - mint: "MSRMcoVyrFxnSgo5uXwone5SKcGhT1KEJMFEkMEWf9L", - }, - }; - } else { - const [token1Mint, _god1] = await serumCmn.createMintAndVault( - provider, - new anchor.BN(10000000000000), - undefined, - 6 - ); - const [token2Mint, _god2] = await serumCmn.createMintAndVault( - provider, - new anchor.BN(10000000000), - undefined, - 0 - ); - return { - token1: { - withdrawalTimelock: 60 * 60 * 24 * 7, - stakeRate: 1000 * 10 ** 6, - rewardQLen: 150, - mint: token1Mint.toString(), - }, - token2: { - withdrawalTimelock: 60 * 60 * 24 * 7, - stakeRate: 1, - rewardQLen: 150, - mint: token2Mint.toString(), - }, - }; - } -} - -async function registrarInit( - registry, - _withdrawalTimelock, - _stakeRate, - rewardQLen, - mint -) { - const registrar = anchor.web3.Keypair.generate(); - const rewardQ = anchor.web3.Keypair.generate(); - const withdrawalTimelock = new anchor.BN(_withdrawalTimelock); - const stakeRate = new anchor.BN(_stakeRate); - const [registrarSigner, nonce] = - await anchor.web3.PublicKey.findProgramAddress( - [registrar.publicKey.toBuffer()], - registry.programId - ); - const poolMint = await serumCmn.createMint( - registry.provider, - registrarSigner - ); - await registry.rpc.initialize( - mint, - registry.provider.wallet.publicKey, - nonce, - withdrawalTimelock, - stakeRate, - rewardQLen, - { - accounts: { - registrar: registrar.publicKey, - poolMint, - rewardEventQ: rewardQ.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [registrar, rewardQ], - instructions: [ - await registry.account.registrar.createInstruction(registrar), - await registry.account.rewardQueue.createInstruction(rewardQ, 8250), - ], - } - ); - return registrar.publicKey; -} diff --git a/tests/lockup/package.json b/tests/lockup/package.json deleted file mode 100644 index a5daf9d7..00000000 --- a/tests/lockup/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "lockup", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/lockup/programs/lockup/Cargo.toml b/tests/lockup/programs/lockup/Cargo.toml deleted file mode 100644 index e53f74d3..00000000 --- a/tests/lockup/programs/lockup/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "lockup" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "lockup" - -[features] -no-entrypoint = [] -cpi = ["no-entrypoint"] -anchor-deprecated-state = [] -default = ["anchor-deprecated-state"] - -[dependencies] -anchor-lang = { path = "../../../../lang" } -anchor-spl = { path = "../../../../spl" } diff --git a/tests/lockup/programs/lockup/Xargo.toml b/tests/lockup/programs/lockup/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/lockup/programs/lockup/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/lockup/programs/lockup/src/calculator.rs b/tests/lockup/programs/lockup/src/calculator.rs deleted file mode 100644 index c897fbd6..00000000 --- a/tests/lockup/programs/lockup/src/calculator.rs +++ /dev/null @@ -1,84 +0,0 @@ -//! Utility functions for calculating unlock schedules for a vesting account. - -use crate::Vesting; - -pub fn available_for_withdrawal(vesting: &Vesting, current_ts: i64) -> u64 { - std::cmp::min(outstanding_vested(vesting, current_ts), balance(vesting)) -} - -// The amount of funds currently in the vault. -fn balance(vesting: &Vesting) -> u64 { - vesting - .outstanding - .checked_sub(vesting.whitelist_owned) - .unwrap() -} - -// The amount of outstanding locked tokens vested. Note that these -// tokens might have been transferred to whitelisted programs. -fn outstanding_vested(vesting: &Vesting, current_ts: i64) -> u64 { - total_vested(vesting, current_ts) - .checked_sub(withdrawn_amount(vesting)) - .unwrap() -} - -// Returns the amount withdrawn from this vesting account. -fn withdrawn_amount(vesting: &Vesting) -> u64 { - vesting - .start_balance - .checked_sub(vesting.outstanding) - .unwrap() -} - -// Returns the total vested amount up to the given ts, assuming zero -// withdrawals and zero funds sent to other programs. -fn total_vested(vesting: &Vesting, current_ts: i64) -> u64 { - if current_ts < vesting.start_ts { - 0 - } else if current_ts >= vesting.end_ts { - vesting.start_balance - } else { - linear_unlock(vesting, current_ts).unwrap() - } -} - -fn linear_unlock(vesting: &Vesting, current_ts: i64) -> Option { - // Signed division not supported. - let current_ts = current_ts as u64; - let start_ts = vesting.start_ts as u64; - let end_ts = vesting.end_ts as u64; - - // If we can't perfectly partition the vesting window, - // push the start of the window back so that we can. - // - // This has the effect of making the first vesting period shorter - // than the rest. - let shifted_start_ts = - start_ts.checked_sub(end_ts.checked_sub(start_ts)? % vesting.period_count)?; - - // Similarly, if we can't perfectly divide up the vesting rewards - // then make the first period act as a cliff, earning slightly more than - // subsequent periods. - let reward_overflow = vesting.start_balance % vesting.period_count; - - // Reward per period ignoring the overflow. - let reward_per_period = - (vesting.start_balance.checked_sub(reward_overflow)?).checked_div(vesting.period_count)?; - - // Number of vesting periods that have passed. - let current_period = { - let period_secs = - (end_ts.checked_sub(shifted_start_ts)?).checked_div(vesting.period_count)?; - let current_period_count = - (current_ts.checked_sub(shifted_start_ts)?).checked_div(period_secs)?; - std::cmp::min(current_period_count, vesting.period_count) - }; - - if current_period == 0 { - return Some(0); - } - - current_period - .checked_mul(reward_per_period)? - .checked_add(reward_overflow) -} diff --git a/tests/lockup/programs/lockup/src/lib.rs b/tests/lockup/programs/lockup/src/lib.rs deleted file mode 100644 index 55eb0237..00000000 --- a/tests/lockup/programs/lockup/src/lib.rs +++ /dev/null @@ -1,542 +0,0 @@ -//! A relatively advanced example of a lockup program. If you're new to Anchor, -//! it's suggested to start with the other examples. - -use anchor_lang::accounts::state::ProgramState; -use anchor_lang::prelude::*; -use anchor_lang::solana_program; -use anchor_lang::solana_program::instruction::Instruction; -use anchor_spl::token::{self, TokenAccount, Transfer}; - -mod calculator; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -pub mod lockup { - use super::*; - - #[state] - pub struct Lockup { - /// The key with the ability to change the whitelist. - pub authority: Pubkey, - /// List of programs locked tokens can be sent to. These programs - /// are completely trusted to maintain the locked property. - pub whitelist: Vec, - } - - impl Lockup { - pub const WHITELIST_SIZE: usize = 10; - - pub fn new(ctx: Context) -> Result { - let mut whitelist = vec![]; - whitelist.resize(Self::WHITELIST_SIZE, Default::default()); - Ok(Lockup { - authority: *ctx.accounts.authority.key, - whitelist, - }) - } - - #[access_control(whitelist_auth(self, &ctx))] - pub fn whitelist_add(&mut self, ctx: Context, entry: WhitelistEntry) -> Result<()> { - if self.whitelist.len() == Self::WHITELIST_SIZE { - return err!(ErrorCode::WhitelistFull); - } - if self.whitelist.contains(&entry) { - return err!(ErrorCode::WhitelistEntryAlreadyExists); - } - self.whitelist.push(entry); - Ok(()) - } - - #[access_control(whitelist_auth(self, &ctx))] - pub fn whitelist_delete( - &mut self, - ctx: Context, - entry: WhitelistEntry, - ) -> Result<()> { - if !self.whitelist.contains(&entry) { - return err!(ErrorCode::WhitelistEntryNotFound); - } - self.whitelist.retain(|e| e != &entry); - Ok(()) - } - - #[access_control(whitelist_auth(self, &ctx))] - pub fn set_authority(&mut self, ctx: Context, new_authority: Pubkey) -> Result<()> { - self.authority = new_authority; - Ok(()) - } - } - - #[access_control(CreateVesting::accounts(&ctx, nonce))] - pub fn create_vesting( - ctx: Context, - beneficiary: Pubkey, - deposit_amount: u64, - nonce: u8, - start_ts: i64, - end_ts: i64, - period_count: u64, - realizor: Option, - ) -> Result<()> { - if deposit_amount == 0 { - return err!(ErrorCode::InvalidDepositAmount); - } - if !is_valid_schedule(start_ts, end_ts, period_count) { - return err!(ErrorCode::InvalidSchedule); - } - let vesting = &mut ctx.accounts.vesting; - vesting.beneficiary = beneficiary; - vesting.mint = ctx.accounts.vault.mint; - vesting.vault = *ctx.accounts.vault.to_account_info().key; - vesting.period_count = period_count; - vesting.start_balance = deposit_amount; - vesting.end_ts = end_ts; - vesting.start_ts = start_ts; - vesting.created_ts = ctx.accounts.clock.unix_timestamp; - vesting.outstanding = deposit_amount; - vesting.whitelist_owned = 0; - vesting.grantor = *ctx.accounts.depositor_authority.key; - vesting.nonce = nonce; - vesting.realizor = realizor; - - token::transfer(ctx.accounts.into(), deposit_amount)?; - - Ok(()) - } - - #[access_control(is_realized(&ctx))] - pub fn withdraw(ctx: Context, amount: u64) -> Result<()> { - // Has the given amount vested? - if amount - > calculator::available_for_withdrawal( - &ctx.accounts.vesting, - ctx.accounts.clock.unix_timestamp, - ) - { - return err!(ErrorCode::InsufficientWithdrawalBalance); - } - - // Transfer funds out. - let seeds = &[ - ctx.accounts.vesting.to_account_info().key.as_ref(), - &[ctx.accounts.vesting.nonce], - ]; - let signer = &[&seeds[..]]; - let cpi_ctx = CpiContext::from(&*ctx.accounts).with_signer(signer); - token::transfer(cpi_ctx, amount)?; - - // Bookeeping. - let vesting = &mut ctx.accounts.vesting; - vesting.outstanding -= amount; - - Ok(()) - } - - // Sends funds from the lockup program to a whitelisted program. - pub fn whitelist_withdraw<'a, 'b, 'c, 'info>( - ctx: Context<'a, 'b, 'c, 'info, WhitelistWithdraw<'info>>, - instruction_data: Vec, - amount: u64, - ) -> Result<()> { - let before_amount = ctx.accounts.transfer.vault.amount; - whitelist_relay_cpi( - &ctx.accounts.transfer, - ctx.remaining_accounts, - instruction_data, - )?; - ctx.accounts.transfer.vault.reload()?; - let after_amount = ctx.accounts.transfer.vault.amount; - - // CPI safety checks. - let withdraw_amount = before_amount - after_amount; - if withdraw_amount > amount { - return err!(ErrorCode::WhitelistWithdrawLimit); - } - - // Bookeeping. - ctx.accounts.transfer.vesting.whitelist_owned += withdraw_amount; - - Ok(()) - } - - // Sends funds from a whitelisted program back to the lockup program. - pub fn whitelist_deposit<'a, 'b, 'c, 'info>( - ctx: Context<'a, 'b, 'c, 'info, WhitelistDeposit<'info>>, - instruction_data: Vec, - ) -> Result<()> { - let before_amount = ctx.accounts.transfer.vault.amount; - whitelist_relay_cpi( - &ctx.accounts.transfer, - ctx.remaining_accounts, - instruction_data, - )?; - ctx.accounts.transfer.vault.reload()?; - let after_amount = ctx.accounts.transfer.vault.amount; - - // CPI safety checks. - let deposit_amount = after_amount - before_amount; - if deposit_amount <= 0 { - return err!(ErrorCode::InsufficientWhitelistDepositAmount); - } - if deposit_amount > ctx.accounts.transfer.vesting.whitelist_owned { - return err!(ErrorCode::WhitelistDepositOverflow)?; - } - - // Bookkeeping. - ctx.accounts.transfer.vesting.whitelist_owned -= deposit_amount; - - Ok(()) - } - - // Convenience function for UI's to calculate the withdrawable amount. - pub fn available_for_withdrawal(ctx: Context) -> Result<()> { - let available = calculator::available_for_withdrawal( - &ctx.accounts.vesting, - ctx.accounts.clock.unix_timestamp, - ); - // Log as string so that JS can read as a BN. - msg!(&format!("{{ \"result\": \"{}\" }}", available)); - Ok(()) - } -} - -#[derive(Accounts)] -pub struct Auth<'info> { - #[account(signer)] - authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct CreateVesting<'info> { - // Vesting. - #[account(zero)] - pub vesting: Account<'info, Vesting>, - #[account(mut)] - pub vault: Account<'info, TokenAccount>, - // Depositor. - #[account(mut)] - pub depositor: AccountInfo<'info>, - #[account(signer)] - pub depositor_authority: AccountInfo<'info>, - // Misc. - #[account(constraint = token_program.key == &token::ID)] - pub token_program: AccountInfo<'info>, - pub clock: Sysvar<'info, Clock>, -} - -impl<'info> CreateVesting<'info> { - fn accounts(ctx: &Context, nonce: u8) -> Result<()> { - let vault_authority = Pubkey::create_program_address( - &[ - ctx.accounts.vesting.to_account_info().key.as_ref(), - &[nonce], - ], - ctx.program_id, - ) - .map_err(|_| error!(ErrorCode::InvalidProgramAddress))?; - if ctx.accounts.vault.owner != vault_authority { - return err!(ErrorCode::InvalidVaultOwner)?; - } - - Ok(()) - } -} - -// All accounts not included here, i.e., the "remaining accounts" should be -// ordered according to the realization interface. -#[derive(Accounts)] -pub struct Withdraw<'info> { - // Vesting. - #[account(mut, has_one = beneficiary, has_one = vault)] - vesting: Account<'info, Vesting>, - beneficiary: Signer<'info>, - #[account(mut)] - vault: Account<'info, TokenAccount>, - #[account( - seeds = [vesting.to_account_info().key.as_ref()], - bump = vesting.nonce, - )] - vesting_signer: AccountInfo<'info>, - // Withdraw receiving target.. - #[account(mut)] - token: Account<'info, TokenAccount>, - // Misc. - #[account(constraint = token_program.key == &token::ID)] - token_program: AccountInfo<'info>, - clock: Sysvar<'info, Clock>, -} - -#[derive(Accounts)] -pub struct WhitelistWithdraw<'info> { - transfer: WhitelistTransfer<'info>, -} - -#[derive(Accounts)] -pub struct WhitelistDeposit<'info> { - transfer: WhitelistTransfer<'info>, -} - -#[derive(Accounts)] -pub struct WhitelistTransfer<'info> { - lockup: ProgramState<'info, Lockup>, - beneficiary: Signer<'info>, - whitelisted_program: AccountInfo<'info>, - - // Whitelist interface. - #[account(mut, has_one = beneficiary, has_one = vault)] - vesting: Account<'info, Vesting>, - #[account(mut, constraint = &vault.owner == vesting_signer.key)] - vault: Account<'info, TokenAccount>, - #[account( - seeds = [vesting.to_account_info().key.as_ref()], - bump = vesting.nonce, - )] - vesting_signer: AccountInfo<'info>, - #[account("token_program.key == &token::ID")] - token_program: AccountInfo<'info>, - #[account(mut)] - whitelisted_program_vault: AccountInfo<'info>, - whitelisted_program_vault_authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct AvailableForWithdrawal<'info> { - vesting: Account<'info, Vesting>, - clock: Sysvar<'info, Clock>, -} - -#[account] -pub struct Vesting { - /// The owner of this Vesting account. - pub beneficiary: Pubkey, - /// The mint of the SPL token locked up. - pub mint: Pubkey, - /// Address of the account's token vault. - pub vault: Pubkey, - /// The owner of the token account funding this account. - pub grantor: Pubkey, - /// The outstanding SRM deposit backing this vesting account. All - /// withdrawals will deduct this balance. - pub outstanding: u64, - /// The starting balance of this vesting account, i.e., how much was - /// originally deposited. - pub start_balance: u64, - /// The unix timestamp at which this vesting account was created. - pub created_ts: i64, - /// The time at which vesting begins. - pub start_ts: i64, - /// The time at which all tokens are vested. - pub end_ts: i64, - /// The number of times vesting will occur. For example, if vesting - /// is once a year over seven years, this will be 7. - pub period_count: u64, - /// The amount of tokens in custody of whitelisted programs. - pub whitelist_owned: u64, - /// Signer nonce. - pub nonce: u8, - /// The program that determines when the locked account is **realized**. - /// In addition to the lockup schedule, the program provides the ability - /// for applications to determine when locked tokens are considered earned. - /// For example, when earning locked tokens via the staking program, one - /// cannot receive the tokens until unstaking. As a result, if one never - /// unstakes, one would never actually receive the locked tokens. - pub realizor: Option, -} - -#[derive(AnchorSerialize, AnchorDeserialize, Clone, Debug)] -pub struct Realizor { - /// Program to invoke to check a realization condition. This program must - /// implement the `RealizeLock` trait. - pub program: Pubkey, - /// Address of an arbitrary piece of metadata interpretable by the realizor - /// program. For example, when a vesting account is allocated, the program - /// can define its realization condition as a function of some account - /// state. The metadata is the address of that account. - /// - /// In the case of staking, the metadata is a `Member` account address. When - /// the realization condition is checked, the staking program will check the - /// `Member` account defined by the `metadata` has no staked tokens. - pub metadata: Pubkey, -} - -#[derive(AnchorSerialize, AnchorDeserialize, PartialEq, Default, Copy, Clone)] -pub struct WhitelistEntry { - pub program_id: Pubkey, -} - -#[error_code] -pub enum ErrorCode { - #[msg("Vesting end must be greater than the current unix timestamp.")] - InvalidTimestamp, - #[msg("The number of vesting periods must be greater than zero.")] - InvalidPeriod, - #[msg("The vesting deposit amount must be greater than zero.")] - InvalidDepositAmount, - #[msg("The Whitelist entry is not a valid program address.")] - InvalidWhitelistEntry, - #[msg("Invalid program address. Did you provide the correct nonce?")] - InvalidProgramAddress, - #[msg("Invalid vault owner.")] - InvalidVaultOwner, - #[msg("Vault amount must be zero.")] - InvalidVaultAmount, - #[msg("Insufficient withdrawal balance.")] - InsufficientWithdrawalBalance, - #[msg("Whitelist is full")] - WhitelistFull, - #[msg("Whitelist entry already exists")] - WhitelistEntryAlreadyExists, - #[msg("Balance must go up when performing a whitelist deposit")] - InsufficientWhitelistDepositAmount, - #[msg("Cannot deposit more than withdrawn")] - WhitelistDepositOverflow, - #[msg("Tried to withdraw over the specified limit")] - WhitelistWithdrawLimit, - #[msg("Whitelist entry not found.")] - WhitelistEntryNotFound, - #[msg("You do not have sufficient permissions to perform this action.")] - Unauthorized, - #[msg("You are unable to realize projected rewards until unstaking.")] - UnableToWithdrawWhileStaked, - #[msg("The given lock realizor doesn't match the vesting account.")] - InvalidLockRealizor, - #[msg("You have not realized this vesting account.")] - UnrealizedVesting, - #[msg("Invalid vesting schedule given.")] - InvalidSchedule, -} - -impl<'a, 'b, 'c, 'info> From<&mut CreateVesting<'info>> - for CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> -{ - fn from(accounts: &mut CreateVesting<'info>) -> CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> { - let cpi_accounts = Transfer { - from: accounts.depositor.clone(), - to: accounts.vault.to_account_info(), - authority: accounts.depositor_authority.clone(), - }; - let cpi_program = accounts.token_program.clone(); - CpiContext::new(cpi_program, cpi_accounts) - } -} - -impl<'a, 'b, 'c, 'info> From<&Withdraw<'info>> for CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> { - fn from(accounts: &Withdraw<'info>) -> CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> { - let cpi_accounts = Transfer { - from: accounts.vault.to_account_info(), - to: accounts.token.to_account_info(), - authority: accounts.vesting_signer.to_account_info(), - }; - let cpi_program = accounts.token_program.to_account_info(); - CpiContext::new(cpi_program, cpi_accounts) - } -} - -#[access_control(is_whitelisted(transfer))] -pub fn whitelist_relay_cpi<'info>( - transfer: &WhitelistTransfer<'info>, - remaining_accounts: &[AccountInfo<'info>], - instruction_data: Vec, -) -> Result<()> { - let mut meta_accounts = vec![ - AccountMeta::new_readonly(*transfer.vesting.to_account_info().key, false), - AccountMeta::new(*transfer.vault.to_account_info().key, false), - AccountMeta::new_readonly(*transfer.vesting_signer.to_account_info().key, true), - AccountMeta::new_readonly(*transfer.token_program.to_account_info().key, false), - AccountMeta::new( - *transfer.whitelisted_program_vault.to_account_info().key, - false, - ), - AccountMeta::new_readonly( - *transfer - .whitelisted_program_vault_authority - .to_account_info() - .key, - false, - ), - ]; - meta_accounts.extend(remaining_accounts.iter().map(|a| { - if a.is_writable { - AccountMeta::new(*a.key, a.is_signer) - } else { - AccountMeta::new_readonly(*a.key, a.is_signer) - } - })); - let relay_instruction = Instruction { - program_id: *transfer.whitelisted_program.to_account_info().key, - accounts: meta_accounts, - data: instruction_data.to_vec(), - }; - - let seeds = &[ - transfer.vesting.to_account_info().key.as_ref(), - &[transfer.vesting.nonce], - ]; - let signer = &[&seeds[..]]; - let mut accounts = transfer.to_account_infos(); - accounts.extend_from_slice(&remaining_accounts); - solana_program::program::invoke_signed(&relay_instruction, &accounts, signer) - .map_err(Into::into) -} - -pub fn is_whitelisted<'info>(transfer: &WhitelistTransfer<'info>) -> Result<()> { - if !transfer.lockup.whitelist.contains(&WhitelistEntry { - program_id: *transfer.whitelisted_program.key, - }) { - return err!(ErrorCode::WhitelistEntryNotFound); - } - Ok(()) -} - -fn whitelist_auth(lockup: &Lockup, ctx: &Context) -> Result<()> { - if &lockup.authority != ctx.accounts.authority.key { - return err!(ErrorCode::Unauthorized); - } - Ok(()) -} - -pub fn is_valid_schedule(start_ts: i64, end_ts: i64, period_count: u64) -> bool { - if end_ts <= start_ts { - return false; - } - if period_count > (end_ts - start_ts) as u64 { - return false; - } - if period_count == 0 { - return false; - } - true -} - -// Returns Ok if the locked vesting account has been "realized". Realization -// is application dependent. For example, in the case of staking, one must first -// unstake before being able to earn locked tokens. -fn is_realized(ctx: &Context) -> Result<()> { - if let Some(realizor) = &ctx.accounts.vesting.realizor { - let cpi_program = { - let p = ctx.remaining_accounts[0].clone(); - if p.key != &realizor.program { - return err!(ErrorCode::InvalidLockRealizor); - } - p - }; - let cpi_accounts = ctx.remaining_accounts.to_vec()[1..].to_vec(); - let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); - let vesting = (*ctx.accounts.vesting).clone(); - realize_lock::is_realized(cpi_ctx, vesting) - .map_err(|_| error!(ErrorCode::UnrealizedVesting))?; - } - Ok(()) -} - -/// RealizeLock defines the interface an external program must implement if -/// they want to define a "realization condition" on a locked vesting account. -/// This condition must be satisfied *even if a vesting schedule has -/// completed*. Otherwise the user can never earn the locked funds. For example, -/// in the case of the staking program, one cannot received a locked reward -/// until one has completely unstaked. -#[interface] -pub trait RealizeLock<'info, T: Accounts<'info>> { - fn is_realized(ctx: Context, v: Vesting) -> Result<()>; -} diff --git a/tests/lockup/programs/registry/Cargo.toml b/tests/lockup/programs/registry/Cargo.toml deleted file mode 100644 index 9b67806c..00000000 --- a/tests/lockup/programs/registry/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "registry" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "registry" - -[features] -no-entrypoint = [] -cpi = ["no-entrypoint"] -anchor-deprecated-state = [] -default = ["anchor-deprecated-state"] - -[dependencies] -anchor-lang = { path = "../../../../lang" } -anchor-spl = { path = "../../../../spl" } -lockup = { path = "../lockup", features = ["cpi"] } diff --git a/tests/lockup/programs/registry/Xargo.toml b/tests/lockup/programs/registry/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/lockup/programs/registry/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/lockup/programs/registry/src/lib.rs b/tests/lockup/programs/registry/src/lib.rs deleted file mode 100644 index 2512e4f5..00000000 --- a/tests/lockup/programs/registry/src/lib.rs +++ /dev/null @@ -1,1326 +0,0 @@ -//! A relatively advanced example of a staking program. If you're new to Anchor, -//! it's suggested to start with the other examples. - -use anchor_lang::accounts::state::ProgramState; -use anchor_lang::prelude::*; -use anchor_lang::solana_program::account_info::next_account_info; -use anchor_lang::solana_program::program_option::COption; -use anchor_spl::token::{self, Mint, TokenAccount, Transfer}; -use lockup::{CreateVesting, RealizeLock, Realizor, Vesting}; -use std::convert::Into; - -declare_id!("HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L"); - -#[program] -mod registry { - use super::*; - - #[state] - pub struct Registry { - pub lockup_program: Pubkey, - } - - impl Registry { - pub fn new(ctx: Context) -> Result { - Ok(Registry { - lockup_program: *ctx.accounts.lockup_program.key, - }) - } - - pub fn set_lockup_program( - &mut self, - ctx: Context, - lockup_program: Pubkey, - ) -> Result<()> { - // Hard code the authority because the first version of this program - // did not set an authority account in the global state. - // - // When removing the program's upgrade authority, one should remove - // this method first, redeploy, then remove the upgrade authority. - let expected: Pubkey = "HUgFuN4PbvF5YzjDSw9dQ8uTJUcwm2ANsMXwvRdY4ABx" - .parse() - .unwrap(); - if ctx.accounts.authority.key != &expected { - return err!(ErrorCode::InvalidProgramAuthority); - } - - self.lockup_program = lockup_program; - - Ok(()) - } - } - - impl<'info> RealizeLock<'info, IsRealized<'info>> for Registry { - fn is_realized(ctx: Context, v: Vesting) -> Result<()> { - if let Some(realizor) = &v.realizor { - if &realizor.metadata != ctx.accounts.member.to_account_info().key { - return err!(ErrorCode::InvalidRealizorMetadata); - } - assert!(ctx.accounts.member.beneficiary == v.beneficiary); - let total_staked = - ctx.accounts.member_spt.amount + ctx.accounts.member_spt_locked.amount; - if total_staked != 0 { - return err!(ErrorCode::UnrealizedReward); - } - } - Ok(()) - } - } - - #[access_control(Initialize::accounts(&ctx, nonce))] - pub fn initialize( - ctx: Context, - mint: Pubkey, - authority: Pubkey, - nonce: u8, - withdrawal_timelock: i64, - stake_rate: u64, - reward_q_len: u32, - ) -> Result<()> { - let registrar = &mut ctx.accounts.registrar; - - registrar.authority = authority; - registrar.nonce = nonce; - registrar.mint = mint; - registrar.pool_mint = *ctx.accounts.pool_mint.to_account_info().key; - registrar.stake_rate = stake_rate; - registrar.reward_event_q = *ctx.accounts.reward_event_q.to_account_info().key; - registrar.withdrawal_timelock = withdrawal_timelock; - - let reward_q = &mut ctx.accounts.reward_event_q; - reward_q - .events - .resize(reward_q_len as usize, Default::default()); - - Ok(()) - } - - pub fn update_registrar( - ctx: Context, - new_authority: Option, - withdrawal_timelock: Option, - ) -> Result<()> { - let registrar = &mut ctx.accounts.registrar; - - if let Some(new_authority) = new_authority { - registrar.authority = new_authority; - } - - if let Some(withdrawal_timelock) = withdrawal_timelock { - registrar.withdrawal_timelock = withdrawal_timelock; - } - - Ok(()) - } - - #[access_control(CreateMember::accounts(&ctx, nonce))] - pub fn create_member(ctx: Context, nonce: u8) -> Result<()> { - let member = &mut ctx.accounts.member; - member.registrar = *ctx.accounts.registrar.to_account_info().key; - member.beneficiary = *ctx.accounts.beneficiary.key; - member.balances = (&ctx.accounts.balances).into(); - member.balances_locked = (&ctx.accounts.balances_locked).into(); - member.nonce = nonce; - Ok(()) - } - - pub fn update_member(ctx: Context, metadata: Option) -> Result<()> { - let member = &mut ctx.accounts.member; - if let Some(m) = metadata { - member.metadata = m; - } - Ok(()) - } - - // Deposits that can only come directly from the member beneficiary. - pub fn deposit(ctx: Context, amount: u64) -> Result<()> { - token::transfer(ctx.accounts.into(), amount).map_err(Into::into) - } - - // Deposits that can only come from the beneficiary's vesting accounts. - pub fn deposit_locked(ctx: Context, amount: u64) -> Result<()> { - token::transfer(ctx.accounts.into(), amount).map_err(Into::into) - } - - #[access_control(no_available_rewards( - &ctx.accounts.reward_event_q, - &ctx.accounts.member, - &ctx.accounts.balances, - &ctx.accounts.balances_locked, - ))] - pub fn stake(ctx: Context, spt_amount: u64, locked: bool) -> Result<()> { - let balances = { - if locked { - &ctx.accounts.balances_locked - } else { - &ctx.accounts.balances - } - }; - - // Transfer tokens into the stake vault. - { - let seeds = &[ - ctx.accounts.registrar.to_account_info().key.as_ref(), - ctx.accounts.member.to_account_info().key.as_ref(), - &[ctx.accounts.member.nonce], - ]; - let member_signer = &[&seeds[..]]; - let cpi_ctx = CpiContext::new_with_signer( - ctx.accounts.token_program.clone(), - token::Transfer { - from: balances.vault.to_account_info(), - to: balances.vault_stake.to_account_info(), - authority: ctx.accounts.member_signer.to_account_info(), - }, - member_signer, - ); - // Convert from stake-token units to mint-token units. - let token_amount = spt_amount - .checked_mul(ctx.accounts.registrar.stake_rate) - .unwrap(); - token::transfer(cpi_ctx, token_amount)?; - } - - // Mint pool tokens to the staker. - { - let seeds = &[ - ctx.accounts.registrar.to_account_info().key.as_ref(), - &[ctx.accounts.registrar.nonce], - ]; - let registrar_signer = &[&seeds[..]]; - - let cpi_ctx = CpiContext::new_with_signer( - ctx.accounts.token_program.clone(), - token::MintTo { - mint: ctx.accounts.pool_mint.to_account_info(), - to: balances.spt.to_account_info(), - authority: ctx.accounts.registrar_signer.to_account_info(), - }, - registrar_signer, - ); - token::mint_to(cpi_ctx, spt_amount)?; - } - - // Update stake timestamp. - let member = &mut ctx.accounts.member; - member.last_stake_ts = ctx.accounts.clock.unix_timestamp; - - Ok(()) - } - - #[access_control(no_available_rewards( - &ctx.accounts.reward_event_q, - &ctx.accounts.member, - &ctx.accounts.balances, - &ctx.accounts.balances_locked, - ))] - pub fn start_unstake(ctx: Context, spt_amount: u64, locked: bool) -> Result<()> { - let balances = { - if locked { - &ctx.accounts.balances_locked - } else { - &ctx.accounts.balances - } - }; - - // Program signer. - let seeds = &[ - ctx.accounts.registrar.to_account_info().key.as_ref(), - ctx.accounts.member.to_account_info().key.as_ref(), - &[ctx.accounts.member.nonce], - ]; - let member_signer = &[&seeds[..]]; - - // Burn pool tokens. - { - let cpi_ctx = CpiContext::new_with_signer( - ctx.accounts.token_program.clone(), - token::Burn { - mint: ctx.accounts.pool_mint.to_account_info(), - from: balances.spt.to_account_info(), - authority: ctx.accounts.member_signer.to_account_info(), - }, - member_signer, - ); - token::burn(cpi_ctx, spt_amount)?; - } - - // Convert from stake-token units to mint-token units. - let token_amount = spt_amount - .checked_mul(ctx.accounts.registrar.stake_rate) - .unwrap(); - - // Transfer tokens from the stake to pending vault. - { - let cpi_ctx = CpiContext::new_with_signer( - ctx.accounts.token_program.clone(), - token::Transfer { - from: balances.vault_stake.to_account_info(), - to: balances.vault_pw.to_account_info(), - authority: ctx.accounts.member_signer.to_account_info(), - }, - member_signer, - ); - token::transfer(cpi_ctx, token_amount)?; - } - - // Print receipt. - let pending_withdrawal = &mut ctx.accounts.pending_withdrawal; - pending_withdrawal.burned = false; - pending_withdrawal.member = *ctx.accounts.member.to_account_info().key; - pending_withdrawal.start_ts = ctx.accounts.clock.unix_timestamp; - pending_withdrawal.end_ts = - ctx.accounts.clock.unix_timestamp + ctx.accounts.registrar.withdrawal_timelock; - pending_withdrawal.amount = token_amount; - pending_withdrawal.pool = ctx.accounts.registrar.pool_mint; - pending_withdrawal.registrar = *ctx.accounts.registrar.to_account_info().key; - pending_withdrawal.locked = locked; - - // Update stake timestamp. - let member = &mut ctx.accounts.member; - member.last_stake_ts = ctx.accounts.clock.unix_timestamp; - - Ok(()) - } - - pub fn end_unstake(ctx: Context) -> Result<()> { - if ctx.accounts.pending_withdrawal.end_ts > ctx.accounts.clock.unix_timestamp { - return err!(ErrorCode::UnstakeTimelock); - } - - // Select which balance set this affects. - let balances = { - if ctx.accounts.pending_withdrawal.locked { - &ctx.accounts.member.balances_locked - } else { - &ctx.accounts.member.balances - } - }; - // Check the vaults given are corrrect. - if &balances.vault != ctx.accounts.vault.key { - return err!(ErrorCode::InvalidVault); - } - if &balances.vault_pw != ctx.accounts.vault_pw.key { - return err!(ErrorCode::InvalidVault); - } - - // Transfer tokens between vaults. - { - let seeds = &[ - ctx.accounts.registrar.to_account_info().key.as_ref(), - ctx.accounts.member.to_account_info().key.as_ref(), - &[ctx.accounts.member.nonce], - ]; - let signer = &[&seeds[..]]; - let cpi_ctx = CpiContext::new_with_signer( - ctx.accounts.token_program.clone(), - Transfer { - from: ctx.accounts.vault_pw.to_account_info(), - to: ctx.accounts.vault.to_account_info(), - authority: ctx.accounts.member_signer.clone(), - }, - signer, - ); - token::transfer(cpi_ctx, ctx.accounts.pending_withdrawal.amount)?; - } - - // Burn the pending withdrawal receipt. - let pending_withdrawal = &mut ctx.accounts.pending_withdrawal; - pending_withdrawal.burned = true; - - Ok(()) - } - - pub fn withdraw(ctx: Context, amount: u64) -> Result<()> { - let seeds = &[ - ctx.accounts.registrar.to_account_info().key.as_ref(), - ctx.accounts.member.to_account_info().key.as_ref(), - &[ctx.accounts.member.nonce], - ]; - let signer = &[&seeds[..]]; - let cpi_accounts = Transfer { - from: ctx.accounts.vault.to_account_info(), - to: ctx.accounts.depositor.to_account_info(), - authority: ctx.accounts.member_signer.clone(), - }; - let cpi_program = ctx.accounts.token_program.clone(); - let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); - - token::transfer(cpi_ctx, amount).map_err(Into::into) - } - - pub fn withdraw_locked(ctx: Context, amount: u64) -> Result<()> { - let seeds = &[ - ctx.accounts.registrar.to_account_info().key.as_ref(), - ctx.accounts.member.to_account_info().key.as_ref(), - &[ctx.accounts.member.nonce], - ]; - let signer = &[&seeds[..]]; - let cpi_accounts = Transfer { - from: ctx.accounts.member_vault.to_account_info(), - to: ctx.accounts.vesting_vault.to_account_info(), - authority: ctx.accounts.member_signer.clone(), - }; - let cpi_program = ctx.accounts.token_program.clone(); - let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); - - token::transfer(cpi_ctx, amount).map_err(Into::into) - } - - #[access_control(DropReward::accounts(&ctx, nonce))] - pub fn drop_reward( - ctx: Context, - kind: RewardVendorKind, - total: u64, - expiry_ts: i64, - expiry_receiver: Pubkey, - nonce: u8, - ) -> Result<()> { - if total < ctx.accounts.pool_mint.supply { - return err!(ErrorCode::InsufficientReward); - } - if ctx.accounts.clock.unix_timestamp >= expiry_ts { - return err!(ErrorCode::InvalidExpiry); - } - if let RewardVendorKind::Locked { - start_ts, - end_ts, - period_count, - } = kind - { - if !lockup::is_valid_schedule(start_ts, end_ts, period_count) { - return err!(ErrorCode::InvalidVestingSchedule); - } - } - - // Transfer funds into the vendor's vault. - token::transfer(ctx.accounts.into(), total)?; - - // Add the event to the reward queue. - let reward_q = &mut ctx.accounts.reward_event_q; - let cursor = reward_q.append(RewardEvent { - vendor: *ctx.accounts.vendor.to_account_info().key, - ts: ctx.accounts.clock.unix_timestamp, - locked: kind != RewardVendorKind::Unlocked, - })?; - - // Initialize the vendor. - let vendor = &mut ctx.accounts.vendor; - vendor.registrar = *ctx.accounts.registrar.to_account_info().key; - vendor.vault = *ctx.accounts.vendor_vault.to_account_info().key; - vendor.mint = ctx.accounts.vendor_vault.mint; - vendor.nonce = nonce; - vendor.pool_token_supply = ctx.accounts.pool_mint.supply; - vendor.reward_event_q_cursor = cursor; - vendor.start_ts = ctx.accounts.clock.unix_timestamp; - vendor.expiry_ts = expiry_ts; - vendor.expiry_receiver = expiry_receiver; - vendor.from = *ctx.accounts.depositor_authority.key; - vendor.total = total; - vendor.expired = false; - vendor.kind = kind; - - Ok(()) - } - - #[access_control(reward_eligible(&ctx.accounts.cmn))] - pub fn claim_reward(ctx: Context) -> Result<()> { - if RewardVendorKind::Unlocked != ctx.accounts.cmn.vendor.kind { - return err!(ErrorCode::ExpectedUnlockedVendor); - } - // Reward distribution. - let spt_total = - ctx.accounts.cmn.balances.spt.amount + ctx.accounts.cmn.balances_locked.spt.amount; - let reward_amount = spt_total - .checked_mul(ctx.accounts.cmn.vendor.total) - .unwrap() - .checked_div(ctx.accounts.cmn.vendor.pool_token_supply) - .unwrap(); - assert!(reward_amount > 0); - - // Send reward to the given token account. - let seeds = &[ - ctx.accounts.cmn.registrar.to_account_info().key.as_ref(), - ctx.accounts.cmn.vendor.to_account_info().key.as_ref(), - &[ctx.accounts.cmn.vendor.nonce], - ]; - let signer = &[&seeds[..]]; - let cpi_ctx = CpiContext::new_with_signer( - ctx.accounts.cmn.token_program.clone(), - token::Transfer { - from: ctx.accounts.cmn.vault.to_account_info(), - to: ctx.accounts.to.to_account_info(), - authority: ctx.accounts.cmn.vendor_signer.to_account_info(), - }, - signer, - ); - token::transfer(cpi_ctx, reward_amount)?; - - // Update member as having processed the reward. - let member = &mut ctx.accounts.cmn.member; - member.rewards_cursor = ctx.accounts.cmn.vendor.reward_event_q_cursor + 1; - - Ok(()) - } - - #[access_control(reward_eligible(&ctx.accounts.cmn))] - pub fn claim_reward_locked<'a, 'b, 'c, 'info>( - ctx: Context<'a, 'b, 'c, 'info, ClaimRewardLocked<'info>>, - nonce: u8, - ) -> Result<()> { - let (start_ts, end_ts, period_count) = match ctx.accounts.cmn.vendor.kind { - RewardVendorKind::Unlocked => return err!(ErrorCode::ExpectedLockedVendor), - RewardVendorKind::Locked { - start_ts, - end_ts, - period_count, - } => (start_ts, end_ts, period_count), - }; - - // Reward distribution. - let spt_total = - ctx.accounts.cmn.balances.spt.amount + ctx.accounts.cmn.balances_locked.spt.amount; - let reward_amount = spt_total - .checked_mul(ctx.accounts.cmn.vendor.total) - .unwrap() - .checked_div(ctx.accounts.cmn.vendor.pool_token_supply) - .unwrap(); - assert!(reward_amount > 0); - - // Specify the vesting account's realizor, so that unlocks can only - // execute once completely unstaked. - let realizor = Some(Realizor { - program: *ctx.program_id, - metadata: *ctx.accounts.cmn.member.to_account_info().key, - }); - - // CPI: Create lockup account for the member's beneficiary. - let seeds = &[ - ctx.accounts.cmn.registrar.to_account_info().key.as_ref(), - ctx.accounts.cmn.vendor.to_account_info().key.as_ref(), - &[ctx.accounts.cmn.vendor.nonce], - ]; - let signer = &[&seeds[..]]; - let remaining_accounts: &[AccountInfo] = ctx.remaining_accounts; - let cpi_program = ctx.accounts.lockup_program.clone(); - let cpi_accounts = { - let accs = &mut remaining_accounts.iter(); - lockup::cpi::accounts::CreateVesting { - vesting: next_account_info(accs)?.to_account_info(), - vault: next_account_info(accs)?.to_account_info(), - depositor: next_account_info(accs)?.to_account_info(), - depositor_authority: next_account_info(accs)?.to_account_info(), - token_program: next_account_info(accs)?.to_account_info(), - clock: next_account_info(accs)?.to_account_info(), - } - }; - let cpi_ctx = CpiContext::new_with_signer(cpi_program, cpi_accounts, signer); - lockup::cpi::create_vesting( - cpi_ctx, - ctx.accounts.cmn.member.beneficiary, - reward_amount, - nonce, - start_ts, - end_ts, - period_count, - realizor, - )?; - - // Make sure this reward can't be processed more than once. - let member = &mut ctx.accounts.cmn.member; - member.rewards_cursor = ctx.accounts.cmn.vendor.reward_event_q_cursor + 1; - - Ok(()) - } - - pub fn expire_reward(ctx: Context) -> Result<()> { - if ctx.accounts.clock.unix_timestamp < ctx.accounts.vendor.expiry_ts { - return err!(ErrorCode::VendorNotYetExpired); - } - - // Send all remaining funds to the expiry receiver's token. - let seeds = &[ - ctx.accounts.registrar.to_account_info().key.as_ref(), - ctx.accounts.vendor.to_account_info().key.as_ref(), - &[ctx.accounts.vendor.nonce], - ]; - let signer = &[&seeds[..]]; - let cpi_ctx = CpiContext::new_with_signer( - ctx.accounts.token_program.clone(), - token::Transfer { - to: ctx.accounts.expiry_receiver_token.to_account_info(), - from: ctx.accounts.vault.to_account_info(), - authority: ctx.accounts.vendor_signer.to_account_info(), - }, - signer, - ); - token::transfer(cpi_ctx, ctx.accounts.vault.amount)?; - - // Burn the vendor. - let vendor = &mut ctx.accounts.vendor; - vendor.expired = true; - - Ok(()) - } -} - -#[derive(Accounts)] -pub struct Initialize<'info> { - #[account(zero)] - registrar: Account<'info, Registrar>, - #[account(zero)] - reward_event_q: Account<'info, RewardQueue>, - #[account("pool_mint.decimals == 0")] - pool_mint: Account<'info, Mint>, -} - -impl<'info> Initialize<'info> { - fn accounts(ctx: &Context>, nonce: u8) -> Result<()> { - let registrar_signer = Pubkey::create_program_address( - &[ - ctx.accounts.registrar.to_account_info().key.as_ref(), - &[nonce], - ], - ctx.program_id, - ) - .map_err(|_| error!(ErrorCode::InvalidNonce))?; - if ctx.accounts.pool_mint.mint_authority != COption::Some(registrar_signer) { - return err!(ErrorCode::InvalidPoolMintAuthority); - } - assert!(ctx.accounts.pool_mint.supply == 0); - Ok(()) - } -} - -#[derive(Accounts)] -pub struct UpdateRegistrar<'info> { - #[account(mut, has_one = authority)] - registrar: Account<'info, Registrar>, - authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct CreateMember<'info> { - // Stake instance. - registrar: Box>, - // Member. - #[account(zero)] - member: Box>, - beneficiary: Signer<'info>, - #[account( - "&balances.spt.owner == member_signer.key", - "balances.spt.mint == registrar.pool_mint", - "balances.vault.mint == registrar.mint" - )] - balances: BalanceSandboxAccounts<'info>, - #[account( - "&balances_locked.spt.owner == member_signer.key", - "balances_locked.spt.mint == registrar.pool_mint", - "balances_locked.vault.mint == registrar.mint" - )] - balances_locked: BalanceSandboxAccounts<'info>, - member_signer: AccountInfo<'info>, - // Misc. - #[account("token_program.key == &token::ID")] - token_program: AccountInfo<'info>, -} - -impl<'info> CreateMember<'info> { - fn accounts(ctx: &Context, nonce: u8) -> Result<()> { - let seeds = &[ - ctx.accounts.registrar.to_account_info().key.as_ref(), - ctx.accounts.member.to_account_info().key.as_ref(), - &[nonce], - ]; - let member_signer = Pubkey::create_program_address(seeds, ctx.program_id) - .map_err(|_| error!(ErrorCode::InvalidNonce))?; - if &member_signer != ctx.accounts.member_signer.to_account_info().key { - return err!(ErrorCode::InvalidMemberSigner); - } - - Ok(()) - } -} - -// When creating a member, the mints and owners of these accounts are correct. -// Upon creation, we assign the accounts. A onetime operation. -// When using a member, we check these accounts addresess are equal to the -// addresses stored on the member. If so, the correct accounts were given are -// correct. -#[derive(Accounts, Clone)] -pub struct BalanceSandboxAccounts<'info> { - #[account(mut)] - spt: Box>, - #[account(mut, constraint = vault.owner == spt.owner)] - vault: Box>, - #[account( - mut, - constraint = vault_stake.owner == spt.owner, - constraint = vault_stake.mint == vault.mint - )] - vault_stake: Box>, - #[account(mut, constraint = vault_pw.owner == spt.owner, constraint = vault_pw.mint == vault.mint)] - vault_pw: Box>, -} - -#[derive(Accounts)] -pub struct Ctor<'info> { - lockup_program: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct SetLockupProgram<'info> { - authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct IsRealized<'info> { - #[account( - constraint = &member.balances.spt == member_spt.to_account_info().key, - constraint = &member.balances_locked.spt == member_spt_locked.to_account_info().key - )] - member: Account<'info, Member>, - member_spt: Account<'info, TokenAccount>, - member_spt_locked: Account<'info, TokenAccount>, -} - -#[derive(Accounts)] -pub struct UpdateMember<'info> { - #[account(mut, has_one = beneficiary)] - member: Account<'info, Member>, - beneficiary: Signer<'info>, -} - -#[derive(Accounts)] -pub struct Deposit<'info> { - // Member. - #[account(has_one = beneficiary)] - member: Account<'info, Member>, - beneficiary: Signer<'info>, - #[account(mut, constraint = vault.to_account_info().key == &member.balances.vault)] - vault: Account<'info, TokenAccount>, - // Depositor. - #[account(mut)] - depositor: AccountInfo<'info>, - #[account(signer, constraint = depositor_authority.key == &member.beneficiary)] - depositor_authority: AccountInfo<'info>, - // Misc. - #[account(constraint = token_program.key == &token::ID)] - token_program: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct DepositLocked<'info> { - // Lockup whitelist relay interface. - #[account( - constraint = vesting.to_account_info().owner == ®istry.lockup_program, - constraint = vesting.beneficiary == member.beneficiary - )] - vesting: Box>, - #[account(mut, constraint = vesting_vault.key == &vesting.vault)] - vesting_vault: AccountInfo<'info>, - // Note: no need to verify the depositor_authority since the SPL program - // will fail the transaction if it's not correct. - pub depositor_authority: Signer<'info>, - #[account(constraint = token_program.key == &token::ID)] - token_program: AccountInfo<'info>, - #[account( - mut, - constraint = member_vault.to_account_info().key == &member.balances_locked.vault - )] - member_vault: Box>, - #[account( - seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()], - bump = member.nonce, - )] - member_signer: AccountInfo<'info>, - - // Program specific. - registry: ProgramState<'info, Registry>, - registrar: Box>, - #[account(has_one = registrar, has_one = beneficiary)] - member: Box>, - beneficiary: Signer<'info>, -} - -#[derive(Accounts)] -pub struct Stake<'info> { - // Global accounts for the staking instance. - #[account(has_one = pool_mint, has_one = reward_event_q)] - registrar: Account<'info, Registrar>, - reward_event_q: Account<'info, RewardQueue>, - #[account(mut)] - pool_mint: Account<'info, Mint>, - - // Member. - #[account(mut, has_one = beneficiary, has_one = registrar)] - member: Account<'info, Member>, - beneficiary: Signer<'info>, - #[account(constraint = BalanceSandbox::from(&balances) == member.balances)] - balances: BalanceSandboxAccounts<'info>, - #[account(constraint = BalanceSandbox::from(&balances_locked) == member.balances_locked)] - balances_locked: BalanceSandboxAccounts<'info>, - - // Program signers. - #[account( - seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()], - bump = member.nonce, - )] - member_signer: AccountInfo<'info>, - #[account( - seeds = [registrar.to_account_info().key.as_ref()], - bump = registrar.nonce, - )] - registrar_signer: AccountInfo<'info>, - - // Misc. - clock: Sysvar<'info, Clock>, - #[account(constraint = token_program.key == &token::ID)] - token_program: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct StartUnstake<'info> { - // Stake instance globals. - #[account(has_one = reward_event_q)] - registrar: Account<'info, Registrar>, - reward_event_q: Account<'info, RewardQueue>, - #[account(mut)] - pool_mint: AccountInfo<'info>, - - // Member. - #[account(zero)] - pending_withdrawal: Account<'info, PendingWithdrawal>, - #[account(has_one = beneficiary, has_one = registrar)] - member: Account<'info, Member>, - beneficiary: Signer<'info>, - #[account(constraint = BalanceSandbox::from(&balances) == member.balances)] - balances: BalanceSandboxAccounts<'info>, - #[account(constraint = BalanceSandbox::from(&balances_locked) == member.balances_locked)] - balances_locked: BalanceSandboxAccounts<'info>, - - // Programmatic signers. - #[account( - seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()], - bump = member.nonce, - )] - member_signer: AccountInfo<'info>, - - // Misc. - #[account(constraint = token_program.key == &token::ID)] - token_program: AccountInfo<'info>, - clock: Sysvar<'info, Clock>, -} - -#[derive(Accounts)] -pub struct EndUnstake<'info> { - registrar: Account<'info, Registrar>, - - #[account(has_one = registrar, has_one = beneficiary)] - member: Account<'info, Member>, - beneficiary: Signer<'info>, - #[account(mut, has_one = registrar, has_one = member, constraint = !pending_withdrawal.burned)] - pending_withdrawal: Account<'info, PendingWithdrawal>, - - // If we had ordered maps implementing Accounts we could do a constraint like - // balances.get(pending_withdrawal.balance_id).vault == vault.key. - // - // Note: we do the constraints check in the handler, not here. - #[account(mut)] - vault: AccountInfo<'info>, - #[account(mut)] - vault_pw: AccountInfo<'info>, - - #[account( - seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()], - bump = member.nonce, - )] - member_signer: AccountInfo<'info>, - - clock: Sysvar<'info, Clock>, - #[account(constraint = token_program.key == &token::ID)] - token_program: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct Withdraw<'info> { - // Stake instance. - registrar: Account<'info, Registrar>, - // Member. - #[account(has_one = registrar, has_one = beneficiary)] - member: Account<'info, Member>, - beneficiary: Signer<'info>, - #[account(mut, constraint = vault.to_account_info().key == &member.balances.vault)] - vault: Account<'info, TokenAccount>, - #[account( - seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()], - bump = member.nonce, - )] - member_signer: AccountInfo<'info>, - // Receiver. - #[account(mut)] - depositor: AccountInfo<'info>, - // Misc. - #[account(constraint = token_program.key == &token::ID)] - token_program: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct WithdrawLocked<'info> { - // Lockup whitelist relay interface. - #[account( - constraint = vesting.to_account_info().owner == ®istry.lockup_program, - constraint = vesting.beneficiary == member.beneficiary, - )] - vesting: Box>, - #[account(mut, constraint = vesting_vault.key == &vesting.vault)] - vesting_vault: AccountInfo<'info>, - vesting_signer: Signer<'info>, - #[account(constraint = token_program.key == &token::ID)] - token_program: AccountInfo<'info>, - #[account( - mut, - constraint = member_vault.to_account_info().key == &member.balances_locked.vault - )] - member_vault: Box>, - #[account( - seeds = [registrar.to_account_info().key.as_ref(), member.to_account_info().key.as_ref()], - bump = member.nonce, - )] - member_signer: AccountInfo<'info>, - - // Program specific. - registry: ProgramState<'info, Registry>, - registrar: Box>, - #[account(has_one = registrar, has_one = beneficiary)] - member: Box>, - beneficiary: Signer<'info>, -} - -#[derive(Accounts)] -pub struct DropReward<'info> { - // Staking instance. - #[account(has_one = reward_event_q, has_one = pool_mint)] - registrar: Account<'info, Registrar>, - #[account(mut)] - reward_event_q: Account<'info, RewardQueue>, - pool_mint: Account<'info, Mint>, - // Vendor. - #[account(zero)] - vendor: Account<'info, RewardVendor>, - #[account(mut)] - vendor_vault: Account<'info, TokenAccount>, - // Depositor. - #[account(mut)] - depositor: AccountInfo<'info>, - #[account(signer)] - depositor_authority: AccountInfo<'info>, - // Misc. - #[account(constraint = token_program.key == &token::ID)] - token_program: AccountInfo<'info>, - clock: Sysvar<'info, Clock>, -} - -impl<'info> DropReward<'info> { - fn accounts(ctx: &Context, nonce: u8) -> Result<()> { - let vendor_signer = Pubkey::create_program_address( - &[ - ctx.accounts.registrar.to_account_info().key.as_ref(), - ctx.accounts.vendor.to_account_info().key.as_ref(), - &[nonce], - ], - ctx.program_id, - ) - .map_err(|_| error!(ErrorCode::InvalidNonce))?; - if vendor_signer != ctx.accounts.vendor_vault.owner { - return err!(ErrorCode::InvalidVaultOwner); - } - - Ok(()) - } -} - -#[derive(Accounts)] -pub struct ClaimReward<'info> { - cmn: ClaimRewardCommon<'info>, - // Account to send reward to. - #[account(mut)] - to: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct ClaimRewardLocked<'info> { - cmn: ClaimRewardCommon<'info>, - registry: ProgramState<'info, Registry>, - #[account("lockup_program.key == ®istry.lockup_program")] - lockup_program: AccountInfo<'info>, -} - -// Accounts common to both claim reward locked/unlocked instructions. -#[derive(Accounts)] -pub struct ClaimRewardCommon<'info> { - // Stake instance. - registrar: Box>, - // Member. - #[account(mut, has_one = registrar, has_one = beneficiary)] - member: Account<'info, Member>, - beneficiary: Signer<'info>, - #[account(constraint = BalanceSandbox::from(&balances) == member.balances)] - balances: BalanceSandboxAccounts<'info>, - #[account(constraint = BalanceSandbox::from(&balances_locked) == member.balances_locked)] - balances_locked: BalanceSandboxAccounts<'info>, - // Vendor. - #[account(has_one = registrar, has_one = vault)] - vendor: Account<'info, RewardVendor>, - #[account(mut)] - vault: AccountInfo<'info>, - #[account( - seeds = [registrar.to_account_info().key.as_ref(), vendor.to_account_info().key.as_ref()], - bump = vendor.nonce, - )] - vendor_signer: AccountInfo<'info>, - // Misc. - #[account(constraint = token_program.key == &token::ID)] - token_program: AccountInfo<'info>, - clock: Sysvar<'info, Clock>, -} - -#[derive(Accounts)] -pub struct ExpireReward<'info> { - // Staking instance globals. - registrar: Account<'info, Registrar>, - // Vendor. - #[account(mut, has_one = registrar, has_one = vault, has_one = expiry_receiver)] - vendor: Account<'info, RewardVendor>, - #[account(mut)] - vault: Account<'info, TokenAccount>, - #[account( - seeds = [registrar.to_account_info().key.as_ref(), vendor.to_account_info().key.as_ref()], - bump = vendor.nonce - )] - vendor_signer: AccountInfo<'info>, - // Receiver. - expiry_receiver: Signer<'info>, - #[account(mut)] - expiry_receiver_token: AccountInfo<'info>, - // Misc. - #[account(constraint = token_program.key == &token::ID)] - token_program: AccountInfo<'info>, - clock: Sysvar<'info, Clock>, -} - -#[account] -pub struct Registrar { - /// Priviledged account. - pub authority: Pubkey, - /// Nonce to derive the program-derived address owning the vaults. - pub nonce: u8, - /// Number of seconds that must pass for a withdrawal to complete. - pub withdrawal_timelock: i64, - /// Global event queue for reward vendoring. - pub reward_event_q: Pubkey, - /// Mint of the tokens that can be staked. - pub mint: Pubkey, - /// Staking pool token mint. - pub pool_mint: Pubkey, - /// The amount of tokens (not decimal) that must be staked to get a single - /// staking pool token. - pub stake_rate: u64, -} - -#[account] -pub struct Member { - /// Registrar the member belongs to. - pub registrar: Pubkey, - /// The effective owner of the Member account. - pub beneficiary: Pubkey, - /// Arbitrary metadata account owned by any program. - pub metadata: Pubkey, - /// Sets of balances owned by the Member. - pub balances: BalanceSandbox, - /// Locked balances owned by the Member. - pub balances_locked: BalanceSandbox, - /// Next position in the rewards event queue to process. - pub rewards_cursor: u32, - /// The clock timestamp of the last time this account staked or switched - /// entities. Used as a proof to reward vendors that the Member account - /// was staked at a given point in time. - pub last_stake_ts: i64, - /// Signer nonce. - pub nonce: u8, -} - -// BalanceSandbox defines isolated funds that can only be deposited/withdrawn -// into the program. -// -// Once controlled by the program, the associated `Member` account's beneficiary -// can send funds to/from any of the accounts within the sandbox, e.g., to -// stake. -#[derive(AnchorSerialize, AnchorDeserialize, Default, Debug, Clone, PartialEq)] -pub struct BalanceSandbox { - // Staking pool token. - pub spt: Pubkey, - // Free balance (deposit) vaults. - pub vault: Pubkey, - // Stake vaults. - pub vault_stake: Pubkey, - // Pending withdrawal vaults. - pub vault_pw: Pubkey, -} - -#[account] -pub struct PendingWithdrawal { - /// Registrar this account belongs to. - pub registrar: Pubkey, - /// Member this account belongs to. - pub member: Pubkey, - /// One time token. True if the withdrawal has been completed. - pub burned: bool, - /// The pool being withdrawn from. - pub pool: Pubkey, - /// Unix timestamp when this account was initialized. - pub start_ts: i64, - /// Timestamp when the pending withdrawal completes. - pub end_ts: i64, - /// The number of tokens redeemed from the staking pool. - pub amount: u64, - /// True if the withdrawal applies to locked balances. - pub locked: bool, -} - -#[account] -pub struct RewardQueue { - // Invariant: index is position of the next available slot. - head: u32, - // Invariant: index is position of the first (oldest) taken slot. - // Invariant: head == tail => queue is initialized. - // Invariant: index_of(head + 1) == index_of(tail) => queue is full. - tail: u32, - // Although a vec is used, the size is immutable. - events: Vec, -} - -impl RewardQueue { - pub fn append(&mut self, event: RewardEvent) -> Result { - let cursor = self.head; - - // Insert into next available slot. - let h_idx = self.index_of(self.head); - self.events[h_idx] = event; - - // Update head and tail counters. - let is_full = self.index_of(self.head + 1) == self.index_of(self.tail); - if is_full { - self.tail += 1; - } - self.head += 1; - - Ok(cursor) - } - - pub fn index_of(&self, counter: u32) -> usize { - counter as usize % self.capacity() - } - - pub fn capacity(&self) -> usize { - self.events.len() - } - - pub fn get(&self, cursor: u32) -> &RewardEvent { - &self.events[cursor as usize % self.capacity()] - } - - pub fn head(&self) -> u32 { - self.head - } - - pub fn tail(&self) -> u32 { - self.tail - } -} - -#[derive(Default, Clone, Copy, Debug, AnchorSerialize, AnchorDeserialize)] -pub struct RewardEvent { - vendor: Pubkey, - ts: i64, - locked: bool, -} - -#[account] -pub struct RewardVendor { - pub registrar: Pubkey, - pub vault: Pubkey, - pub mint: Pubkey, - pub nonce: u8, - pub pool_token_supply: u64, - pub reward_event_q_cursor: u32, - pub start_ts: i64, - pub expiry_ts: i64, - pub expiry_receiver: Pubkey, - pub from: Pubkey, - pub total: u64, - pub expired: bool, - pub kind: RewardVendorKind, -} - -#[derive(AnchorSerialize, AnchorDeserialize, Clone, PartialEq)] -pub enum RewardVendorKind { - Unlocked, - Locked { - start_ts: i64, - end_ts: i64, - period_count: u64, - }, -} - -#[error_code] -pub enum ErrorCode { - #[msg("The given reward queue has already been initialized.")] - RewardQAlreadyInitialized, - #[msg("The nonce given doesn't derive a valid program address.")] - InvalidNonce, - #[msg("Invalid pool mint authority")] - InvalidPoolMintAuthority, - #[msg("Member signer doesn't match the derived address.")] - InvalidMemberSigner, - #[msg("The given vault owner must match the signing depositor.")] - InvalidVaultDeposit, - #[msg("The signing depositor doesn't match either of the balance accounts")] - InvalidDepositor, - #[msg("The vault given does not match the vault expected.")] - InvalidVault, - #[msg("Invalid vault owner.")] - InvalidVaultOwner, - #[msg("An unknown error has occured.")] - Unknown, - #[msg("The unstake timelock has not yet expired.")] - UnstakeTimelock, - #[msg("Reward vendors must have at least one token unit per pool token")] - InsufficientReward, - #[msg("Reward expiry must be after the current clock timestamp.")] - InvalidExpiry, - #[msg("The reward vendor has been expired.")] - VendorExpired, - #[msg("This reward has already been processed.")] - CursorAlreadyProcessed, - #[msg("The account was not staked at the time of this reward.")] - NotStakedDuringDrop, - #[msg("The vendor is not yet eligible for expiry.")] - VendorNotYetExpired, - #[msg("Please collect your reward before otherwise using the program.")] - RewardsNeedsProcessing, - #[msg("Locked reward vendor expected but an unlocked vendor was given.")] - ExpectedLockedVendor, - #[msg("Unlocked reward vendor expected but a locked vendor was given.")] - ExpectedUnlockedVendor, - #[msg("Locked deposit from an invalid deposit authority.")] - InvalidVestingSigner, - #[msg("Locked rewards cannot be realized until one unstaked all tokens.")] - UnrealizedReward, - #[msg("The beneficiary doesn't match.")] - InvalidBeneficiary, - #[msg("The given member account does not match the realizor metadata.")] - InvalidRealizorMetadata, - #[msg("Invalid vesting schedule for the locked reward.")] - InvalidVestingSchedule, - #[msg("Please specify the correct authority for this program.")] - InvalidProgramAuthority, -} - -impl<'a, 'b, 'c, 'info> From<&mut Deposit<'info>> - for CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> -{ - fn from(accounts: &mut Deposit<'info>) -> CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> { - let cpi_accounts = Transfer { - from: accounts.depositor.clone(), - to: accounts.vault.to_account_info(), - authority: accounts.depositor_authority.clone(), - }; - let cpi_program = accounts.token_program.clone(); - CpiContext::new(cpi_program, cpi_accounts) - } -} - -impl<'a, 'b, 'c, 'info> From<&mut DepositLocked<'info>> - for CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> -{ - fn from(accounts: &mut DepositLocked<'info>) -> CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> { - let cpi_accounts = Transfer { - from: accounts.vesting_vault.clone(), - to: accounts.member_vault.to_account_info(), - authority: accounts.depositor_authority.to_account_info().clone(), - }; - let cpi_program = accounts.token_program.clone(); - CpiContext::new(cpi_program, cpi_accounts) - } -} - -impl<'a, 'b, 'c, 'info> From<&mut DropReward<'info>> - for CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> -{ - fn from(accounts: &mut DropReward<'info>) -> CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> { - let cpi_accounts = Transfer { - from: accounts.depositor.clone(), - to: accounts.vendor_vault.to_account_info(), - authority: accounts.depositor_authority.clone(), - }; - let cpi_program = accounts.token_program.clone(); - CpiContext::new(cpi_program, cpi_accounts) - } -} - -impl<'info> From<&BalanceSandboxAccounts<'info>> for BalanceSandbox { - fn from(accs: &BalanceSandboxAccounts<'info>) -> Self { - Self { - spt: *accs.spt.to_account_info().key, - vault: *accs.vault.to_account_info().key, - vault_stake: *accs.vault_stake.to_account_info().key, - vault_pw: *accs.vault_pw.to_account_info().key, - } - } -} - -fn reward_eligible(cmn: &ClaimRewardCommon) -> Result<()> { - let vendor = &cmn.vendor; - let member = &cmn.member; - if vendor.expired { - return err!(ErrorCode::VendorExpired); - } - if member.rewards_cursor > vendor.reward_event_q_cursor { - return err!(ErrorCode::CursorAlreadyProcessed); - } - if member.last_stake_ts > vendor.start_ts { - return err!(ErrorCode::NotStakedDuringDrop); - } - Ok(()) -} - -// Asserts the user calling the `Stake` instruction has no rewards available -// in the reward queue. -pub fn no_available_rewards<'info>( - reward_q: &Account<'info, RewardQueue>, - member: &Account<'info, Member>, - balances: &BalanceSandboxAccounts<'info>, - balances_locked: &BalanceSandboxAccounts<'info>, -) -> Result<()> { - let mut cursor = member.rewards_cursor; - - // If the member's cursor is less then the tail, then the ring buffer has - // overwritten those entries, so jump to the tail. - let tail = reward_q.tail(); - if cursor < tail { - cursor = tail; - } - - while cursor < reward_q.head() { - let r_event = reward_q.get(cursor); - if member.last_stake_ts < r_event.ts { - if balances.spt.amount > 0 || balances_locked.spt.amount > 0 { - return err!(ErrorCode::RewardsNeedsProcessing); - } - } - cursor += 1; - } - - Ok(()) -} diff --git a/tests/lockup/tests/lockup.js b/tests/lockup/tests/lockup.js deleted file mode 100644 index 0aee2a3c..00000000 --- a/tests/lockup/tests/lockup.js +++ /dev/null @@ -1,968 +0,0 @@ -const anchor = require("@project-serum/anchor"); -const serumCmn = require("@project-serum/common"); -const { TOKEN_PROGRAM_ID } = require("@solana/spl-token"); -const utils = require("./utils"); -const { assert, expect } = require("chai"); -const nativeAssert = require("assert"); - -anchor.utils.features.set("anchor-deprecated-state"); - -describe("Lockup and Registry", () => { - // Read the provider from the configured environmnet. - const provider = anchor.AnchorProvider.env(); - // hack so we don't have to update serum-common library - // to the new AnchorProvider class and Provider interface - provider.send = provider.sendAndConfirm; - - // Configure the client to use the provider. - anchor.setProvider(provider); - - const lockup = anchor.workspace.Lockup; - const registry = anchor.workspace.Registry; - - let lockupAddress = null; - const WHITELIST_SIZE = 10; - - let mint = null; - let god = null; - - it("Sets up initial test state", async () => { - const [_mint, _god] = await serumCmn.createMintAndVault( - provider, - new anchor.BN(1000000) - ); - mint = _mint; - god = _god; - }); - - it("Is initialized!", async () => { - await lockup.state.rpc.new({ - accounts: { - authority: provider.wallet.publicKey, - }, - }); - - lockupAddress = await lockup.state.address(); - const lockupAccount = await lockup.state.fetch(); - - assert.isTrue(lockupAccount.authority.equals(provider.wallet.publicKey)); - assert.strictEqual(lockupAccount.whitelist.length, WHITELIST_SIZE); - lockupAccount.whitelist.forEach((e) => { - assert.isTrue(e.programId.equals(anchor.web3.PublicKey.default)); - }); - }); - - it("Deletes the default whitelisted addresses", async () => { - const defaultEntry = { programId: anchor.web3.PublicKey.default }; - await lockup.state.rpc.whitelistDelete(defaultEntry, { - accounts: { - authority: provider.wallet.publicKey, - }, - }); - }); - - it("Sets a new authority", async () => { - const newAuthority = anchor.web3.Keypair.generate(); - await lockup.state.rpc.setAuthority(newAuthority.publicKey, { - accounts: { - authority: provider.wallet.publicKey, - }, - }); - - let lockupAccount = await lockup.state.fetch(); - assert.isTrue(lockupAccount.authority.equals(newAuthority.publicKey)); - - await lockup.state.rpc.setAuthority(provider.wallet.publicKey, { - accounts: { - authority: newAuthority.publicKey, - }, - signers: [newAuthority], - }); - - lockupAccount = await lockup.state.fetch(); - assert.isTrue(lockupAccount.authority.equals(provider.wallet.publicKey)); - }); - - const entries = []; - - it("Adds to the whitelist", async () => { - const generateEntry = async () => { - let programId = anchor.web3.Keypair.generate().publicKey; - return { - programId, - }; - }; - - for (let k = 0; k < WHITELIST_SIZE; k += 1) { - entries.push(await generateEntry()); - } - - const accounts = { - authority: provider.wallet.publicKey, - }; - - await lockup.state.rpc.whitelistAdd(entries[0], { accounts }); - - let lockupAccount = await lockup.state.fetch(); - - assert.lengthOf(lockupAccount.whitelist, 1); - assert.deepEqual(lockupAccount.whitelist, [entries[0]]); - - for (let k = 1; k < WHITELIST_SIZE; k += 1) { - await lockup.state.rpc.whitelistAdd(entries[k], { accounts }); - } - - lockupAccount = await lockup.state.fetch(); - - assert.deepEqual(lockupAccount.whitelist, entries); - - await nativeAssert.rejects( - async () => { - const e = await generateEntry(); - await lockup.state.rpc.whitelistAdd(e, { accounts }); - }, - (err) => { - assert.strictEqual(err.error.errorCode.number, 6008); - assert.strictEqual(err.error.errorMessage, "Whitelist is full"); - return true; - } - ); - }); - - it("Removes from the whitelist", async () => { - await lockup.state.rpc.whitelistDelete(entries[0], { - accounts: { - authority: provider.wallet.publicKey, - }, - }); - let lockupAccount = await lockup.state.fetch(); - assert.deepStrictEqual(lockupAccount.whitelist, entries.slice(1)); - }); - - const vesting = anchor.web3.Keypair.generate(); - let vestingAccount = null; - let vestingSigner = null; - - it("Creates a vesting account", async () => { - const startTs = new anchor.BN(Date.now() / 1000); - const endTs = new anchor.BN(startTs.toNumber() + 5); - const periodCount = new anchor.BN(2); - const beneficiary = provider.wallet.publicKey; - const depositAmount = new anchor.BN(100); - - const vault = anchor.web3.Keypair.generate(); - let [_vestingSigner, nonce] = - await anchor.web3.PublicKey.findProgramAddress( - [vesting.publicKey.toBuffer()], - lockup.programId - ); - vestingSigner = _vestingSigner; - - await lockup.rpc.createVesting( - beneficiary, - depositAmount, - nonce, - startTs, - endTs, - periodCount, - null, // Lock realizor is None. - { - accounts: { - vesting: vesting.publicKey, - vault: vault.publicKey, - depositor: god, - depositorAuthority: provider.wallet.publicKey, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, - }, - signers: [vesting, vault], - instructions: [ - await lockup.account.vesting.createInstruction(vesting), - ...(await serumCmn.createTokenAccountInstrs( - provider, - vault.publicKey, - mint, - vestingSigner - )), - ], - } - ); - - vestingAccount = await lockup.account.vesting.fetch(vesting.publicKey); - - assert.isTrue(vestingAccount.beneficiary.equals(provider.wallet.publicKey)); - assert.isTrue(vestingAccount.mint.equals(mint)); - assert.isTrue(vestingAccount.grantor.equals(provider.wallet.publicKey)); - assert.isTrue(vestingAccount.outstanding.eq(depositAmount)); - assert.isTrue(vestingAccount.startBalance.eq(depositAmount)); - assert.isTrue(vestingAccount.whitelistOwned.eq(new anchor.BN(0))); - assert.strictEqual(vestingAccount.nonce, nonce); - assert.isTrue(vestingAccount.createdTs.gt(new anchor.BN(0))); - assert.isTrue(vestingAccount.startTs.eq(startTs)); - assert.isTrue(vestingAccount.endTs.eq(endTs)); - assert.isNull(vestingAccount.realizor); - }); - - it("Fails to withdraw from a vesting account before vesting", async () => { - await nativeAssert.rejects( - async () => { - await lockup.rpc.withdraw(new anchor.BN(100), { - accounts: { - vesting: vesting.publicKey, - beneficiary: provider.wallet.publicKey, - token: god, - vault: vestingAccount.vault, - vestingSigner: vestingSigner, - tokenProgram: TOKEN_PROGRAM_ID, - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, - }, - }); - }, - (err) => { - assert.strictEqual(err.error.errorCode.number, 6007); - assert.strictEqual( - err.error.errorMessage, - "Insufficient withdrawal balance." - ); - return true; - } - ); - }); - - it("Waits for a vesting period to pass", async () => { - await serumCmn.sleep(10 * 1000); - }); - - it("Withdraws from the vesting account", async () => { - const token = await serumCmn.createTokenAccount( - provider, - mint, - provider.wallet.publicKey - ); - - await lockup.rpc.withdraw(new anchor.BN(100), { - accounts: { - vesting: vesting.publicKey, - beneficiary: provider.wallet.publicKey, - token, - vault: vestingAccount.vault, - vestingSigner, - tokenProgram: TOKEN_PROGRAM_ID, - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, - }, - }); - - vestingAccount = await lockup.account.vesting.fetch(vesting.publicKey); - assert.isTrue(vestingAccount.outstanding.eq(new anchor.BN(0))); - - const vaultAccount = await serumCmn.getTokenAccount( - provider, - vestingAccount.vault - ); - assert.isTrue(vaultAccount.amount.eq(new anchor.BN(0))); - - const tokenAccount = await serumCmn.getTokenAccount(provider, token); - assert.isTrue(tokenAccount.amount.eq(new anchor.BN(100))); - }); - - const registrar = anchor.web3.Keypair.generate(); - const rewardQ = anchor.web3.Keypair.generate(); - const withdrawalTimelock = new anchor.BN(4); - const stakeRate = new anchor.BN(2); - const rewardQLen = 170; - let registrarAccount = null; - let registrarSigner = null; - let nonce = null; - let poolMint = null; - - it("Creates registry genesis", async () => { - const [_registrarSigner, _nonce] = - await anchor.web3.PublicKey.findProgramAddress( - [registrar.publicKey.toBuffer()], - registry.programId - ); - registrarSigner = _registrarSigner; - nonce = _nonce; - poolMint = await serumCmn.createMint(provider, registrarSigner); - }); - - it("Initializes registry's global state", async () => { - await registry.state.rpc.new({ - accounts: { lockupProgram: lockup.programId }, - }); - - const state = await registry.state.fetch(); - assert.isTrue(state.lockupProgram.equals(lockup.programId)); - - // Should not allow a second initializatoin. - await nativeAssert.rejects( - async () => { - await registry.state.rpc.new(lockup.programId); - }, - (err) => { - return true; - } - ); - }); - - it("Initializes the registrar", async () => { - await registry.rpc.initialize( - mint, - provider.wallet.publicKey, - nonce, - withdrawalTimelock, - stakeRate, - rewardQLen, - { - accounts: { - registrar: registrar.publicKey, - poolMint, - rewardEventQ: rewardQ.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [registrar, rewardQ], - instructions: [ - await registry.account.registrar.createInstruction(registrar), - await registry.account.rewardQueue.createInstruction(rewardQ, 8250), - ], - } - ); - - registrarAccount = await registry.account.registrar.fetch( - registrar.publicKey - ); - - assert.isTrue(registrarAccount.authority.equals(provider.wallet.publicKey)); - assert.strictEqual(registrarAccount.nonce, nonce); - assert.isTrue(registrarAccount.mint.equals(mint)); - assert.isTrue(registrarAccount.poolMint.equals(poolMint)); - assert.isTrue(registrarAccount.stakeRate.eq(stakeRate)); - assert.isTrue(registrarAccount.rewardEventQ.equals(rewardQ.publicKey)); - assert.isTrue(registrarAccount.withdrawalTimelock.eq(withdrawalTimelock)); - }); - - const member = anchor.web3.Keypair.generate(); - let memberAccount = null; - let memberSigner = null; - let balances = null; - let balancesLocked = null; - - it("Creates a member", async () => { - const [_memberSigner, nonce] = - await anchor.web3.PublicKey.findProgramAddress( - [registrar.publicKey.toBuffer(), member.publicKey.toBuffer()], - registry.programId - ); - memberSigner = _memberSigner; - - const [mainTx, _balances] = await utils.createBalanceSandbox( - provider, - registrarAccount, - memberSigner - ); - const [lockedTx, _balancesLocked] = await utils.createBalanceSandbox( - provider, - registrarAccount, - memberSigner - ); - - balances = _balances; - balancesLocked = _balancesLocked; - - const tx = registry.transaction.createMember(nonce, { - accounts: { - registrar: registrar.publicKey, - member: member.publicKey, - beneficiary: provider.wallet.publicKey, - memberSigner, - balances, - balancesLocked, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - instructions: [await registry.account.member.createInstruction(member)], - }); - - const signers = [member, provider.wallet.payer]; - - const allTxs = [mainTx, lockedTx, { tx, signers }]; - - let txSigs = await provider.sendAll(allTxs); - - memberAccount = await registry.account.member.fetch(member.publicKey); - - assert.isTrue(memberAccount.registrar.equals(registrar.publicKey)); - assert.isTrue(memberAccount.beneficiary.equals(provider.wallet.publicKey)); - assert.isTrue(memberAccount.metadata.equals(anchor.web3.PublicKey.default)); - assert.strictEqual( - JSON.stringify(memberAccount.balances), - JSON.stringify(balances) - ); - assert.strictEqual( - JSON.stringify(memberAccount.balancesLocked), - JSON.stringify(balancesLocked) - ); - assert.strictEqual(memberAccount.rewardsCursor, 0); - assert.isTrue(memberAccount.lastStakeTs.eq(new anchor.BN(0))); - }); - - it("Deposits (unlocked) to a member", async () => { - const depositAmount = new anchor.BN(120); - await registry.rpc.deposit(depositAmount, { - accounts: { - depositor: god, - depositorAuthority: provider.wallet.publicKey, - tokenProgram: TOKEN_PROGRAM_ID, - vault: memberAccount.balances.vault, - beneficiary: provider.wallet.publicKey, - member: member.publicKey, - }, - }); - - const memberVault = await serumCmn.getTokenAccount( - provider, - memberAccount.balances.vault - ); - assert.isTrue(memberVault.amount.eq(depositAmount)); - }); - - it("Stakes to a member (unlocked)", async () => { - const stakeAmount = new anchor.BN(10); - await registry.rpc.stake(stakeAmount, false, { - accounts: { - // Stake instance. - registrar: registrar.publicKey, - rewardEventQ: rewardQ.publicKey, - poolMint, - // Member. - member: member.publicKey, - beneficiary: provider.wallet.publicKey, - balances, - balancesLocked, - // Program signers. - memberSigner, - registrarSigner, - // Misc. - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, - tokenProgram: TOKEN_PROGRAM_ID, - }, - }); - - const vault = await serumCmn.getTokenAccount( - provider, - memberAccount.balances.vault - ); - const vaultStake = await serumCmn.getTokenAccount( - provider, - memberAccount.balances.vaultStake - ); - const spt = await serumCmn.getTokenAccount( - provider, - memberAccount.balances.spt - ); - - assert.isTrue(vault.amount.eq(new anchor.BN(100))); - assert.isTrue(vaultStake.amount.eq(new anchor.BN(20))); - assert.isTrue(spt.amount.eq(new anchor.BN(10))); - }); - - const unlockedVendor = anchor.web3.Keypair.generate(); - const unlockedVendorVault = anchor.web3.Keypair.generate(); - let unlockedVendorSigner = null; - - it("Drops an unlocked reward", async () => { - const rewardKind = { - unlocked: {}, - }; - const rewardAmount = new anchor.BN(200); - const expiry = new anchor.BN(Date.now() / 1000 + 5); - const [_vendorSigner, nonce] = - await anchor.web3.PublicKey.findProgramAddress( - [registrar.publicKey.toBuffer(), unlockedVendor.publicKey.toBuffer()], - registry.programId - ); - unlockedVendorSigner = _vendorSigner; - - await registry.rpc.dropReward( - rewardKind, - rewardAmount, - expiry, - provider.wallet.publicKey, - nonce, - { - accounts: { - registrar: registrar.publicKey, - rewardEventQ: rewardQ.publicKey, - poolMint, - - vendor: unlockedVendor.publicKey, - vendorVault: unlockedVendorVault.publicKey, - - depositor: god, - depositorAuthority: provider.wallet.publicKey, - - tokenProgram: TOKEN_PROGRAM_ID, - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [unlockedVendorVault, unlockedVendor], - instructions: [ - ...(await serumCmn.createTokenAccountInstrs( - provider, - unlockedVendorVault.publicKey, - mint, - unlockedVendorSigner - )), - await registry.account.rewardVendor.createInstruction(unlockedVendor), - ], - } - ); - - const vendorAccount = await registry.account.rewardVendor.fetch( - unlockedVendor.publicKey - ); - - assert.isTrue(vendorAccount.registrar.equals(registrar.publicKey)); - assert.isTrue(vendorAccount.vault.equals(unlockedVendorVault.publicKey)); - assert.strictEqual(vendorAccount.nonce, nonce); - assert.isTrue(vendorAccount.poolTokenSupply.eq(new anchor.BN(10))); - assert.isTrue(vendorAccount.expiryTs.eq(expiry)); - assert.isTrue( - vendorAccount.expiryReceiver.equals(provider.wallet.publicKey) - ); - assert.isTrue(vendorAccount.total.eq(rewardAmount)); - assert.isFalse(vendorAccount.expired); - assert.strictEqual(vendorAccount.rewardEventQCursor, 0); - assert.deepEqual(vendorAccount.kind, rewardKind); - - const rewardQAccount = await registry.account.rewardQueue.fetch( - rewardQ.publicKey - ); - assert.strictEqual(rewardQAccount.head, 1); - assert.strictEqual(rewardQAccount.tail, 0); - const e = rewardQAccount.events[0]; - assert.isTrue(e.vendor.equals(unlockedVendor.publicKey)); - assert.strictEqual(e.locked, false); - }); - - it("Collects an unlocked reward", async () => { - const token = await serumCmn.createTokenAccount( - provider, - mint, - provider.wallet.publicKey - ); - await registry.rpc.claimReward({ - accounts: { - to: token, - cmn: { - registrar: registrar.publicKey, - - member: member.publicKey, - beneficiary: provider.wallet.publicKey, - balances, - balancesLocked, - - vendor: unlockedVendor.publicKey, - vault: unlockedVendorVault.publicKey, - vendorSigner: unlockedVendorSigner, - - tokenProgram: TOKEN_PROGRAM_ID, - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, - }, - }, - }); - - let tokenAccount = await serumCmn.getTokenAccount(provider, token); - assert.isTrue(tokenAccount.amount.eq(new anchor.BN(200))); - - const memberAccount = await registry.account.member.fetch(member.publicKey); - assert.strictEqual(memberAccount.rewardsCursor, 1); - }); - - const lockedVendor = anchor.web3.Keypair.generate(); - const lockedVendorVault = anchor.web3.Keypair.generate(); - let lockedVendorSigner = null; - let lockedRewardAmount = null; - let lockedRewardKind = null; - - it("Drops a locked reward", async () => { - lockedRewardKind = { - locked: { - startTs: new anchor.BN(Date.now() / 1000), - endTs: new anchor.BN(Date.now() / 1000 + 6), - periodCount: new anchor.BN(2), - }, - }; - lockedRewardAmount = new anchor.BN(200); - const expiry = new anchor.BN(Date.now() / 1000 + 5); - const [_vendorSigner, nonce] = - await anchor.web3.PublicKey.findProgramAddress( - [registrar.publicKey.toBuffer(), lockedVendor.publicKey.toBuffer()], - registry.programId - ); - lockedVendorSigner = _vendorSigner; - - await registry.rpc.dropReward( - lockedRewardKind, - lockedRewardAmount, - expiry, - provider.wallet.publicKey, - nonce, - { - accounts: { - registrar: registrar.publicKey, - rewardEventQ: rewardQ.publicKey, - poolMint, - - vendor: lockedVendor.publicKey, - vendorVault: lockedVendorVault.publicKey, - - depositor: god, - depositorAuthority: provider.wallet.publicKey, - - tokenProgram: TOKEN_PROGRAM_ID, - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [lockedVendorVault, lockedVendor], - instructions: [ - ...(await serumCmn.createTokenAccountInstrs( - provider, - lockedVendorVault.publicKey, - mint, - lockedVendorSigner - )), - await registry.account.rewardVendor.createInstruction(lockedVendor), - ], - } - ); - - const vendorAccount = await registry.account.rewardVendor.fetch( - lockedVendor.publicKey - ); - - assert.isTrue(vendorAccount.registrar.equals(registrar.publicKey)); - assert.isTrue(vendorAccount.vault.equals(lockedVendorVault.publicKey)); - assert.strictEqual(vendorAccount.nonce, nonce); - assert.isTrue(vendorAccount.poolTokenSupply.eq(new anchor.BN(10))); - assert.isTrue(vendorAccount.expiryTs.eq(expiry)); - assert.isTrue( - vendorAccount.expiryReceiver.equals(provider.wallet.publicKey) - ); - assert.isTrue(vendorAccount.total.eq(lockedRewardAmount)); - assert.isFalse(vendorAccount.expired); - assert.strictEqual(vendorAccount.rewardEventQCursor, 1); - assert.strictEqual( - JSON.stringify(vendorAccount.kind), - JSON.stringify(lockedRewardKind) - ); - - const rewardQAccount = await registry.account.rewardQueue.fetch( - rewardQ.publicKey - ); - assert.strictEqual(rewardQAccount.head, 2); - assert.strictEqual(rewardQAccount.tail, 0); - const e = rewardQAccount.events[1]; - assert.isTrue(e.vendor.equals(lockedVendor.publicKey)); - assert.isTrue(e.locked); - }); - - let vendoredVesting = null; - let vendoredVestingVault = null; - let vendoredVestingSigner = null; - - it("Claims a locked reward", async () => { - vendoredVesting = anchor.web3.Keypair.generate(); - vendoredVestingVault = anchor.web3.Keypair.generate(); - let [_vendoredVestingSigner, nonce] = - await anchor.web3.PublicKey.findProgramAddress( - [vendoredVesting.publicKey.toBuffer()], - lockup.programId - ); - vendoredVestingSigner = _vendoredVestingSigner; - const remainingAccounts = lockup.instruction.createVesting - .accounts({ - vesting: vendoredVesting.publicKey, - vault: vendoredVestingVault.publicKey, - depositor: lockedVendorVault.publicKey, - depositorAuthority: lockedVendorSigner, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, - }) - // Change the signer status on the vendor signer since it's signed by the program, not the - // client. - .map((meta) => - meta.pubkey === lockedVendorSigner ? { ...meta, isSigner: false } : meta - ); - - await registry.rpc.claimRewardLocked(nonce, { - accounts: { - registry: await registry.state.address(), - lockupProgram: lockup.programId, - cmn: { - registrar: registrar.publicKey, - - member: member.publicKey, - beneficiary: provider.wallet.publicKey, - balances, - balancesLocked, - - vendor: lockedVendor.publicKey, - vault: lockedVendorVault.publicKey, - vendorSigner: lockedVendorSigner, - - tokenProgram: TOKEN_PROGRAM_ID, - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, - }, - }, - remainingAccounts, - signers: [vendoredVesting, vendoredVestingVault], - instructions: [ - await lockup.account.vesting.createInstruction(vendoredVesting), - ...(await serumCmn.createTokenAccountInstrs( - provider, - vendoredVestingVault.publicKey, - mint, - vendoredVestingSigner - )), - ], - }); - - const lockupAccount = await lockup.account.vesting.fetch( - vendoredVesting.publicKey - ); - - assert.isTrue(lockupAccount.beneficiary.equals(provider.wallet.publicKey)); - assert.isTrue(lockupAccount.mint.equals(mint)); - assert.isTrue(lockupAccount.vault.equals(vendoredVestingVault.publicKey)); - assert.isTrue(lockupAccount.outstanding.eq(lockedRewardAmount)); - assert.isTrue(lockupAccount.startBalance.eq(lockedRewardAmount)); - assert.isTrue(lockupAccount.endTs.eq(lockedRewardKind.locked.endTs)); - assert.isTrue( - lockupAccount.periodCount.eq(lockedRewardKind.locked.periodCount) - ); - assert.isTrue(lockupAccount.whitelistOwned.eq(new anchor.BN(0))); - assert.isTrue(lockupAccount.realizor.program.equals(registry.programId)); - assert.isTrue(lockupAccount.realizor.metadata.equals(member.publicKey)); - }); - - it("Waits for the lockup period to pass", async () => { - await serumCmn.sleep(10 * 1000); - }); - - it("Should fail to unlock an unrealized lockup reward", async () => { - const token = await serumCmn.createTokenAccount( - provider, - mint, - provider.wallet.publicKey - ); - await nativeAssert.rejects( - async () => { - const withdrawAmount = new anchor.BN(10); - await lockup.rpc.withdraw(withdrawAmount, { - accounts: { - vesting: vendoredVesting.publicKey, - beneficiary: provider.wallet.publicKey, - token, - vault: vendoredVestingVault.publicKey, - vestingSigner: vendoredVestingSigner, - tokenProgram: TOKEN_PROGRAM_ID, - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, - }, - // TODO: trait methods generated on the client. Until then, we need to manually - // specify the account metas here. - remainingAccounts: [ - { pubkey: registry.programId, isWritable: false, isSigner: false }, - { pubkey: member.publicKey, isWritable: false, isSigner: false }, - { pubkey: balances.spt, isWritable: false, isSigner: false }, - { pubkey: balancesLocked.spt, isWritable: false, isSigner: false }, - ], - }); - }, - (err) => { - // Solana doesn't propagate errors across CPI. So we receive the registry's error code, - // not the lockup's. - assert.strictEqual(err.error.errorCode.number, 6020); - assert.strictEqual(err.error.errorCode.code, "UnrealizedReward"); - assert.strictEqual( - err.error.errorMessage, - "Locked rewards cannot be realized until one unstaked all tokens." - ); - expect(err.error.origin).to.deep.equal({ - file: "programs/registry/src/lib.rs", - line: 63, - }); - assert.strictEqual( - err.program.toString(), - "HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L" - ); - expect(err.programErrorStack.map((pk) => pk.toString())).to.deep.equal([ - "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS", - "HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L", - ]); - return true; - } - ); - }); - - const pendingWithdrawal = anchor.web3.Keypair.generate(); - - it("Unstakes (unlocked)", async () => { - const unstakeAmount = new anchor.BN(10); - - await registry.rpc.startUnstake(unstakeAmount, false, { - accounts: { - registrar: registrar.publicKey, - rewardEventQ: rewardQ.publicKey, - poolMint, - - pendingWithdrawal: pendingWithdrawal.publicKey, - member: member.publicKey, - beneficiary: provider.wallet.publicKey, - balances, - balancesLocked, - - memberSigner, - - tokenProgram: TOKEN_PROGRAM_ID, - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [pendingWithdrawal], - instructions: [ - await registry.account.pendingWithdrawal.createInstruction( - pendingWithdrawal - ), - ], - }); - - const vaultPw = await serumCmn.getTokenAccount( - provider, - memberAccount.balances.vaultPw - ); - const vaultStake = await serumCmn.getTokenAccount( - provider, - memberAccount.balances.vaultStake - ); - const spt = await serumCmn.getTokenAccount( - provider, - memberAccount.balances.spt - ); - - assert.isTrue(vaultPw.amount.eq(new anchor.BN(20))); - assert.isTrue(vaultStake.amount.eq(new anchor.BN(0))); - assert.isTrue(spt.amount.eq(new anchor.BN(0))); - }); - - const tryEndUnstake = async () => { - await registry.rpc.endUnstake({ - accounts: { - registrar: registrar.publicKey, - - member: member.publicKey, - beneficiary: provider.wallet.publicKey, - pendingWithdrawal: pendingWithdrawal.publicKey, - - vault: balances.vault, - vaultPw: balances.vaultPw, - - memberSigner, - - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, - tokenProgram: TOKEN_PROGRAM_ID, - }, - }); - }; - - it("Fails to end unstaking before timelock", async () => { - await nativeAssert.rejects( - async () => { - await tryEndUnstake(); - }, - (err) => { - assert.strictEqual(err.error.errorCode.number, 6009); - assert.strictEqual( - err.error.errorMessage, - "The unstake timelock has not yet expired." - ); - return true; - } - ); - }); - - it("Waits for the unstake period to end", async () => { - await serumCmn.sleep(5000); - }); - - it("Unstake finalizes (unlocked)", async () => { - await tryEndUnstake(); - - const vault = await serumCmn.getTokenAccount( - provider, - memberAccount.balances.vault - ); - const vaultPw = await serumCmn.getTokenAccount( - provider, - memberAccount.balances.vaultPw - ); - - assert.isTrue(vault.amount.eq(new anchor.BN(120))); - assert.isTrue(vaultPw.amount.eq(new anchor.BN(0))); - }); - - it("Withdraws deposits (unlocked)", async () => { - const token = await serumCmn.createTokenAccount( - provider, - mint, - provider.wallet.publicKey - ); - const withdrawAmount = new anchor.BN(100); - await registry.rpc.withdraw(withdrawAmount, { - accounts: { - registrar: registrar.publicKey, - member: member.publicKey, - beneficiary: provider.wallet.publicKey, - vault: memberAccount.balances.vault, - memberSigner, - depositor: token, - tokenProgram: TOKEN_PROGRAM_ID, - }, - }); - - const tokenAccount = await serumCmn.getTokenAccount(provider, token); - assert.isTrue(tokenAccount.amount.eq(withdrawAmount)); - }); - - it("Should succesfully unlock a locked reward after unstaking", async () => { - const token = await serumCmn.createTokenAccount( - provider, - mint, - provider.wallet.publicKey - ); - - const withdrawAmount = new anchor.BN(7); - await lockup.rpc.withdraw(withdrawAmount, { - accounts: { - vesting: vendoredVesting.publicKey, - beneficiary: provider.wallet.publicKey, - token, - vault: vendoredVestingVault.publicKey, - vestingSigner: vendoredVestingSigner, - tokenProgram: TOKEN_PROGRAM_ID, - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, - }, - // TODO: trait methods generated on the client. Until then, we need to manually - // specify the account metas here. - remainingAccounts: [ - { pubkey: registry.programId, isWritable: false, isSigner: false }, - { pubkey: member.publicKey, isWritable: false, isSigner: false }, - { pubkey: balances.spt, isWritable: false, isSigner: false }, - { pubkey: balancesLocked.spt, isWritable: false, isSigner: false }, - ], - }); - const tokenAccount = await serumCmn.getTokenAccount(provider, token); - assert.isTrue(tokenAccount.amount.eq(withdrawAmount)); - }); -}); diff --git a/tests/lockup/tests/utils.js b/tests/lockup/tests/utils.js deleted file mode 100644 index ce53bba0..00000000 --- a/tests/lockup/tests/utils.js +++ /dev/null @@ -1,66 +0,0 @@ -const anchor = require("@project-serum/anchor"); -const serumCmn = require("@project-serum/common"); - -async function createBalanceSandbox(provider, r, registrySigner) { - const spt = anchor.web3.Keypair.generate(); - const vault = anchor.web3.Keypair.generate(); - const vaultStake = anchor.web3.Keypair.generate(); - const vaultPw = anchor.web3.Keypair.generate(); - - const lamports = await provider.connection.getMinimumBalanceForRentExemption( - 165 - ); - - const createSptIx = await serumCmn.createTokenAccountInstrs( - provider, - spt.publicKey, - r.poolMint, - registrySigner, - lamports - ); - const createVaultIx = await serumCmn.createTokenAccountInstrs( - provider, - vault.publicKey, - r.mint, - registrySigner, - lamports - ); - const createVaultStakeIx = await serumCmn.createTokenAccountInstrs( - provider, - vaultStake.publicKey, - r.mint, - registrySigner, - lamports - ); - const createVaultPwIx = await serumCmn.createTokenAccountInstrs( - provider, - vaultPw.publicKey, - r.mint, - registrySigner, - lamports - ); - let tx0 = new anchor.web3.Transaction(); - tx0.add( - ...createSptIx, - ...createVaultIx, - ...createVaultStakeIx, - ...createVaultPwIx - ); - let signers0 = [spt, vault, vaultStake, vaultPw]; - - const tx = { tx: tx0, signers: signers0 }; - - return [ - tx, - { - spt: spt.publicKey, - vault: vault.publicKey, - vaultStake: vaultStake.publicKey, - vaultPw: vaultPw.publicKey, - }, - ]; -} - -module.exports = { - createBalanceSandbox, -}; diff --git a/tests/misc/Anchor.toml b/tests/misc/Anchor.toml deleted file mode 100644 index d80f984a..00000000 --- a/tests/misc/Anchor.toml +++ /dev/null @@ -1,11 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -misc = "3TEqcc8xhrhdspwbvoamUJe2borm4Nr72JxL66k6rgrh" -misc2 = "HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L" -init_if_needed = "BZoppwWi6jMnydnUBEJzotgEXHwLr3b3NramJgZtWeF2" - -[workspace] -exclude = ["programs/shared"] diff --git a/tests/misc/Cargo.toml b/tests/misc/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/misc/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/misc/ci.sh b/tests/misc/ci.sh deleted file mode 100755 index a08b0f49..00000000 --- a/tests/misc/ci.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/sh - -# this script ensures that the Misc test does not -# test the miscNonRentExempt.ts during its test in the ci -# because the misc test uses a newer solana version -# than the miscNonRentExempt one. The latter needs to be on -# a validator with a version < 1.9, so it can test -# whether anchor's rent-exemption checks work for -# legacy accounts which dont have to be rent-exempt -rm ./tests/misc/misc.ts -mv miscNonRentExempt.ts ./tests/misc/miscNonRentExempt.ts diff --git a/tests/misc/migrations/deploy.js b/tests/misc/migrations/deploy.js deleted file mode 100644 index 5e3df0dc..00000000 --- a/tests/misc/migrations/deploy.js +++ /dev/null @@ -1,12 +0,0 @@ -// Migrations are an early feature. Currently, they're nothing more than this -// single deploy script that's invoked from the CLI, injecting a provider -// configured from the workspace's Anchor.toml. - -const anchor = require("@project-serum/anchor"); - -module.exports = async function (provider) { - // Configure client to use the provider. - anchor.setProvider(provider); - - // Add your deploy script here. -}; diff --git a/tests/misc/miscNonRentExempt.ts b/tests/misc/miscNonRentExempt.ts deleted file mode 100644 index df91d504..00000000 --- a/tests/misc/miscNonRentExempt.ts +++ /dev/null @@ -1,135 +0,0 @@ -import * as anchor from "@project-serum/anchor"; -import { Program, BN, IdlAccounts, AnchorError } from "@project-serum/anchor"; -import { - PublicKey, - Keypair, - SystemProgram, - SYSVAR_RENT_PUBKEY, -} from "@solana/web3.js"; -import { Misc } from "../../target/types/misc"; -const { assert } = require("chai"); - -describe("miscNonRentExempt", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.env()); - const program = anchor.workspace.Misc as Program; - - it("init_if_needed checks rent_exemption if init is not needed", async () => { - const data = Keypair.generate(); - await program.rpc.initDecreaseLamports({ - accounts: { - data: data.publicKey, - user: anchor.getProvider().wallet.publicKey, - systemProgram: SystemProgram.programId, - }, - signers: [data], - }); - - try { - await program.rpc.initIfNeededChecksRentExemption({ - accounts: { - data: data.publicKey, - user: anchor.getProvider().wallet.publicKey, - systemProgram: SystemProgram.programId, - }, - signers: [data], - }); - assert.ok(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2005); - } - }); - - it("allows non-rent exempt accounts", async () => { - const data = Keypair.generate(); - await program.rpc.initializeNoRentExempt({ - accounts: { - data: data.publicKey, - rent: SYSVAR_RENT_PUBKEY, - }, - signers: [data], - instructions: [ - SystemProgram.createAccount({ - programId: program.programId, - space: 8 + 16 + 16, - lamports: - await program.provider.connection.getMinimumBalanceForRentExemption( - 39 - ), - fromPubkey: anchor.getProvider().wallet.publicKey, - newAccountPubkey: data.publicKey, - }), - ], - }); - await program.rpc.testNoRentExempt({ - accounts: { - data: data.publicKey, - }, - }); - }); - - it("allows rent exemption to be skipped", async () => { - const data = anchor.web3.Keypair.generate(); - await program.rpc.initializeSkipRentExempt({ - accounts: { - data: data.publicKey, - rent: SYSVAR_RENT_PUBKEY, - }, - signers: [data], - instructions: [ - SystemProgram.createAccount({ - programId: program.programId, - space: 8 + 16 + 16, - lamports: - await program.provider.connection.getMinimumBalanceForRentExemption( - 39 - ), - fromPubkey: anchor.getProvider().wallet.publicKey, - newAccountPubkey: data.publicKey, - }), - ], - }); - }); - - it("can use rent_exempt to enforce rent exemption", async () => { - const data = Keypair.generate(); - await program.rpc.initializeSkipRentExempt({ - accounts: { - data: data.publicKey, - rent: SYSVAR_RENT_PUBKEY, - }, - signers: [data], - instructions: [ - SystemProgram.createAccount({ - programId: program.programId, - space: 8 + 16 + 16, - lamports: - await program.provider.connection.getMinimumBalanceForRentExemption( - 39 - ), - fromPubkey: anchor.getProvider().wallet.publicKey, - newAccountPubkey: data.publicKey, - }), - ], - }); - - try { - await program.rpc.testEnforceRentExempt({ - accounts: { - data: data.publicKey, - }, - }); - assert.ok(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2005); - assert.strictEqual( - "A rent exemption constraint was violated", - err.error.errorMessage - ); - } - }); -}); diff --git a/tests/misc/package.json b/tests/misc/package.json deleted file mode 100644 index ca965350..00000000 --- a/tests/misc/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "misc", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - }, - "dependencies": { - "mocha": "^9.1.3" - } -} diff --git a/tests/misc/programs/init-if-needed/Cargo.toml b/tests/misc/programs/init-if-needed/Cargo.toml deleted file mode 100644 index 8843138b..00000000 --- a/tests/misc/programs/init-if-needed/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "init-if-needed" -version = "0.1.0" -description = "Created with Anchor" -edition = "2018" - -[lib] -crate-type = ["cdylib", "lib"] -name = "init_if_needed" - -[features] -no-entrypoint = [] -no-idl = [] -no-log-ix-name = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] } diff --git a/tests/misc/programs/init-if-needed/Xargo.toml b/tests/misc/programs/init-if-needed/Xargo.toml deleted file mode 100644 index 475fb71e..00000000 --- a/tests/misc/programs/init-if-needed/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] diff --git a/tests/misc/programs/init-if-needed/src/lib.rs b/tests/misc/programs/init-if-needed/src/lib.rs deleted file mode 100644 index ea7abe1f..00000000 --- a/tests/misc/programs/init-if-needed/src/lib.rs +++ /dev/null @@ -1,61 +0,0 @@ -use anchor_lang::prelude::*; - -declare_id!("BZoppwWi6jMnydnUBEJzotgEXHwLr3b3NramJgZtWeF2"); - -#[program] -pub mod init_if_needed { - use super::*; - - // _val only used to make tx different so that it doesn't result - // in dup tx error - pub fn initialize(ctx: Context, _val: u8) -> Result<()> { - ctx.accounts.acc.val = 1000; - Ok(()) - } - - pub fn second_initialize(ctx: Context, _val: u8) -> Result<()> { - ctx.accounts.acc.other_val = 2000; - Ok(()) - } - - pub fn close(ctx: Context) -> Result<()> { - ctx.accounts.acc.val = 5000; - Ok(()) - } -} - -#[account] -pub struct MyData { - pub val: u64, -} - -#[account] -pub struct OtherData { - pub other_val: u64, -} - -#[derive(Accounts)] -pub struct Initialize<'info> { - #[account(init_if_needed, payer = payer, space = 8 + 8)] - pub acc: Account<'info, MyData>, - #[account(mut)] - pub payer: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct SecondInitialize<'info> { - #[account(init, payer = payer, space = 8 + 8)] - pub acc: Account<'info, OtherData>, - #[account(mut)] - pub payer: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct Close<'info> { - #[account(mut, close = receiver)] - pub acc: Account<'info, MyData>, - #[account(mut)] - pub receiver: UncheckedAccount<'info>, -} diff --git a/tests/misc/programs/misc/Cargo.toml b/tests/misc/programs/misc/Cargo.toml deleted file mode 100644 index 3b7d921d..00000000 --- a/tests/misc/programs/misc/Cargo.toml +++ /dev/null @@ -1,22 +0,0 @@ -[package] -name = "misc" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "misc" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang", features = ["init-if-needed"] } -anchor-spl = { path = "../../../../spl" } -misc2 = { path = "../misc2", features = ["cpi"] } -spl-associated-token-account = "=1.0.3" diff --git a/tests/misc/programs/misc/Xargo.toml b/tests/misc/programs/misc/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/misc/programs/misc/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/misc/programs/misc/src/account.rs b/tests/misc/programs/misc/src/account.rs deleted file mode 100644 index 893dac25..00000000 --- a/tests/misc/programs/misc/src/account.rs +++ /dev/null @@ -1,75 +0,0 @@ -use anchor_lang::prelude::*; - -macro_rules! size { - ($name: ident, $size:expr) => { - impl $name { - pub const LEN: usize = $size; - } - }; -} - -pub const MAX_SIZE: usize = 10; -pub const MAX_SIZE_U8: u8 = 11; - -#[account] -pub struct Data { - pub udata: u128, // 16 - pub idata: i128, // 16 -} -size!(Data, 32); - -#[account] -pub struct DataU16 { - pub data: u16, // 2 -} -size!(DataU16, 32); - -#[account] -pub struct DataI8 { - pub data: i8, // 1 -} -size!(DataI8, 1); - -#[account] -pub struct DataI16 { - pub data: i16, // 2 -} -size!(DataI16, 2); - -#[account(zero_copy)] -pub struct DataZeroCopy { - pub data: u16, // 2 - pub _padding: u8, // 1 - pub bump: u8, // 1 -} -size!(DataZeroCopy, 4); - -#[account] -pub struct DataWithFilter { - pub authority: Pubkey, // 32 - pub filterable: Pubkey, // 32 -} -size!(DataWithFilter, 64); - -#[account] -pub struct DataMultidimensionalArray { - pub data: [[u8; 10]; 10], // 100 -} -size!(DataMultidimensionalArray, 100); - -#[account] -pub struct DataConstArraySize { - pub data: [u8; MAX_SIZE], // 10 -} -size!(DataConstArraySize, MAX_SIZE); - -#[account] -pub struct DataConstCastArraySize { - pub data_one: [u8; MAX_SIZE as usize], - pub data_two: [u8; MAX_SIZE_U8 as usize], -} - -#[account] -pub struct DataMultidimensionalArrayConstSizes { - pub data: [[u8; MAX_SIZE_U8 as usize]; MAX_SIZE], -} diff --git a/tests/misc/programs/misc/src/context.rs b/tests/misc/programs/misc/src/context.rs deleted file mode 100644 index 69110939..00000000 --- a/tests/misc/programs/misc/src/context.rs +++ /dev/null @@ -1,578 +0,0 @@ -use crate::account::*; -use anchor_lang::accounts::cpi_state::CpiState; -use anchor_lang::accounts::loader::Loader; -use anchor_lang::prelude::*; -use anchor_spl::associated_token::AssociatedToken; -use anchor_spl::token::{Mint, Token, TokenAccount}; -use misc2::misc2::MyState as Misc2State; -use std::mem::size_of; - -#[derive(Accounts)] -pub struct TestTokenSeedsInit<'info> { - #[account( - init, - seeds = [b"my-mint-seed".as_ref()], - bump, - payer = authority, - mint::decimals = 6, - mint::authority = authority, - )] - pub mint: Account<'info, Mint>, - #[account( - init, - seeds = [b"my-token-seed".as_ref()], - bump, - payer = authority, - token::mint = mint, - token::authority = authority, - )] - pub my_pda: Account<'info, TokenAccount>, - #[account(mut)] - /// CHECK: - pub authority: AccountInfo<'info>, - pub system_program: Program<'info, System>, - pub rent: Sysvar<'info, Rent>, - pub token_program: Program<'info, Token>, -} - -#[derive(Accounts)] -pub struct TestInitAssociatedToken<'info> { - #[account( - init, - associated_token::mint = mint, - payer = payer, - associated_token::authority = payer, - )] - pub token: Account<'info, TokenAccount>, - pub mint: Account<'info, Mint>, - #[account(mut)] - pub payer: Signer<'info>, - pub rent: Sysvar<'info, Rent>, - pub system_program: Program<'info, System>, - pub token_program: Program<'info, Token>, - pub associated_token_program: Program<'info, AssociatedToken>, -} - -#[derive(Accounts)] -pub struct TestValidateAssociatedToken<'info> { - #[account( - associated_token::mint = mint, - associated_token::authority = wallet, - )] - pub token: Account<'info, TokenAccount>, - pub mint: Account<'info, Mint>, - /// CHECK: - pub wallet: AccountInfo<'info>, -} - -#[derive(Accounts)] -#[instruction(nonce: u8)] -pub struct TestInstructionConstraint<'info> { - #[account( - seeds = [b"my-seed", my_account.key.as_ref()], - bump = nonce, - )] - /// CHECK: - pub my_pda: AccountInfo<'info>, - /// CHECK: - pub my_account: AccountInfo<'info>, -} - -#[derive(Accounts)] -#[instruction(domain: String, seed: Vec, bump: u8)] -pub struct TestPdaInit<'info> { - #[account( - init, - seeds = [b"my-seed", domain.as_bytes(), foo.key.as_ref(), &seed], - bump, - payer = my_payer, - space = DataU16::LEN + 8 - )] - pub my_pda: Account<'info, DataU16>, - #[account(mut)] - pub my_payer: Signer<'info>, - /// CHECK: - pub foo: AccountInfo<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct TestPdaInitZeroCopy<'info> { - #[account( - init, - seeds = [b"my-seed".as_ref()], - bump, - payer = my_payer, - space = DataZeroCopy::LEN + 8 - )] - pub my_pda: AccountLoader<'info, DataZeroCopy>, - #[account(mut)] - pub my_payer: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct TestPdaMutZeroCopy<'info> { - #[account( - mut, - seeds = [b"my-seed".as_ref()], - bump = my_pda.load()?.bump, - )] - pub my_pda: AccountLoader<'info, DataZeroCopy>, - /// CHECK: - pub my_payer: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct Ctor {} - -#[derive(Accounts)] -pub struct RemainingAccounts {} - -#[derive(Accounts)] -pub struct Initialize<'info> { - #[account(zero)] - pub data: Account<'info, Data>, -} - -#[derive(Accounts)] -pub struct InitializeSkipRentExempt<'info> { - #[account(zero, rent_exempt = skip)] - pub data: Account<'info, Data>, -} - -#[derive(Accounts)] -pub struct InitializeNoRentExempt<'info> { - /// CHECK: - pub data: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct TestOwner<'info> { - #[account(owner = *misc.key)] - /// CHECK: - pub data: AccountInfo<'info>, - /// CHECK: - pub misc: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct TestExecutable<'info> { - #[account(executable)] - /// CHECK: - pub program: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct TestStateCpi<'info> { - #[account(signer)] - /// CHECK: - pub authority: AccountInfo<'info>, - #[account(mut, state = misc2_program)] - pub cpi_state: CpiState<'info, Misc2State>, - #[account(executable)] - /// CHECK: - pub misc2_program: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct TestClose<'info> { - #[account(mut, close = sol_dest)] - pub data: Account<'info, Data>, - /// CHECK: - sol_dest: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct TestU16<'info> { - #[account(zero)] - pub my_account: Account<'info, DataU16>, -} - -#[derive(Accounts)] -pub struct TestI16<'info> { - #[account(zero)] - pub data: Account<'info, DataI16>, -} - -#[derive(Accounts)] -pub struct TestSimulate {} - -#[derive(Accounts)] -pub struct TestI8<'info> { - #[account(zero)] - pub data: Account<'info, DataI8>, -} - -#[derive(Accounts)] -pub struct TestInit<'info> { - #[account(init, payer = payer, space = DataI8::LEN + 8)] - pub data: Account<'info, DataI8>, - #[account(mut)] - pub payer: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct TestInitZeroCopy<'info> { - #[account(init, payer = payer, space = DataZeroCopy::LEN + 8)] - pub data: Loader<'info, DataZeroCopy>, - #[account(mut)] - pub payer: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct TestInitMint<'info> { - #[account(init, mint::decimals = 6, mint::authority = payer, mint::freeze_authority = payer, payer = payer, )] - pub mint: Account<'info, Mint>, - #[account(mut)] - pub payer: Signer<'info>, - pub rent: Sysvar<'info, Rent>, - pub system_program: Program<'info, System>, - pub token_program: Program<'info, Token>, -} - -#[derive(Accounts)] -pub struct TestInitToken<'info> { - #[account(init, token::mint = mint, token::authority = payer, payer = payer, )] - pub token: Account<'info, TokenAccount>, - pub mint: Account<'info, Mint>, - #[account(mut)] - pub payer: Signer<'info>, - pub rent: Sysvar<'info, Rent>, - pub system_program: Program<'info, System>, - pub token_program: Program<'info, Token>, -} - -#[derive(Accounts)] -pub struct TestCompositePayer<'info> { - pub composite: TestInit<'info>, - #[account(init, payer = composite.payer, space = Data::LEN + 8)] - pub data: Account<'info, Data>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct TestFetchAll<'info> { - #[account(init, payer = authority, space = DataWithFilter::LEN + 8)] - pub data: Account<'info, DataWithFilter>, - #[account(mut)] - pub authority: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct TestInitWithEmptySeeds<'info> { - #[account(init, seeds = [], bump, payer = authority, space = Data::LEN + 8)] - pub pda: Account<'info, Data>, - #[account(mut)] - pub authority: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct TestEmptySeedsConstraint<'info> { - #[account(seeds = [], bump)] - /// CHECK: - pub pda: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct InitWithSpace<'info> { - #[account(init, payer = payer, space = DataU16::LEN + 8)] - pub data: Account<'info, DataU16>, - #[account(mut)] - pub payer: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct TestInitIfNeeded<'info> { - // intentionally using more space (+500) to check whether space is checked when using init_if_needed - #[account(init_if_needed, payer = payer, space = DataU16::LEN + 8 + 500)] - pub data: Account<'info, DataU16>, - #[account(mut)] - pub payer: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct TestInitIfNeededChecksOwner<'info> { - #[account(init_if_needed, payer = payer, space = 100, owner = *owner.key, seeds = [b"hello"], bump)] - /// CHECK: - pub data: UncheckedAccount<'info>, - #[account(mut)] - pub payer: Signer<'info>, - pub system_program: Program<'info, System>, - /// CHECK: - pub owner: AccountInfo<'info>, -} - -#[derive(Accounts)] -#[instruction(seed_data: String)] -pub struct TestInitIfNeededChecksSeeds<'info> { - #[account(init_if_needed, payer = payer, space = 100, seeds = [seed_data.as_bytes()], bump)] - /// CHECK: - pub data: UncheckedAccount<'info>, - #[account(mut)] - pub payer: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -#[instruction(decimals: u8)] -pub struct TestInitMintIfNeeded<'info> { - #[account(init_if_needed, mint::decimals = decimals, mint::authority = mint_authority, mint::freeze_authority = freeze_authority, payer = payer)] - pub mint: Account<'info, Mint>, - #[account(mut)] - pub payer: Signer<'info>, - pub rent: Sysvar<'info, Rent>, - pub system_program: Program<'info, System>, - pub token_program: Program<'info, Token>, - /// CHECK: - pub mint_authority: AccountInfo<'info>, - /// CHECK: - pub freeze_authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct TestInitTokenIfNeeded<'info> { - #[account(init_if_needed, token::mint = mint, token::authority = authority, payer = payer, )] - pub token: Account<'info, TokenAccount>, - pub mint: Account<'info, Mint>, - #[account(mut)] - pub payer: Signer<'info>, - pub rent: Sysvar<'info, Rent>, - pub system_program: Program<'info, System>, - pub token_program: Program<'info, Token>, - /// CHECK: - pub authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct TestInitAssociatedTokenIfNeeded<'info> { - #[account( - init_if_needed, - payer = payer, - associated_token::mint = mint, - associated_token::authority = authority - )] - pub token: Account<'info, TokenAccount>, - pub mint: Account<'info, Mint>, - #[account(mut)] - pub payer: Signer<'info>, - pub rent: Sysvar<'info, Rent>, - pub system_program: Program<'info, System>, - pub token_program: Program<'info, Token>, - pub associated_token_program: Program<'info, AssociatedToken>, - /// CHECK: - pub authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct TestMultidimensionalArray<'info> { - #[account(zero)] - pub data: Account<'info, DataMultidimensionalArray>, -} - -#[derive(Accounts)] -pub struct TestConstArraySize<'info> { - #[account(zero)] - pub data: Account<'info, DataConstArraySize>, -} - -#[derive(Accounts)] -pub struct TestConstIxDataSize<'info> { - #[account(zero)] - pub data: Account<'info, DataConstArraySize>, -} - -#[derive(Accounts)] -pub struct TestMultidimensionalArrayConstSizes<'info> { - #[account(zero)] - pub data: Account<'info, DataMultidimensionalArrayConstSizes>, -} - -#[derive(Accounts)] -pub struct NoRentExempt<'info> { - /// CHECK: - pub data: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct EnforceRentExempt<'info> { - #[account(rent_exempt = enforce)] - /// CHECK: - pub data: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct InitDecreaseLamports<'info> { - #[account(init, payer = user, space = 1000)] - /// CHECK: - pub data: AccountInfo<'info>, - #[account(mut)] - pub user: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct InitIfNeededChecksRentExemption<'info> { - #[account(init_if_needed, payer = user, space = 1000)] - /// CHECK: - pub data: AccountInfo<'info>, - #[account(mut)] - pub user: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -#[instruction(bump: u8, second_bump: u8)] -pub struct TestProgramIdConstraint<'info> { - // not a real associated token account - // just deriving like this for testing purposes - #[account(seeds = [b"seed"], bump = bump, seeds::program = anchor_spl::associated_token::ID)] - /// CHECK: - first: AccountInfo<'info>, - - #[account(seeds = [b"seed"], bump = second_bump, seeds::program = crate::ID)] - /// CHECK: - second: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct TestProgramIdConstraintUsingFindPda<'info> { - // not a real associated token account - // just deriving like this for testing purposes - #[account(seeds = [b"seed"], bump, seeds::program = anchor_spl::associated_token::ID)] - /// CHECK: - first: AccountInfo<'info>, - - #[account(seeds = [b"seed"], bump, seeds::program = crate::ID)] - /// CHECK: - second: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct TestUnsafeFieldSafetyErrors<'info> { - #[doc = "test"] - /// CHECK: - pub data: UncheckedAccount<'info>, - #[account(mut)] - /// CHECK: - pub data_two: UncheckedAccount<'info>, - #[account( - seeds = [b"my-seed", signer.key.as_ref()], - bump - )] - /// CHECK: - pub data_three: UncheckedAccount<'info>, - /// CHECK: - pub data_four: UncheckedAccount<'info>, - pub signer: Signer<'info>, - pub system_program: Program<'info, System>, -} - -#[derive(Accounts)] -pub struct TestConstraintToken<'info> { - #[account( - token::mint = mint, - token::authority = payer - )] - pub token: Account<'info, TokenAccount>, - pub mint: Account<'info, Mint>, - pub payer: Signer<'info>, -} - -#[derive(Accounts)] -pub struct TestAuthorityConstraint<'info> { - #[account( - token::mint = mint, - token::authority = fake_authority - )] - pub token: Account<'info, TokenAccount>, - pub mint: Account<'info, Mint>, - pub fake_authority: AccountInfo<'info>, -} -#[derive(Accounts)] -pub struct TestOnlyAuthorityConstraint<'info> { - #[account( - token::authority = payer - )] - pub token: Account<'info, TokenAccount>, - pub mint: Account<'info, Mint>, - pub payer: Signer<'info>, -} -#[derive(Accounts)] -pub struct TestOnlyMintConstraint<'info> { - #[account( - token::mint = mint, - )] - pub token: Account<'info, TokenAccount>, - pub mint: Account<'info, Mint>, -} - -#[derive(Accounts)] -#[instruction(decimals: u8)] -pub struct TestMintConstraint<'info> { - #[account( - mint::decimals = decimals, - mint::authority = mint_authority, - mint::freeze_authority = freeze_authority - )] - pub mint: Account<'info, Mint>, - pub mint_authority: AccountInfo<'info>, - pub freeze_authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -#[instruction(decimals: u8)] -pub struct TestMintOnlyDecimalsConstraint<'info> { - #[account( - mint::decimals = decimals, - )] - pub mint: Account<'info, Mint>, -} - -#[derive(Accounts)] -pub struct TestMintAuthorityConstraint<'info> { - #[account( - mint::authority = mint_authority, - mint::freeze_authority = freeze_authority - )] - pub mint: Account<'info, Mint>, - pub mint_authority: AccountInfo<'info>, - pub freeze_authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct TestMintOneAuthorityConstraint<'info> { - #[account( - mint::authority = mint_authority, - )] - pub mint: Account<'info, Mint>, - pub mint_authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -#[instruction(decimals: u8)] -pub struct TestMintMissMintAuthConstraint<'info> { - #[account( - mint::decimals = decimals, - mint::freeze_authority = freeze_authority, - )] - pub mint: Account<'info, Mint>, - pub freeze_authority: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct TestAssociatedToken<'info> { - #[account( - associated_token::mint = mint, - associated_token::authority = authority, - )] - pub token: Account<'info, TokenAccount>, - pub mint: Account<'info, Mint>, - pub authority: AccountInfo<'info>, -} diff --git a/tests/misc/programs/misc/src/event.rs b/tests/misc/programs/misc/src/event.rs deleted file mode 100644 index 80b7dd8f..00000000 --- a/tests/misc/programs/misc/src/event.rs +++ /dev/null @@ -1,34 +0,0 @@ -use anchor_lang::prelude::*; - -pub const MAX_EVENT_SIZE: usize = 10; -pub const MAX_EVENT_SIZE_U8: u8 = 11; - -#[event] -pub struct E1 { - pub data: u32, -} - -#[event] -pub struct E2 { - pub data: u32, -} - -#[event] -pub struct E3 { - pub data: u32, -} - -#[event] -pub struct E4 { - pub data: Pubkey, -} - -#[event] -pub struct E5 { - pub data: [u8; MAX_EVENT_SIZE], -} - -#[event] -pub struct E6 { - pub data: [u8; MAX_EVENT_SIZE_U8 as usize], -} diff --git a/tests/misc/programs/misc/src/lib.rs b/tests/misc/programs/misc/src/lib.rs deleted file mode 100644 index 821f5eb6..00000000 --- a/tests/misc/programs/misc/src/lib.rs +++ /dev/null @@ -1,356 +0,0 @@ -//! Misc example is a catchall program for testing unrelated features. -//! It's not too instructive/coherent by itself, so please see other examples. - -use account::MAX_SIZE; -use anchor_lang::prelude::*; -use context::*; -use event::*; -use misc2::Auth; - -mod account; -mod context; -mod event; - -declare_id!("3TEqcc8xhrhdspwbvoamUJe2borm4Nr72JxL66k6rgrh"); - -#[constant] -pub const BASE: u128 = 1_000_000; -#[constant] -pub const DECIMALS: u8 = 6; -pub const NO_IDL: u16 = 55; - -#[program] -pub mod misc { - use super::*; - - pub const SIZE: u64 = 99; - - #[state(SIZE)] - pub struct MyState { - pub v: Vec, - } - - impl MyState { - pub fn new(_ctx: Context) -> Result { - Ok(Self { v: vec![] }) - } - - pub fn remaining_accounts(&mut self, ctx: Context) -> Result<()> { - if ctx.remaining_accounts.len() != 1 { - return Err(ProgramError::Custom(1).into()); // Arbitrary error. - } - Ok(()) - } - } - - pub fn initialize(ctx: Context, udata: u128, idata: i128) -> Result<()> { - ctx.accounts.data.udata = udata; - ctx.accounts.data.idata = idata; - Ok(()) - } - - pub fn initialize_no_rent_exempt(ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn initialize_skip_rent_exempt(ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_owner(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_executable(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_state_cpi(ctx: Context, data: u64) -> Result<()> { - let cpi_program = ctx.accounts.misc2_program.clone(); - let cpi_accounts = Auth { - authority: ctx.accounts.authority.clone(), - }; - let ctx = ctx.accounts.cpi_state.context(cpi_program, cpi_accounts); - misc2::cpi::state::set_data(ctx, data) - } - - pub fn test_u16(ctx: Context, data: u16) -> Result<()> { - ctx.accounts.my_account.data = data; - Ok(()) - } - - pub fn test_simulate(_ctx: Context, data: u32) -> Result<()> { - emit!(E1 { data }); - emit!(E2 { data: 1234 }); - emit!(E3 { data: 9 }); - emit!(E5 { - data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - }); - emit!(E6 { - data: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - }); - Ok(()) - } - - pub fn test_i8(ctx: Context, data: i8) -> Result<()> { - ctx.accounts.data.data = data; - Ok(()) - } - - pub fn test_i16(ctx: Context, data: i16) -> Result<()> { - ctx.accounts.data.data = data; - Ok(()) - } - - pub fn test_const_array_size(ctx: Context, data: u8) -> Result<()> { - ctx.accounts.data.data[0] = data; - Ok(()) - } - - pub fn test_const_ix_data_size( - ctx: Context, - data: [u8; MAX_SIZE], - ) -> Result<()> { - ctx.accounts.data.data = data; - Ok(()) - } - - pub fn test_close(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_instruction_constraint( - _ctx: Context, - _nonce: u8, - ) -> Result<()> { - Ok(()) - } - - pub fn test_pda_init( - ctx: Context, - _domain: String, - _seed: Vec, - _bump: u8, - ) -> Result<()> { - ctx.accounts.my_pda.data = 6; - Ok(()) - } - - pub fn test_pda_init_zero_copy(ctx: Context) -> Result<()> { - let mut acc = ctx.accounts.my_pda.load_init()?; - acc.data = 9; - acc.bump = *ctx.bumps.get("my_pda").unwrap(); - Ok(()) - } - - pub fn test_pda_mut_zero_copy(ctx: Context) -> Result<()> { - let mut acc = ctx.accounts.my_pda.load_mut()?; - acc.data = 1234; - Ok(()) - } - - pub fn test_token_seeds_init(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn default<'info>( - _program_id: &Pubkey, - _accounts: &[AccountInfo<'info>], - _data: &[u8], - ) -> Result<()> { - Err(ProgramError::Custom(1234).into()) - } - - pub fn test_init(ctx: Context) -> Result<()> { - ctx.accounts.data.data = 3; - Ok(()) - } - - pub fn test_init_zero_copy(ctx: Context) -> Result<()> { - let mut data = ctx.accounts.data.load_init()?; - data.data = 10; - data.bump = 2; - Ok(()) - } - - pub fn test_init_mint(ctx: Context) -> Result<()> { - assert!(ctx.accounts.mint.decimals == 6); - Ok(()) - } - - pub fn test_init_token(ctx: Context) -> Result<()> { - assert!(ctx.accounts.token.mint == ctx.accounts.mint.key()); - Ok(()) - } - - pub fn test_composite_payer(ctx: Context) -> Result<()> { - ctx.accounts.composite.data.data = 1; - ctx.accounts.data.udata = 2; - ctx.accounts.data.idata = 3; - Ok(()) - } - - pub fn test_init_associated_token(ctx: Context) -> Result<()> { - assert!(ctx.accounts.token.mint == ctx.accounts.mint.key()); - Ok(()) - } - - pub fn test_validate_associated_token( - _ctx: Context, - ) -> Result<()> { - Ok(()) - } - - pub fn test_fetch_all(ctx: Context, filterable: Pubkey) -> Result<()> { - ctx.accounts.data.authority = ctx.accounts.authority.key(); - ctx.accounts.data.filterable = filterable; - Ok(()) - } - - pub fn test_init_with_empty_seeds(ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_empty_seeds_constraint(ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_init_if_needed(ctx: Context, data: u16) -> Result<()> { - ctx.accounts.data.data = data; - Ok(()) - } - - pub fn test_init_if_needed_checks_owner( - ctx: Context, - ) -> Result<()> { - Ok(()) - } - - pub fn test_init_if_needed_checks_seeds( - ctx: Context, - seed_data: String, - ) -> Result<()> { - Ok(()) - } - - pub fn test_init_mint_if_needed( - ctx: Context, - decimals: u8, - ) -> Result<()> { - Ok(()) - } - - pub fn test_init_token_if_needed(ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_init_associated_token_if_needed( - ctx: Context, - ) -> Result<()> { - Ok(()) - } - - pub fn init_with_space(ctx: Context, data: u16) -> Result<()> { - Ok(()) - } - - pub fn test_multidimensional_array( - ctx: Context, - data: [[u8; 10]; 10], - ) -> Result<()> { - ctx.accounts.data.data = data; - Ok(()) - } - - pub fn test_multidimensional_array_const_sizes( - ctx: Context, - data: [[u8; 11]; 10], - ) -> Result<()> { - ctx.accounts.data.data = data; - Ok(()) - } - - pub fn test_no_rent_exempt(ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_enforce_rent_exempt(ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn init_decrease_lamports(ctx: Context) -> Result<()> { - **ctx.accounts.data.try_borrow_mut_lamports()? -= 1; - **ctx.accounts.user.try_borrow_mut_lamports()? += 1; - Ok(()) - } - - pub fn init_if_needed_checks_rent_exemption( - _ctx: Context, - ) -> Result<()> { - Ok(()) - } - - pub fn test_program_id_constraint( - _ctx: Context, - _bump: u8, - _second_bump: u8, - ) -> Result<()> { - Ok(()) - } - - pub fn test_program_id_constraint_find_pda( - _ctx: Context, - ) -> Result<()> { - Ok(()) - } - - pub fn test_token_constraint(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_token_auth_constraint(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_only_auth_constraint(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_only_mint_constraint(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn test_mint_constraint(_ctx: Context, _decimals: u8) -> Result<()> { - Ok(()) - } - - pub fn test_mint_only_decimals_constraint( - _ctx: Context, - _decimals: u8, - ) -> Result<()> { - Ok(()) - } - - pub fn test_mint_only_auth_constraint( - _ctx: Context, - ) -> Result<()> { - Ok(()) - } - - pub fn test_mint_only_one_auth_constraint( - _ctx: Context, - ) -> Result<()> { - Ok(()) - } - - pub fn test_mint_miss_mint_auth_constraint( - _ctx: Context, - _decimals: u8, - ) -> Result<()> { - Ok(()) - } - - pub fn test_associated_constraint(_ctx: Context) -> Result<()> { - Ok(()) - } -} diff --git a/tests/misc/programs/misc2/Cargo.toml b/tests/misc/programs/misc2/Cargo.toml deleted file mode 100644 index d3eb0295..00000000 --- a/tests/misc/programs/misc2/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "misc2" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "misc2" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } diff --git a/tests/misc/programs/misc2/Xargo.toml b/tests/misc/programs/misc2/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/misc/programs/misc2/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/misc/programs/misc2/src/lib.rs b/tests/misc/programs/misc2/src/lib.rs deleted file mode 100644 index d651a2c6..00000000 --- a/tests/misc/programs/misc2/src/lib.rs +++ /dev/null @@ -1,38 +0,0 @@ -use anchor_lang::prelude::*; - -declare_id!("HmbTLCmaGvZhKnn1Zfa1JVnp7vkMV4DYVxPLWBVoN65L"); - -#[program] -pub mod misc2 { - use super::*; - - #[state] - pub struct MyState { - pub data: u64, - pub auth: Pubkey, - } - - impl MyState { - pub fn new(ctx: Context) -> Result { - Ok(Self { - data: 0, - auth: *ctx.accounts.authority.key, - }) - } - - pub fn set_data(&mut self, ctx: Context, data: u64) -> Result<()> { - if self.auth != *ctx.accounts.authority.key { - return Err(ProgramError::Custom(1234).into()); // Arbitrary error code. - } - self.data = data; - Ok(()) - } - } -} - -#[derive(Accounts)] -pub struct Auth<'info> { - #[account(signer)] - /// CHECK: - pub authority: AccountInfo<'info>, -} diff --git a/tests/misc/programs/shared/Cargo.toml b/tests/misc/programs/shared/Cargo.toml deleted file mode 100644 index c0dff4a0..00000000 --- a/tests/misc/programs/shared/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "shared" -version = "0.1.0" -rust-version = "1.56" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/tests/misc/programs/shared/src/lib.rs b/tests/misc/programs/shared/src/lib.rs deleted file mode 100644 index 31e1bb20..00000000 --- a/tests/misc/programs/shared/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} diff --git a/tests/misc/tests/init-if-needed/Test.toml b/tests/misc/tests/init-if-needed/Test.toml deleted file mode 100644 index 9eb55d60..00000000 --- a/tests/misc/tests/init-if-needed/Test.toml +++ /dev/null @@ -1,2 +0,0 @@ -[scripts] -test = "yarn run ts-mocha -t 1000000 ./tests/init-if-needed/*.ts" diff --git a/tests/misc/tests/init-if-needed/init-if-needed.ts b/tests/misc/tests/init-if-needed/init-if-needed.ts deleted file mode 100644 index dbeaf3c1..00000000 --- a/tests/misc/tests/init-if-needed/init-if-needed.ts +++ /dev/null @@ -1,101 +0,0 @@ -import * as anchor from "@project-serum/anchor"; -import { AnchorError, Program } from "@project-serum/anchor"; -import { InitIfNeeded } from "../../target/types/init_if_needed"; -import { SystemProgram, LAMPORTS_PER_SOL } from "@solana/web3.js"; -import { expect } from "chai"; - -describe("init-if-needed", () => { - anchor.setProvider(anchor.AnchorProvider.env()); - - const program = anchor.workspace.InitIfNeeded as Program; - - it("init_if_needed should reject a CLOSED discriminator if init is NOT NEEDED", async () => { - const account = anchor.web3.Keypair.generate(); - - await program.methods - .initialize(1) - .accounts({ - acc: account.publicKey, - }) - .signers([account]) - .rpc(); - - const oldState = await program.account.myData.fetch(account.publicKey); - expect(oldState.val.toNumber()).to.equal(1000); - - // This initialize call should fail because the account has the account discriminator - // set to the CLOSED one - try { - await program.methods - .initialize(5) - .accounts({ - acc: account.publicKey, - }) - .signers([account]) - .preInstructions([ - await program.methods - .close() - .accounts({ - acc: account.publicKey, - receiver: program.provider.wallet.publicKey, - }) - .instruction(), - SystemProgram.transfer({ - fromPubkey: program.provider.wallet.publicKey, - toPubkey: account.publicKey, - lamports: 1 * LAMPORTS_PER_SOL, - }), - ]) - .rpc(); - } catch (_err) { - expect(_err).to.be.instanceOf(AnchorError); - const err: AnchorError = _err; - expect(err.error.errorCode.code).to.equal("AccountDiscriminatorMismatch"); - } - }); - - it("init_if_needed should reject a discriminator of a different account if init is NOT NEEDED", async () => { - const account = anchor.web3.Keypair.generate(); - console.log("account: ", account.publicKey.toBase58()); - const otherAccount = anchor.web3.Keypair.generate(); - console.log("otherAccount: ", otherAccount.publicKey.toBase58()); - - await program.methods - .initialize(1) - .accounts({ - acc: account.publicKey, - }) - .signers([account]) - .rpc(); - - const oldState = await program.account.myData.fetch(account.publicKey); - expect(oldState.val.toNumber()).to.equal(1000); - - await program.methods - .secondInitialize(1) - .accounts({ - acc: otherAccount.publicKey, - }) - .signers([otherAccount]) - .rpc(); - - const secondState = await program.account.otherData.fetch( - otherAccount.publicKey - ); - expect(secondState.otherVal.toNumber()).to.equal(2000); - - try { - await program.methods - .initialize(3) - .accounts({ - acc: otherAccount.publicKey, - }) - .signers([otherAccount]) - .rpc(); - } catch (_err) { - expect(_err).to.be.instanceOf(AnchorError); - const err: AnchorError = _err; - expect(err.error.errorCode.code).to.equal("AccountDiscriminatorMismatch"); - } - }); -}); diff --git a/tests/misc/tests/misc/Test.toml b/tests/misc/tests/misc/Test.toml deleted file mode 100644 index bcacd6b4..00000000 --- a/tests/misc/tests/misc/Test.toml +++ /dev/null @@ -1,6 +0,0 @@ -[[test.genesis]] -address = "FtMNMKp9DZHKWUyVAsj3Q5QV8ow4P3fUPP7ZrWEQJzKr" -program = "../../target/deploy/misc.so" - -[scripts] -test = "yarn run ts-mocha -t 1000000 ./tests/misc/*.ts" diff --git a/tests/misc/tests/misc/misc.ts b/tests/misc/tests/misc/misc.ts deleted file mode 100644 index 3f86dbbb..00000000 --- a/tests/misc/tests/misc/misc.ts +++ /dev/null @@ -1,2139 +0,0 @@ -import * as anchor from "@project-serum/anchor"; -import { Program, BN, IdlAccounts, AnchorError } from "@project-serum/anchor"; -import { - PublicKey, - Keypair, - SystemProgram, - SYSVAR_RENT_PUBKEY, -} from "@solana/web3.js"; -import { - TOKEN_PROGRAM_ID, - Token, - ASSOCIATED_TOKEN_PROGRAM_ID, -} from "@solana/spl-token"; -import { Misc } from "../../target/types/misc"; -import { Misc2 } from "../../target/types/misc2"; -const utf8 = anchor.utils.bytes.utf8; -const { assert, expect } = require("chai"); -const nativeAssert = require("assert"); -const miscIdl = require("../../target/idl/misc.json"); - -describe("misc", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.env()); - const program = anchor.workspace.Misc as Program; - const misc2Program = anchor.workspace.Misc2 as Program; - - it("Can allocate extra space for a state constructor", async () => { - const tx = await program.state.rpc.new(); - const addr = await program.state.address(); - const state = await program.state.fetch(); - const accountInfo = await program.provider.connection.getAccountInfo(addr); - assert.isTrue(state.v.equals(Buffer.from([]))); - assert.lengthOf(accountInfo.data, 99); - }); - - it("Can use remaining accounts for a state instruction", async () => { - await program.state.rpc.remainingAccounts({ - remainingAccounts: [ - { pubkey: misc2Program.programId, isWritable: false, isSigner: false }, - ], - }); - }); - - const data = anchor.web3.Keypair.generate(); - - it("Can use u128 and i128", async () => { - const tx = await program.rpc.initialize( - new anchor.BN(1234), - new anchor.BN(22), - { - accounts: { - data: data.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [data], - instructions: [await program.account.data.createInstruction(data)], - } - ); - const dataAccount = await program.account.data.fetch(data.publicKey); - assert.isTrue(dataAccount.udata.eq(new anchor.BN(1234))); - assert.isTrue(dataAccount.idata.eq(new anchor.BN(22))); - }); - - it("Can use u16", async () => { - const data = anchor.web3.Keypair.generate(); - const tx = await program.rpc.testU16(99, { - accounts: { - myAccount: data.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [data], - instructions: [await program.account.dataU16.createInstruction(data)], - }); - const dataAccount = await program.account.dataU16.fetch(data.publicKey); - assert.strictEqual(dataAccount.data, 99); - }); - - it("Can embed programs into genesis from the Anchor.toml", async () => { - const pid = new anchor.web3.PublicKey( - "FtMNMKp9DZHKWUyVAsj3Q5QV8ow4P3fUPP7ZrWEQJzKr" - ); - let accInfo = await anchor.getProvider().connection.getAccountInfo(pid); - assert.isTrue(accInfo.executable); - }); - - it("Can use the owner constraint", async () => { - await program.rpc.testOwner({ - accounts: { - data: data.publicKey, - misc: program.programId, - }, - }); - - await nativeAssert.rejects( - async () => { - await program.rpc.testOwner({ - accounts: { - data: program.provider.wallet.publicKey, - misc: program.programId, - }, - }); - }, - (err) => { - return true; - } - ); - }); - - it("Can use the executable attribute", async () => { - await program.rpc.testExecutable({ - accounts: { - program: program.programId, - }, - }); - - await nativeAssert.rejects( - async () => { - await program.rpc.testExecutable({ - accounts: { - program: program.provider.wallet.publicKey, - }, - }); - }, - (err) => { - return true; - } - ); - }); - - it("Can CPI to state instructions", async () => { - const oldData = new anchor.BN(0); - await misc2Program.state.rpc.new({ - accounts: { - authority: program.provider.wallet.publicKey, - }, - }); - let stateAccount = await misc2Program.state.fetch(); - assert.isTrue(stateAccount.data.eq(oldData)); - assert.isTrue(stateAccount.auth.equals(program.provider.wallet.publicKey)); - const newData = new anchor.BN(2134); - await program.rpc.testStateCpi(newData, { - accounts: { - authority: program.provider.wallet.publicKey, - cpiState: await misc2Program.state.address(), - misc2Program: misc2Program.programId, - }, - }); - stateAccount = await misc2Program.state.fetch(); - assert.isTrue(stateAccount.data.eq(newData)); - assert.isTrue(stateAccount.auth.equals(program.provider.wallet.publicKey)); - }); - - it("Can retrieve events when simulating a transaction", async () => { - const resp = await program.simulate.testSimulate(44); - const expectedRaw = [ - "Program 3TEqcc8xhrhdspwbvoamUJe2borm4Nr72JxL66k6rgrh invoke [1]", - "Program log: Instruction: TestSimulate", - "Program data: NgyCA9omwbMsAAAA", - "Program data: fPhuIELK/k7SBAAA", - "Program data: jvbowsvlmkcJAAAA", - "Program data: zxM5neEnS1kBAgMEBQYHCAkK", - "Program data: g06Ei2GL1gIBAgMEBQYHCAkKCw==", - ]; - - assert.deepStrictEqual(expectedRaw, resp.raw.slice(0, -2)); - assert.strictEqual(resp.events[0].name, "E1"); - assert.strictEqual(resp.events[0].data.data, 44); - assert.strictEqual(resp.events[1].name, "E2"); - assert.strictEqual(resp.events[1].data.data, 1234); - assert.strictEqual(resp.events[2].name, "E3"); - assert.strictEqual(resp.events[2].data.data, 9); - assert.strictEqual(resp.events[3].name, "E5"); - assert.deepStrictEqual( - resp.events[3].data.data, - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - ); - assert.strictEqual(resp.events[4].name, "E6"); - assert.deepStrictEqual( - resp.events[4].data.data, - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - ); - }); - - let dataI8; - - it("Can use i8 in the idl", async () => { - dataI8 = anchor.web3.Keypair.generate(); - await program.rpc.testI8(-3, { - accounts: { - data: dataI8.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - instructions: [await program.account.dataI8.createInstruction(dataI8)], - signers: [dataI8], - }); - const dataAccount = await program.account.dataI8.fetch(dataI8.publicKey); - assert.strictEqual(dataAccount.data, -3); - }); - - let dataPubkey; - - it("Can use i16 in the idl", async () => { - const data = anchor.web3.Keypair.generate(); - await program.rpc.testI16(-2048, { - accounts: { - data: data.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - instructions: [await program.account.dataI16.createInstruction(data)], - signers: [data], - }); - const dataAccount = await program.account.dataI16.fetch(data.publicKey); - assert.strictEqual(dataAccount.data, -2048); - - dataPubkey = data.publicKey; - }); - - it("Can use base58 strings to fetch an account", async () => { - const dataAccount = await program.account.dataI16.fetch( - dataPubkey.toString() - ); - assert.strictEqual(dataAccount.data, -2048); - }); - - it("Should fail to close an account when sending lamports to itself", async () => { - try { - await program.rpc.testClose({ - accounts: { - data: data.publicKey, - solDest: data.publicKey, - }, - }); - expect(false).to.be.true; - } catch (err) { - const errMsg = "A close constraint was violated"; - assert.strictEqual(err.error.errorMessage, errMsg); - assert.strictEqual(err.error.errorCode.number, 2011); - } - }); - - it("Can close an account", async () => { - const openAccount = await program.provider.connection.getAccountInfo( - data.publicKey - ); - assert.isNotNull(openAccount); - - let beforeBalance = ( - await program.provider.connection.getAccountInfo( - program.provider.wallet.publicKey - ) - ).lamports; - - await program.rpc.testClose({ - accounts: { - data: data.publicKey, - solDest: program.provider.wallet.publicKey, - }, - }); - - let afterBalance = ( - await program.provider.connection.getAccountInfo( - program.provider.wallet.publicKey - ) - ).lamports; - - // Retrieved rent exemption sol. - expect(afterBalance > beforeBalance).to.be.true; - - const closedAccount = await program.provider.connection.getAccountInfo( - data.publicKey - ); - assert.isNull(closedAccount); - }); - - it("Can use instruction data in accounts constraints", async () => { - // b"my-seed" - const seed = Buffer.from([109, 121, 45, 115, 101, 101, 100]); - const [myPda, nonce] = await PublicKey.findProgramAddress( - [seed, anchor.web3.SYSVAR_RENT_PUBKEY.toBuffer()], - program.programId - ); - - await program.rpc.testInstructionConstraint(nonce, { - accounts: { - myPda, - myAccount: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - }); - }); - - it("Can create a PDA account with instruction data", async () => { - const seed = Buffer.from([1, 2, 3, 4]); - const domain = "my-domain"; - const foo = anchor.web3.SYSVAR_RENT_PUBKEY; - const [myPda, nonce] = await PublicKey.findProgramAddress( - [ - Buffer.from(anchor.utils.bytes.utf8.encode("my-seed")), - Buffer.from(anchor.utils.bytes.utf8.encode(domain)), - foo.toBuffer(), - seed, - ], - program.programId - ); - - await program.rpc.testPdaInit(domain, seed, nonce, { - accounts: { - myPda, - myPayer: program.provider.wallet.publicKey, - foo, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - systemProgram: anchor.web3.SystemProgram.programId, - }, - }); - - const myPdaAccount = await program.account.dataU16.fetch(myPda); - assert.strictEqual(myPdaAccount.data, 6); - }); - - it("Can create a zero copy PDA account", async () => { - const [myPda, nonce] = await PublicKey.findProgramAddress( - [Buffer.from(anchor.utils.bytes.utf8.encode("my-seed"))], - program.programId - ); - await program.rpc.testPdaInitZeroCopy({ - accounts: { - myPda, - myPayer: program.provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - systemProgram: anchor.web3.SystemProgram.programId, - }, - }); - - const myPdaAccount = await program.account.dataZeroCopy.fetch(myPda); - assert.strictEqual(myPdaAccount.data, 9); - assert.strictEqual(myPdaAccount.bump, nonce); - }); - - it("Can write to a zero copy PDA account", async () => { - const [myPda, bump] = await PublicKey.findProgramAddress( - [Buffer.from(anchor.utils.bytes.utf8.encode("my-seed"))], - program.programId - ); - await program.rpc.testPdaMutZeroCopy({ - accounts: { - myPda, - myPayer: program.provider.wallet.publicKey, - }, - }); - - const myPdaAccount = await program.account.dataZeroCopy.fetch(myPda); - assert.strictEqual(myPdaAccount.data, 1234); - assert.strictEqual(myPdaAccount.bump, bump); - }); - - it("Can create a token account from seeds pda", async () => { - const [mint, mint_bump] = await PublicKey.findProgramAddress( - [Buffer.from(anchor.utils.bytes.utf8.encode("my-mint-seed"))], - program.programId - ); - const [myPda, token_bump] = await PublicKey.findProgramAddress( - [Buffer.from(anchor.utils.bytes.utf8.encode("my-token-seed"))], - program.programId - ); - await program.rpc.testTokenSeedsInit({ - accounts: { - myPda, - mint, - authority: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - tokenProgram: TOKEN_PROGRAM_ID, - }, - }); - - const mintAccount = new Token( - program.provider.connection, - mint, - TOKEN_PROGRAM_ID, - program.provider.wallet.payer - ); - const account = await mintAccount.getAccountInfo(myPda); - assert.strictEqual(account.state, 1); - assert.strictEqual(account.amount.toNumber(), 0); - assert.isTrue(account.isInitialized); - assert.isTrue(account.owner.equals(program.provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(mint)); - }); - - it("Can execute a fallback function", async () => { - await nativeAssert.rejects( - async () => { - await anchor.utils.rpc.invoke(program.programId); - }, - (err) => { - assert.isTrue(err.toString().includes("custom program error: 0x4d2")); - return true; - } - ); - }); - - it("Can init a random account", async () => { - const data = anchor.web3.Keypair.generate(); - await program.rpc.testInit({ - accounts: { - data: data.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data], - }); - - const account = await program.account.dataI8.fetch(data.publicKey); - assert.strictEqual(account.data, 3); - }); - - it("Can init a random account prefunded", async () => { - const data = anchor.web3.Keypair.generate(); - await program.rpc.testInit({ - accounts: { - data: data.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data], - instructions: [ - anchor.web3.SystemProgram.transfer({ - fromPubkey: program.provider.wallet.publicKey, - toPubkey: data.publicKey, - lamports: 4039280, - }), - ], - }); - - const account = await program.account.dataI8.fetch(data.publicKey); - assert.strictEqual(account.data, 3); - }); - - it("Can init a random zero copy account", async () => { - const data = anchor.web3.Keypair.generate(); - await program.rpc.testInitZeroCopy({ - accounts: { - data: data.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data], - }); - const account = await program.account.dataZeroCopy.fetch(data.publicKey); - assert.strictEqual(account.data, 10); - assert.strictEqual(account.bump, 2); - }); - - let mint = undefined; - - it("Can create a random mint account", async () => { - mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - program.provider.wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.strictEqual(mintAccount.decimals, 6); - assert.isTrue( - mintAccount.mintAuthority.equals(program.provider.wallet.publicKey) - ); - assert.isTrue( - mintAccount.freezeAuthority.equals(program.provider.wallet.publicKey) - ); - }); - - it("Can create a random mint account prefunded", async () => { - mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - instructions: [ - anchor.web3.SystemProgram.transfer({ - fromPubkey: program.provider.wallet.publicKey, - toPubkey: mint.publicKey, - lamports: 4039280, - }), - ], - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - program.provider.wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.strictEqual(mintAccount.decimals, 6); - assert.isTrue( - mintAccount.mintAuthority.equals(program.provider.wallet.publicKey) - ); - }); - - it("Can create a random token account", async () => { - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - program.provider.wallet.payer - ); - const account = await client.getAccountInfo(token.publicKey); - assert.strictEqual(account.state, 1); - assert.strictEqual(account.amount.toNumber(), 0); - assert.isTrue(account.isInitialized); - assert.isTrue(account.owner.equals(program.provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(mint.publicKey)); - }); - - it("Can create a random token with prefunding", async () => { - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - instructions: [ - anchor.web3.SystemProgram.transfer({ - fromPubkey: program.provider.wallet.publicKey, - toPubkey: token.publicKey, - lamports: 4039280, - }), - ], - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - program.provider.wallet.payer - ); - const account = await client.getAccountInfo(token.publicKey); - assert.strictEqual(account.state, 1); - assert.strictEqual(account.amount.toNumber(), 0); - assert.isTrue(account.isInitialized); - assert.isTrue(account.owner.equals(program.provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(mint.publicKey)); - }); - - it("Can create a random token with prefunding under the rent exemption", async () => { - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - instructions: [ - anchor.web3.SystemProgram.transfer({ - fromPubkey: program.provider.wallet.publicKey, - toPubkey: token.publicKey, - lamports: 1, - }), - ], - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - program.provider.wallet.payer - ); - const account = await client.getAccountInfo(token.publicKey); - assert.strictEqual(account.state, 1); - assert.strictEqual(account.amount.toNumber(), 0); - assert.isTrue(account.isInitialized); - assert.isTrue(account.owner.equals(program.provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(mint.publicKey)); - }); - - it("Can initialize multiple accounts via a composite payer", async () => { - const data1 = anchor.web3.Keypair.generate(); - const data2 = anchor.web3.Keypair.generate(); - - const tx = await program.rpc.testCompositePayer({ - accounts: { - composite: { - data: data1.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - data: data2.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data1, data2], - }); - - const account1 = await program.account.dataI8.fetch(data1.publicKey); - assert.strictEqual(account1.data, 1); - - const account2 = await program.account.data.fetch(data2.publicKey); - assert.strictEqual(account2.udata.toNumber(), 2); - assert.strictEqual(account2.idata.toNumber(), 3); - }); - - describe("associated_token constraints", () => { - let associatedToken = null; - // apparently cannot await here so doing it in the 'it' statements - let client = Token.createMint( - program.provider.connection, - program.provider.wallet.payer, - program.provider.wallet.publicKey, - program.provider.wallet.publicKey, - 9, - TOKEN_PROGRAM_ID - ); - - it("Can create an associated token account", async () => { - const localClient = await client; - associatedToken = await Token.getAssociatedTokenAddress( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - localClient.publicKey, - program.provider.wallet.publicKey - ); - - await program.rpc.testInitAssociatedToken({ - accounts: { - token: associatedToken, - mint: localClient.publicKey, - payer: program.provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - }, - }); - - const account = await localClient.getAccountInfo(associatedToken); - assert.strictEqual(account.state, 1); - assert.strictEqual(account.amount.toNumber(), 0); - assert.isTrue(account.isInitialized); - assert.isTrue(account.owner.equals(program.provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(localClient.publicKey)); - }); - - it("Can validate associated_token constraints", async () => { - const localClient = await client; - await program.rpc.testValidateAssociatedToken({ - accounts: { - token: associatedToken, - mint: localClient.publicKey, - wallet: program.provider.wallet.publicKey, - }, - }); - - let otherMint = await Token.createMint( - program.provider.connection, - program.provider.wallet.payer, - program.provider.wallet.publicKey, - program.provider.wallet.publicKey, - 9, - TOKEN_PROGRAM_ID - ); - - await nativeAssert.rejects( - async () => { - await program.rpc.testValidateAssociatedToken({ - accounts: { - token: associatedToken, - mint: otherMint.publicKey, - wallet: program.provider.wallet.publicKey, - }, - }); - }, - (err) => { - assert.strictEqual(err.error.errorCode.number, 2009); - return true; - } - ); - }); - - it("associated_token constraints check do not allow authority change", async () => { - const localClient = await client; - await program.rpc.testValidateAssociatedToken({ - accounts: { - token: associatedToken, - mint: localClient.publicKey, - wallet: program.provider.wallet.publicKey, - }, - }); - - await localClient.setAuthority( - associatedToken, - anchor.web3.Keypair.generate().publicKey, - "AccountOwner", - program.provider.wallet.payer, - [] - ); - - await nativeAssert.rejects( - async () => { - await program.rpc.testValidateAssociatedToken({ - accounts: { - token: associatedToken, - mint: localClient.publicKey, - wallet: program.provider.wallet.publicKey, - }, - }); - }, - (err) => { - assert.strictEqual(err.error.errorCode.number, 2015); - return true; - } - ); - }); - }); - - it("Can fetch all accounts of a given type", async () => { - // Initialize the accounts. - const data1 = anchor.web3.Keypair.generate(); - const data2 = anchor.web3.Keypair.generate(); - const data3 = anchor.web3.Keypair.generate(); - const data4 = anchor.web3.Keypair.generate(); - // Initialize filterable data. - const filterable1 = anchor.web3.Keypair.generate().publicKey; - const filterable2 = anchor.web3.Keypair.generate().publicKey; - // Set up a secondary wallet and program. - const anotherProgram = new anchor.Program( - miscIdl, - program.programId, - new anchor.AnchorProvider( - program.provider.connection, - new anchor.Wallet(anchor.web3.Keypair.generate()), - { commitment: program.provider.connection.commitment } - ) - ); - // Request airdrop for secondary wallet. - const signature = await program.provider.connection.requestAirdrop( - anotherProgram.provider.wallet.publicKey, - anchor.web3.LAMPORTS_PER_SOL - ); - await program.provider.connection.confirmTransaction(signature); - // Create all the accounts. - await Promise.all([ - program.rpc.testFetchAll(filterable1, { - accounts: { - data: data1.publicKey, - authority: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data1], - }), - program.rpc.testFetchAll(filterable1, { - accounts: { - data: data2.publicKey, - authority: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data2], - }), - program.rpc.testFetchAll(filterable2, { - accounts: { - data: data3.publicKey, - authority: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data3], - }), - anotherProgram.rpc.testFetchAll(filterable1, { - accounts: { - data: data4.publicKey, - authority: anotherProgram.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - signers: [data4], - }), - ]); - // Call for multiple kinds of .all. - const allAccounts = await program.account.dataWithFilter.all(); - const allAccountsFilteredByBuffer = - await program.account.dataWithFilter.all( - program.provider.wallet.publicKey.toBuffer() - ); - const allAccountsFilteredByProgramFilters1 = - await program.account.dataWithFilter.all([ - { - memcmp: { - offset: 8, - bytes: program.provider.wallet.publicKey.toBase58(), - }, - }, - { memcmp: { offset: 40, bytes: filterable1.toBase58() } }, - ]); - const allAccountsFilteredByProgramFilters2 = - await program.account.dataWithFilter.all([ - { - memcmp: { - offset: 8, - bytes: program.provider.wallet.publicKey.toBase58(), - }, - }, - { memcmp: { offset: 40, bytes: filterable2.toBase58() } }, - ]); - // Without filters there should be 4 accounts. - assert.lengthOf(allAccounts, 4); - // Filtering by main wallet there should be 3 accounts. - assert.lengthOf(allAccountsFilteredByBuffer, 3); - // Filtering all the main wallet accounts and matching the filterable1 value - // results in a 2 accounts. - assert.lengthOf(allAccountsFilteredByProgramFilters1, 2); - // Filtering all the main wallet accounts and matching the filterable2 value - // results in 1 account. - assert.lengthOf(allAccountsFilteredByProgramFilters2, 1); - }); - - it("Can use pdas with empty seeds", async () => { - const [pda, bump] = await PublicKey.findProgramAddress( - [], - program.programId - ); - - await program.rpc.testInitWithEmptySeeds({ - accounts: { - pda: pda, - authority: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - }); - await program.rpc.testEmptySeedsConstraint({ - accounts: { - pda: pda, - }, - }); - - const [pda2, bump2] = await PublicKey.findProgramAddress( - ["non-empty"], - program.programId - ); - await nativeAssert.rejects( - program.rpc.testEmptySeedsConstraint({ - accounts: { - pda: pda2, - }, - }), - (err) => { - assert.equal(err.error.errorCode.number, 2006); - return true; - } - ); - }); - - const ifNeededAcc = anchor.web3.Keypair.generate(); - - it("Can init if needed a new account", async () => { - await program.rpc.testInitIfNeeded(1, { - accounts: { - data: ifNeededAcc.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - payer: program.provider.wallet.publicKey, - }, - signers: [ifNeededAcc], - }); - const account = await program.account.dataU16.fetch(ifNeededAcc.publicKey); - assert.strictEqual(account.data, 1); - }); - - it("Can init if needed a previously created account", async () => { - await program.rpc.testInitIfNeeded(3, { - accounts: { - data: ifNeededAcc.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - payer: program.provider.wallet.publicKey, - }, - signers: [ifNeededAcc], - }); - const account = await program.account.dataU16.fetch(ifNeededAcc.publicKey); - assert.strictEqual(account.data, 3); - }); - - it("Can use const for array size", async () => { - const data = anchor.web3.Keypair.generate(); - const tx = await program.rpc.testConstArraySize(99, { - accounts: { - data: data.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [data], - instructions: [ - await program.account.dataConstArraySize.createInstruction(data), - ], - }); - const dataAccount = await program.account.dataConstArraySize.fetch( - data.publicKey - ); - assert.deepStrictEqual(dataAccount.data, [99, ...new Array(9).fill(0)]); - }); - - it("Can use const for instruction data size", async () => { - const data = anchor.web3.Keypair.generate(); - const dataArray = [99, ...new Array(9).fill(0)]; - const tx = await program.rpc.testConstIxDataSize(dataArray, { - accounts: { - data: data.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [data], - instructions: [ - await program.account.dataConstArraySize.createInstruction(data), - ], - }); - const dataAccount = await program.account.dataConstArraySize.fetch( - data.publicKey - ); - assert.deepStrictEqual(dataAccount.data, dataArray); - }); - - it("Should include BASE const in IDL", async () => { - assert.isDefined( - miscIdl.constants.find( - (c) => c.name === "BASE" && c.type === "u128" && c.value === "1_000_000" - ) - ); - }); - - it("Should include DECIMALS const in IDL", async () => { - assert.isDefined( - miscIdl.constants.find( - (c) => c.name === "DECIMALS" && c.type === "u8" && c.value === "6" - ) - ); - }); - - it("Should not include NO_IDL const in IDL", async () => { - assert.isUndefined(miscIdl.constants.find((c) => c.name === "NO_IDL")); - }); - - it("init_if_needed throws if account exists but is not owned by the expected program", async () => { - const newAcc = await anchor.web3.PublicKey.findProgramAddress( - [utf8.encode("hello")], - program.programId - ); - await program.rpc.testInitIfNeededChecksOwner({ - accounts: { - data: newAcc[0], - systemProgram: anchor.web3.SystemProgram.programId, - payer: program.provider.wallet.publicKey, - owner: program.programId, - }, - }); - - try { - await program.rpc.testInitIfNeededChecksOwner({ - accounts: { - data: newAcc[0], - systemProgram: anchor.web3.SystemProgram.programId, - payer: program.provider.wallet.publicKey, - owner: anchor.web3.Keypair.generate().publicKey, - }, - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2004); - } - }); - - it("init_if_needed throws if pda account exists but does not have the expected seeds", async () => { - const newAcc = await anchor.web3.PublicKey.findProgramAddress( - [utf8.encode("nothello")], - program.programId - ); - await program.rpc.testInitIfNeededChecksSeeds("nothello", { - accounts: { - data: newAcc[0], - systemProgram: anchor.web3.SystemProgram.programId, - payer: program.provider.wallet.publicKey, - }, - }); - - // this will throw if it is not a proper PDA - // we need this so we know that the following tx failed - // not because it couldn't create this pda - // but because the two pdas were different - anchor.web3.PublicKey.createProgramAddress( - [utf8.encode("hello")], - program.programId - ); - - try { - await program.rpc.testInitIfNeededChecksSeeds("hello", { - accounts: { - data: newAcc[0], - systemProgram: anchor.web3.SystemProgram.programId, - payer: program.provider.wallet.publicKey, - owner: anchor.web3.Keypair.generate().publicKey, - }, - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2006); - } - }); - - it("init_if_needed throws if account exists but is not the expected space", async () => { - const newAcc = anchor.web3.Keypair.generate(); - const _irrelevantForTest = 3; - await program.rpc.initWithSpace(_irrelevantForTest, { - accounts: { - data: newAcc.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - payer: program.provider.wallet.publicKey, - }, - signers: [newAcc], - }); - - try { - await program.rpc.testInitIfNeeded(_irrelevantForTest, { - accounts: { - data: newAcc.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - payer: program.provider.wallet.publicKey, - }, - signers: [newAcc], - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2019); - } - }); - - it("init_if_needed throws if mint exists but has the wrong mint authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - try { - await program.rpc.testInitMintIfNeeded(6, { - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - mintAuthority: anchor.web3.Keypair.generate().publicKey, - freezeAuthority: program.provider.wallet.publicKey, - }, - signers: [mint], - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2016); - } - }); - - it("init_if_needed throws if mint exists but has the wrong freeze authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - try { - await program.rpc.testInitMintIfNeeded(6, { - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - mintAuthority: program.provider.wallet.publicKey, - freezeAuthority: anchor.web3.Keypair.generate().publicKey, - }, - signers: [mint], - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2017); - } - }); - - it("init_if_needed throws if mint exists but has the wrong decimals", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - try { - await program.rpc.testInitMintIfNeeded(9, { - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - mintAuthority: program.provider.wallet.publicKey, - freezeAuthority: program.provider.wallet.publicKey, - }, - signers: [mint], - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2018); - } - }); - - it("init_if_needed throws if token exists but has the wrong owner", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - - try { - await program.rpc.testInitTokenIfNeeded({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - authority: anchor.web3.Keypair.generate().publicKey, - }, - signers: [token], - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2015); - } - }); - - it("init_if_needed throws if token exists but has the wrong mint", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const mint2 = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint2.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint2], - }); - - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - - try { - await program.rpc.testInitTokenIfNeeded({ - accounts: { - token: token.publicKey, - mint: mint2.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - authority: program.provider.wallet.publicKey, - }, - signers: [token], - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2014); - } - }); - - it("init_if_needed throws if associated token exists but has the wrong owner", async () => { - const mint = Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const associatedToken = await Token.getAssociatedTokenAddress( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - mint.publicKey, - program.provider.wallet.publicKey - ); - - await program.rpc.testInitAssociatedToken({ - accounts: { - token: associatedToken, - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - }, - }); - - try { - await program.rpc.testInitAssociatedTokenIfNeeded({ - accounts: { - token: associatedToken, - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - authority: anchor.web3.Keypair.generate().publicKey, - }, - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2015); - } - }); - - it("init_if_needed throws if associated token exists but has the wrong mint", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const mint2 = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint2.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint2], - }); - - const associatedToken = await Token.getAssociatedTokenAddress( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - mint.publicKey, - program.provider.wallet.publicKey - ); - - await program.rpc.testInitAssociatedToken({ - accounts: { - token: associatedToken, - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - }, - }); - - try { - await program.rpc.testInitAssociatedTokenIfNeeded({ - accounts: { - token: associatedToken, - mint: mint2.publicKey, - payer: program.provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - authority: program.provider.wallet.publicKey, - }, - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2014); - } - }); - - it("init_if_needed throws if token exists with correct owner and mint but is not the ATA", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const associatedToken = await Token.getAssociatedTokenAddress( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - mint.publicKey, - program.provider.wallet.publicKey - ); - - await program.rpc.testInitAssociatedToken({ - accounts: { - token: associatedToken, - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - }, - }); - - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - - try { - await program.rpc.testInitAssociatedTokenIfNeeded({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - associatedTokenProgram: ASSOCIATED_TOKEN_PROGRAM_ID, - authority: program.provider.wallet.publicKey, - }, - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 3014); - } - }); - - it("Can use multidimensional array", async () => { - const array2d = new Array(10).fill(new Array(10).fill(99)); - const data = anchor.web3.Keypair.generate(); - await program.rpc.testMultidimensionalArray(array2d, { - accounts: { - data: data.publicKey, - }, - signers: [data], - instructions: [ - await program.account.dataMultidimensionalArray.createInstruction(data), - ], - }); - const dataAccount = await program.account.dataMultidimensionalArray.fetch( - data.publicKey - ); - assert.deepStrictEqual(dataAccount.data, array2d); - }); - - it("Can use multidimensional array with const sizes", async () => { - const array2d = new Array(10).fill(new Array(11).fill(22)); - const data = anchor.web3.Keypair.generate(); - await program.rpc.testMultidimensionalArrayConstSizes(array2d, { - accounts: { - data: data.publicKey, - }, - signers: [data], - instructions: [ - await program.account.dataMultidimensionalArrayConstSizes.createInstruction( - data - ), - ], - }); - const dataAccount = - await program.account.dataMultidimensionalArrayConstSizes.fetch( - data.publicKey - ); - assert.deepStrictEqual(dataAccount.data, array2d); - }); - - describe("Can validate PDAs derived from other program ids", () => { - it("With bumps using create_program_address", async () => { - const [firstPDA, firstBump] = - await anchor.web3.PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("seed")], - ASSOCIATED_TOKEN_PROGRAM_ID - ); - const [secondPDA, secondBump] = - await anchor.web3.PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("seed")], - program.programId - ); - - // correct bump but wrong address - const wrongAddress = anchor.web3.Keypair.generate().publicKey; - try { - await program.rpc.testProgramIdConstraint(firstBump, secondBump, { - accounts: { - first: wrongAddress, - second: secondPDA, - }, - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2006); - } - - // matching bump seed for wrong address but derived from wrong program - try { - await program.rpc.testProgramIdConstraint(secondBump, secondBump, { - accounts: { - first: secondPDA, - second: secondPDA, - }, - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2006); - } - - // correct inputs should lead to successful tx - await program.rpc.testProgramIdConstraint(firstBump, secondBump, { - accounts: { - first: firstPDA, - second: secondPDA, - }, - }); - }); - - it("With bumps using find_program_address", async () => { - const firstPDA = ( - await anchor.web3.PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("seed")], - ASSOCIATED_TOKEN_PROGRAM_ID - ) - )[0]; - const secondPDA = ( - await anchor.web3.PublicKey.findProgramAddress( - [anchor.utils.bytes.utf8.encode("seed")], - program.programId - ) - )[0]; - - // random wrong address - const wrongAddress = anchor.web3.Keypair.generate().publicKey; - try { - await program.rpc.testProgramIdConstraintFindPda({ - accounts: { - first: wrongAddress, - second: secondPDA, - }, - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2006); - } - - // same seeds but derived from wrong program - try { - await program.rpc.testProgramIdConstraintFindPda({ - accounts: { - first: secondPDA, - second: secondPDA, - }, - }); - expect(false).to.be.true; - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2006); - } - - // correct inputs should lead to successful tx - await program.rpc.testProgramIdConstraintFindPda({ - accounts: { - first: firstPDA, - second: secondPDA, - }, - }); - }); - }); - describe("Token Constraint Test", () => { - it("Token Constraint Test(no init) - Can make token::mint and token::authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - await program.rpc.testTokenConstraint({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - }, - }); - const mintAccount = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - program.provider.wallet.payer - ); - const account = await mintAccount.getAccountInfo(token.publicKey); - assert.isTrue(account.owner.equals(program.provider.wallet.publicKey)); - assert.isTrue(account.mint.equals(mint.publicKey)); - }); - - it("Token Constraint Test(no init) - Can make only token::authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - await program.rpc.testOnlyAuthConstraint({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - }, - }); - const mintAccount = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - program.provider.wallet.payer - ); - const account = await mintAccount.getAccountInfo(token.publicKey); - assert.isTrue(account.owner.equals(program.provider.wallet.publicKey)); - }); - - it("Token Constraint Test(no init) - Can make only token::mint", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - await program.rpc.testOnlyMintConstraint({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - }, - }); - const mintAccount = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - program.provider.wallet.payer - ); - const account = await mintAccount.getAccountInfo(token.publicKey); - assert.isTrue(account.mint.equals(mint.publicKey)); - }); - - it("Token Constraint Test(no init) - throws if token::mint mismatch", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const mint1 = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint1.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint1], - }); - - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - try { - await program.rpc.testTokenConstraint({ - accounts: { - token: token.publicKey, - mint: mint1.publicKey, - payer: program.provider.wallet.publicKey, - }, - }); - assert.isTrue(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2014); - assert.strictEqual(err.error.errorCode.code, "ConstraintTokenMint"); - } - }); - - it("Token Constraint Test(no init) - throws if token::authority mismatch", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - const fakeAuthority = Keypair.generate(); - try { - await program.rpc.testTokenAuthConstraint({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - fakeAuthority: fakeAuthority.publicKey, - }, - }); - assert.isTrue(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2015); - assert.strictEqual(err.error.errorCode.code, "ConstraintTokenOwner"); - } - }); - - it("Token Constraint Test(no init) - throws if both token::authority, token::mint mismatch", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - const mint1 = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint1.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint1], - }); - const token = anchor.web3.Keypair.generate(); - await program.rpc.testInitToken({ - accounts: { - token: token.publicKey, - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [token], - }); - const fakeAuthority = Keypair.generate(); - try { - await program.rpc.testTokenAuthConstraint({ - accounts: { - token: token.publicKey, - mint: mint1.publicKey, - fakeAuthority: fakeAuthority.publicKey, - }, - }); - assert.isTrue(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2015); - assert.strictEqual(err.error.errorCode.code, "ConstraintTokenOwner"); - } - }); - - it("Mint Constraint Test(no init) - mint::decimals, mint::authority, mint::freeze_authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - await program.rpc.testMintConstraint(6, { - accounts: { - mint: mint.publicKey, - mintAuthority: program.provider.wallet.publicKey, - freezeAuthority: program.provider.wallet.publicKey, - }, - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - program.provider.wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.strictEqual(mintAccount.decimals, 6); - assert.isTrue( - mintAccount.mintAuthority.equals(program.provider.wallet.publicKey) - ); - assert.isTrue( - mintAccount.freezeAuthority.equals(program.provider.wallet.publicKey) - ); - }); - - it("Mint Constraint Test(no init) - throws if mint::decimals mismatch", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - const fakeDecimal = 5; - try { - await program.rpc.testMintConstraint(fakeDecimal, { - accounts: { - mint: mint.publicKey, - mintAuthority: program.provider.wallet.publicKey, - freezeAuthority: program.provider.wallet.publicKey, - }, - }); - assert.isTrue(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2018); - assert.strictEqual(err.error.errorCode.code, "ConstraintMintDecimals"); - } - }); - - it("Mint Constraint Test(no init) - throws if mint::mint_authority mismatch", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const fakeAuthority = Keypair.generate(); - try { - await program.rpc.testMintConstraint(6, { - accounts: { - mint: mint.publicKey, - mintAuthority: fakeAuthority.publicKey, - freezeAuthority: program.provider.wallet.publicKey, - }, - }); - assert.isTrue(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2016); - assert.strictEqual( - err.error.errorCode.code, - "ConstraintMintMintAuthority" - ); - } - }); - - it("Mint Constraint Test(no init) - throws if mint::freeze_authority mismatch", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - const fakeAuthority = Keypair.generate(); - try { - await program.rpc.testMintConstraint(6, { - accounts: { - mint: mint.publicKey, - mintAuthority: program.provider.wallet.publicKey, - freezeAuthority: fakeAuthority.publicKey, - }, - }); - assert.isTrue(false); - } catch (_err) { - assert.isTrue(_err instanceof AnchorError); - const err: AnchorError = _err; - assert.strictEqual(err.error.errorCode.number, 2017); - assert.strictEqual( - err.error.errorCode.code, - "ConstraintMintFreezeAuthority" - ); - } - }); - - it("Mint Constraint Test(no init) - can write only mint::decimals", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - await program.rpc.testMintOnlyDecimalsConstraint(6, { - accounts: { - mint: mint.publicKey, - }, - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - program.provider.wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.strictEqual(mintAccount.decimals, 6); - }); - - it("Mint Constraint Test(no init) - can write only mint::authority and mint::freeze_authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - await program.rpc.testMintOnlyAuthConstraint({ - accounts: { - mint: mint.publicKey, - mintAuthority: program.provider.wallet.publicKey, - freezeAuthority: program.provider.wallet.publicKey, - }, - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - program.provider.wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.isTrue( - mintAccount.mintAuthority.equals(program.provider.wallet.publicKey) - ); - assert.isTrue( - mintAccount.freezeAuthority.equals(program.provider.wallet.publicKey) - ); - }); - - it("Mint Constraint Test(no init) - can write only mint::authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - await program.rpc.testMintOnlyOneAuthConstraint({ - accounts: { - mint: mint.publicKey, - mintAuthority: program.provider.wallet.publicKey, - }, - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - program.provider.wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.isTrue( - mintAccount.mintAuthority.equals(program.provider.wallet.publicKey) - ); - }); - - it("Mint Constraint Test(no init) - can write only mint::decimals and mint::freeze_authority", async () => { - const mint = anchor.web3.Keypair.generate(); - await program.rpc.testInitMint({ - accounts: { - mint: mint.publicKey, - payer: program.provider.wallet.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [mint], - }); - - await program.rpc.testMintMissMintAuthConstraint(6, { - accounts: { - mint: mint.publicKey, - freezeAuthority: program.provider.wallet.publicKey, - }, - }); - const client = new Token( - program.provider.connection, - mint.publicKey, - TOKEN_PROGRAM_ID, - program.provider.wallet.payer - ); - const mintAccount = await client.getMintInfo(); - assert.strictEqual(mintAccount.decimals, 6); - assert.isTrue( - mintAccount.freezeAuthority.equals(program.provider.wallet.publicKey) - ); - }); - }); -}); diff --git a/tests/misc/tsconfig.json b/tests/misc/tsconfig.json deleted file mode 100644 index ab7d0c1b..00000000 --- a/tests/misc/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "types": ["mocha", "chai"], - "typeRoots": ["./node_modules/@types"], - "lib": ["es2015"], - "module": "commonjs", - "target": "es6", - "esModuleInterop": true - } -} diff --git a/tests/multiple-suites/Anchor.toml b/tests/multiple-suites/Anchor.toml deleted file mode 100644 index 33efd71c..00000000 --- a/tests/multiple-suites/Anchor.toml +++ /dev/null @@ -1,18 +0,0 @@ -[features] -seeds = false -[programs.localnet] -multiple_suites = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[registry] -url = "https://anchor.projectserum.com" - -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[test] -startup_wait = 20000 - -[[test.validator.account]] -address = "C4XeBpzX4tDjGV1gkLsj7jJh6XHunVqAykANWCfTLszw" -filename = "./tests/accounts/SOME_ACCOUNT.json" diff --git a/tests/multiple-suites/Cargo.toml b/tests/multiple-suites/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/multiple-suites/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/multiple-suites/migrations/deploy.ts b/tests/multiple-suites/migrations/deploy.ts deleted file mode 100644 index 5e3df0dc..00000000 --- a/tests/multiple-suites/migrations/deploy.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Migrations are an early feature. Currently, they're nothing more than this -// single deploy script that's invoked from the CLI, injecting a provider -// configured from the workspace's Anchor.toml. - -const anchor = require("@project-serum/anchor"); - -module.exports = async function (provider) { - // Configure client to use the provider. - anchor.setProvider(provider); - - // Add your deploy script here. -}; diff --git a/tests/multiple-suites/package.json b/tests/multiple-suites/package.json deleted file mode 100644 index 524dfab0..00000000 --- a/tests/multiple-suites/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "multiple-suites", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } - } diff --git a/tests/multiple-suites/programs/multiple-suites/Cargo.toml b/tests/multiple-suites/programs/multiple-suites/Cargo.toml deleted file mode 100644 index e4f74bb3..00000000 --- a/tests/multiple-suites/programs/multiple-suites/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "multiple-suites" -version = "0.1.0" -description = "Created with Anchor" -edition = "2018" - -[lib] -crate-type = ["cdylib", "lib"] -name = "multiple_suites" - -[features] -no-entrypoint = [] -no-idl = [] -no-log-ix-name = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = "0.24.2" diff --git a/tests/multiple-suites/programs/multiple-suites/Xargo.toml b/tests/multiple-suites/programs/multiple-suites/Xargo.toml deleted file mode 100644 index 475fb71e..00000000 --- a/tests/multiple-suites/programs/multiple-suites/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] diff --git a/tests/multiple-suites/programs/multiple-suites/src/lib.rs b/tests/multiple-suites/programs/multiple-suites/src/lib.rs deleted file mode 100644 index 74945c1f..00000000 --- a/tests/multiple-suites/programs/multiple-suites/src/lib.rs +++ /dev/null @@ -1,16 +0,0 @@ -use anchor_lang::prelude::*; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -pub mod multiple_suites { - use super::*; - - // _val to ensure tx are different so they don't get rejected. - pub fn initialize(_ctx: Context, _val: u64) -> Result<()> { - Ok(()) - } -} - -#[derive(Accounts)] -pub struct Initialize {} diff --git a/tests/multiple-suites/tests/Test.base.toml b/tests/multiple-suites/tests/Test.base.toml deleted file mode 100644 index d158a699..00000000 --- a/tests/multiple-suites/tests/Test.base.toml +++ /dev/null @@ -1,5 +0,0 @@ -extends = ["./Test.root.base.toml"] - -[[test.validator.account]] -address = "C4XeBpzX4tDjGV1gkLsj7jJh6XHunVqAykANWCfTLszw" -filename = "./accounts/SOME_TOKEN.json" diff --git a/tests/multiple-suites/tests/Test.root.base.toml b/tests/multiple-suites/tests/Test.root.base.toml deleted file mode 100644 index 08b4709a..00000000 --- a/tests/multiple-suites/tests/Test.root.base.toml +++ /dev/null @@ -1,12 +0,0 @@ -[test] -startup_wait = 20000 - -[test.validator] -url = "https://api.mainnet-beta.solana.com" - -[[test.validator.clone]] -address = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" - -[[test.validator.account]] -address = "JC7Vcye5upE6tMLAjAem76MCGuPNidTtg2cuYm71UukH" -filename = "./accounts/ANOTHER_ACC.json" diff --git a/tests/multiple-suites/tests/accounts/ANOTHER_ACC.json b/tests/multiple-suites/tests/accounts/ANOTHER_ACC.json deleted file mode 100644 index 08a7258c..00000000 --- a/tests/multiple-suites/tests/accounts/ANOTHER_ACC.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "pubkey": "JC7Vcye5upE6tMLAjAem76MCGuPNidTtg2cuYm71UukH", - "account": { - "lamports": 1141440, - "data": [ - "AgAAALd5VermN3GAImByma8xRkcfPsbJkljqjdkZwDg8igZy", - "base64" - ], - "owner": "BPFLoaderUpgradeab1e11111111111111111111111", - "executable": true, - "rentEpoch": 211 - } -} \ No newline at end of file diff --git a/tests/multiple-suites/tests/accounts/SOME_ACCOUNT.json b/tests/multiple-suites/tests/accounts/SOME_ACCOUNT.json deleted file mode 100644 index 65c414cb..00000000 --- a/tests/multiple-suites/tests/accounts/SOME_ACCOUNT.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "pubkey": "3vMPj13emX9JmifYcWc77ekEzV1F37ga36E1YeSr6Mdj", - "account": { - "lamports": 7906560, - "data": [ - "oZzT/fpANfoAAAacjYHHz87iGrP2++EgvfsQjuGvj1dqAHoCKU9FPDLg9bWjnUCkIZgQQSmJpClN5jbVq6JbdM4Ec4ZpEb2ViTzO//sSNXmZhK5OA5kiKWvPWoUsXExQpNgiaEeW8MWANStiNrxKs/p6yWv9cqbJBd6AygqKx0Y7WysO04FMaSFa/6o5Lt/NyPH8jkM12IYJcvGzIQIbaXRo9eDbJOcPskyugDoJAAAAAAAAAAAAAAAAAAAAAAAAAAAAFQMAAAAAAAB4AQAAAAAAAHIDAAAAAAAAcgMAAAAAAAABAAAAAAAAAAjqplnVZa4ZML82vxalcFuNRlmUiSpI+ZGRVoMP+eF1hDOHYqnPMZXS8rUwGGcq0p98MYBGv4V+9NsD84s31F0BRTMAAAAAAAAAAACAOgkAAAAAAPySPwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB8Cd4FAAAAAL4E7wIAAAAA6pstAQAAAAAAAR0AAAAAALHFQ2IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAabiFf+q4GE+2h/Y0YYwDXaxDncGus7VZig8AAAAAABb8MVpWsIyg7EhPN8Vhh8GnNx3+zvD0Kz0wqIECi79vwBAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACwBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", - "base64" - ], - "owner": "7NryUmAmy8hMbawRNUs9UdkgAGEiHFxghSfve31dC7xh", - "executable": false, - "rentEpoch": 292 - } -} \ No newline at end of file diff --git a/tests/multiple-suites/tests/accounts/SOME_TOKEN.json b/tests/multiple-suites/tests/accounts/SOME_TOKEN.json deleted file mode 100644 index ea50b371..00000000 --- a/tests/multiple-suites/tests/accounts/SOME_TOKEN.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "pubkey": "C4XeBpzX4tDjGV1gkLsj7jJh6XHunVqAykANWCfTLszw", - "account": { - "lamports": 839123197950, - "data": [ - "AAAAAFnkpzMo+KIHXFu0C7POimfWZAwz81Y+ImohwO+lC39o2tFLxYmAIwAGAQAAAABZ5KczKPiiB1xbtAuzzopn1mQMM/NWPiJqIcDvpQt/aA==", - "base64" - ], - "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", - "executable": false, - "rentEpoch": 292 - } -} diff --git a/tests/multiple-suites/tests/another-suite/Test.toml b/tests/multiple-suites/tests/another-suite/Test.toml deleted file mode 100644 index 8404090b..00000000 --- a/tests/multiple-suites/tests/another-suite/Test.toml +++ /dev/null @@ -1,4 +0,0 @@ -extends = ["../Test.base.toml"] - -[scripts] -test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/another-suite/**/*.ts" diff --git a/tests/multiple-suites/tests/another-suite/another-suite.ts b/tests/multiple-suites/tests/another-suite/another-suite.ts deleted file mode 100644 index 0ab19987..00000000 --- a/tests/multiple-suites/tests/another-suite/another-suite.ts +++ /dev/null @@ -1,44 +0,0 @@ -import * as anchor from "@project-serum/anchor"; -import { Program } from "@project-serum/anchor"; -import { PublicKey } from "@solana/web3.js"; -import { assert } from "chai"; -import { MultipleSuites } from "../../target/types/multiple_suites"; - -describe("multiple-suites", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.env()); - - const program = anchor.workspace.MultipleSuites as Program; - - it("Is initialized!", async () => { - // Add your test here. - const tx = await program.rpc.initialize(new anchor.BN(100000), {}); - - // SOME_TOKEN.json should exist. - const SOME_TOKEN = await program.provider.connection.getAccountInfo( - new PublicKey("C4XeBpzX4tDjGV1gkLsj7jJh6XHunVqAykANWCfTLszw") - ); - - // SOME_ACCOUNT.json should NOT exist. - const SOME_ACCOUNT = await program.provider.connection.getAccountInfo( - new PublicKey("3vMPj13emX9JmifYcWc77ekEzV1F37ga36E1YeSr6Mdj") - ); - - // ANOTHER_ACC.json should exist. - const ANOTHER_ACC = await program.provider.connection.getAccountInfo( - new PublicKey("JC7Vcye5upE6tMLAjAem76MCGuPNidTtg2cuYm71UukH") - ); - - // CLONED ACC should exist. - const CLONED_ACC = await program.provider.connection.getAccountInfo( - new PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s") - ); - - assert.isNotNull(SOME_TOKEN); - assert.isNull(SOME_ACCOUNT); - assert.isNotNull(ANOTHER_ACC); - assert.isNotNull(CLONED_ACC); - - console.log("Your transaction signature", tx); - }); -}); diff --git a/tests/multiple-suites/tests/fourth-and-fifth-suite/fifth-suite/Test.toml b/tests/multiple-suites/tests/fourth-and-fifth-suite/fifth-suite/Test.toml deleted file mode 100644 index ff688551..00000000 --- a/tests/multiple-suites/tests/fourth-and-fifth-suite/fifth-suite/Test.toml +++ /dev/null @@ -1,4 +0,0 @@ -extends = ["../../Test.base.toml"] - -[scripts] -test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/fourth-and-fifth-suite/fifth-suite/**/*.ts" diff --git a/tests/multiple-suites/tests/fourth-and-fifth-suite/fifth-suite/fifthSuite.ts b/tests/multiple-suites/tests/fourth-and-fifth-suite/fifth-suite/fifthSuite.ts deleted file mode 100644 index 43e781a5..00000000 --- a/tests/multiple-suites/tests/fourth-and-fifth-suite/fifth-suite/fifthSuite.ts +++ /dev/null @@ -1,43 +0,0 @@ -import * as anchor from "@project-serum/anchor"; -import { Program } from "@project-serum/anchor"; -import { PublicKey } from "@solana/web3.js"; -import { assert } from "chai"; -import { MultipleSuites } from "../../../target/types/multiple_suites"; - -describe("multiple-suites", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.env()); - - const program = anchor.workspace.MultipleSuites as Program; - - it("Is initialized!", async () => { - // Add your test here. - const tx = await program.rpc.initialize(new anchor.BN(4389242), {}); - - // SOME_TOKEN.json should exist. - const SOME_TOKEN = await program.provider.connection.getAccountInfo( - new PublicKey("C4XeBpzX4tDjGV1gkLsj7jJh6XHunVqAykANWCfTLszw") - ); - - // SOME_ACCOUNT.json should NOT exist. - const SOME_ACCOUNT = await program.provider.connection.getAccountInfo( - new PublicKey("3vMPj13emX9JmifYcWc77ekEzV1F37ga36E1YeSr6Mdj") - ); - - // ANOTHER_ACC.json should exist. - const ANOTHER_ACC = await program.provider.connection.getAccountInfo( - new PublicKey("JC7Vcye5upE6tMLAjAem76MCGuPNidTtg2cuYm71UukH") - ); - - // CLONED ACC should exist. - const CLONED_ACC = await program.provider.connection.getAccountInfo( - new PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s") - ); - - assert.isNotNull(SOME_TOKEN); - assert.isNull(SOME_ACCOUNT); - assert.isNotNull(ANOTHER_ACC); - assert.isNotNull(CLONED_ACC); - console.log("Your transaction signature", tx); - }); -}); diff --git a/tests/multiple-suites/tests/fourth-and-fifth-suite/forth-suite/Test.toml b/tests/multiple-suites/tests/fourth-and-fifth-suite/forth-suite/Test.toml deleted file mode 100644 index 5d8ffe24..00000000 --- a/tests/multiple-suites/tests/fourth-and-fifth-suite/forth-suite/Test.toml +++ /dev/null @@ -1,4 +0,0 @@ -extends = ["../../Test.base.toml"] - -[scripts] -test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/fourth-and-fifth-suite/forth-suite/**/*.ts" diff --git a/tests/multiple-suites/tests/fourth-and-fifth-suite/forth-suite/forth-suite.ts b/tests/multiple-suites/tests/fourth-and-fifth-suite/forth-suite/forth-suite.ts deleted file mode 100644 index 5f14aebe..00000000 --- a/tests/multiple-suites/tests/fourth-and-fifth-suite/forth-suite/forth-suite.ts +++ /dev/null @@ -1,43 +0,0 @@ -import * as anchor from "@project-serum/anchor"; -import { Program } from "@project-serum/anchor"; -import { PublicKey } from "@solana/web3.js"; -import { assert } from "chai"; -import { MultipleSuites } from "../../../target/types/multiple_suites"; - -describe("multiple-suites", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.env()); - - const program = anchor.workspace.MultipleSuites as Program; - - it("Is initialized!", async () => { - // Add your test here. - const tx = await program.rpc.initialize(new anchor.BN(34823), {}); - - // SOME_TOKEN.json should exist. - const SOME_TOKEN = await program.provider.connection.getAccountInfo( - new PublicKey("C4XeBpzX4tDjGV1gkLsj7jJh6XHunVqAykANWCfTLszw") - ); - - // SOME_ACCOUNT.json should NOT exist. - const SOME_ACCOUNT = await program.provider.connection.getAccountInfo( - new PublicKey("3vMPj13emX9JmifYcWc77ekEzV1F37ga36E1YeSr6Mdj") - ); - - // ANOTHER_ACC.json should exist. - const ANOTHER_ACC = await program.provider.connection.getAccountInfo( - new PublicKey("JC7Vcye5upE6tMLAjAem76MCGuPNidTtg2cuYm71UukH") - ); - - // CLONED ACC should exist. - const CLONED_ACC = await program.provider.connection.getAccountInfo( - new PublicKey("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s") - ); - - assert.isNotNull(SOME_TOKEN); - assert.isNull(SOME_ACCOUNT); - assert.isNotNull(ANOTHER_ACC); - assert.isNotNull(CLONED_ACC); - console.log("Your transaction signature", tx); - }); -}); diff --git a/tests/multiple-suites/tests/multiple-suites/Test.toml b/tests/multiple-suites/tests/multiple-suites/Test.toml deleted file mode 100644 index a9c631ae..00000000 --- a/tests/multiple-suites/tests/multiple-suites/Test.toml +++ /dev/null @@ -1,4 +0,0 @@ -extends = ["../../Anchor.toml"] - -[scripts] -test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/multiple-suites/**/*.ts" diff --git a/tests/multiple-suites/tests/multiple-suites/multiple-suites.ts b/tests/multiple-suites/tests/multiple-suites/multiple-suites.ts deleted file mode 100644 index 3cdf84ed..00000000 --- a/tests/multiple-suites/tests/multiple-suites/multiple-suites.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as anchor from "@project-serum/anchor"; -import { Program } from "@project-serum/anchor"; -import { PublicKey } from "@solana/web3.js"; -import { assert } from "chai"; -import { MultipleSuites } from "../../target/types/multiple_suites"; - -describe("multiple-suites", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.env()); - - const program = anchor.workspace.MultipleSuites as Program; - - it("Is initialized!", async () => { - // Add your test here. - const tx = await program.rpc.initialize(new anchor.BN(2394832), {}); - - // SOME_TOKEN.json should NOT exist. - const SOME_TOKEN = await program.provider.connection.getAccountInfo( - new PublicKey("C4XeBpzX4tDjGV1gkLsj7jJh6XHunVqAykANWCfTLszw") - ); - - // SOME_ACCOUNT.json should exist. - const SOME_ACCOUNT = await program.provider.connection.getAccountInfo( - new PublicKey("3vMPj13emX9JmifYcWc77ekEzV1F37ga36E1YeSr6Mdj") - ); - - assert.isNull(SOME_TOKEN); - assert.isNotNull(SOME_ACCOUNT); - - console.log("Your transaction signature", tx); - }); -}); diff --git a/tests/multiple-suites/tests/third-suite/Test.toml b/tests/multiple-suites/tests/third-suite/Test.toml deleted file mode 100644 index ec873aa5..00000000 --- a/tests/multiple-suites/tests/third-suite/Test.toml +++ /dev/null @@ -1,4 +0,0 @@ -extends = ["../Test.base.toml"] - -[scripts] -test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/third-suite/**/*.ts" diff --git a/tests/multiple-suites/tests/third-suite/sub-dir-one/subDirOne.ts b/tests/multiple-suites/tests/third-suite/sub-dir-one/subDirOne.ts deleted file mode 100644 index 93cf1c0a..00000000 --- a/tests/multiple-suites/tests/third-suite/sub-dir-one/subDirOne.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as anchor from "@project-serum/anchor"; -import { Program } from "@project-serum/anchor"; -import { PublicKey } from "@solana/web3.js"; -import { MultipleSuites } from "../../../target/types/multiple_suites"; -import { assert } from "chai"; - -describe("multiple-suites", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.env()); - - const program = anchor.workspace.MultipleSuites as Program; - - it("Is initialized!", async () => { - // Add your test here. - const tx = await program.rpc.initialize(new anchor.BN(347234), {}); - - // SOME_TOKEN.json should exist. - const SOME_TOKEN = await program.provider.connection.getAccountInfo( - new PublicKey("C4XeBpzX4tDjGV1gkLsj7jJh6XHunVqAykANWCfTLszw") - ); - - // SOME_ACCOUNT.json should NOT exist. - const SOME_ACCOUNT = await program.provider.connection.getAccountInfo( - new PublicKey("3vMPj13emX9JmifYcWc77ekEzV1F37ga36E1YeSr6Mdj") - ); - - assert.isNotNull(SOME_TOKEN); - assert.isNull(SOME_ACCOUNT); - - console.log("Your transaction signature", tx); - }); -}); diff --git a/tests/multiple-suites/tests/third-suite/sub-dir-two/subDirTwo.ts b/tests/multiple-suites/tests/third-suite/sub-dir-two/subDirTwo.ts deleted file mode 100644 index 62be69fc..00000000 --- a/tests/multiple-suites/tests/third-suite/sub-dir-two/subDirTwo.ts +++ /dev/null @@ -1,32 +0,0 @@ -import * as anchor from "@project-serum/anchor"; -import { Program } from "@project-serum/anchor"; -import { PublicKey } from "@solana/web3.js"; -import { assert } from "chai"; -import { MultipleSuites } from "../../../target/types/multiple_suites"; - -describe("multiple-suites", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.env()); - - const program = anchor.workspace.MultipleSuites as Program; - - it("Is initialized!", async () => { - // Add your test here. - const tx = await program.rpc.initialize(new anchor.BN(9348239), {}); - - // SOME_TOKEN.json should exist. - const SOME_TOKEN = await program.provider.connection.getAccountInfo( - new PublicKey("C4XeBpzX4tDjGV1gkLsj7jJh6XHunVqAykANWCfTLszw") - ); - - // SOME_ACCOUNT.json should NOT exist. - const SOME_ACCOUNT = await program.provider.connection.getAccountInfo( - new PublicKey("3vMPj13emX9JmifYcWc77ekEzV1F37ga36E1YeSr6Mdj") - ); - - assert.isNotNull(SOME_TOKEN); - assert.isNull(SOME_ACCOUNT); - - console.log("Your transaction signature", tx); - }); -}); diff --git a/tests/multiple-suites/tsconfig.json b/tests/multiple-suites/tsconfig.json deleted file mode 100644 index cd5d2e3d..00000000 --- a/tests/multiple-suites/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "types": ["mocha", "chai"], - "typeRoots": ["./node_modules/@types"], - "lib": ["es2015"], - "module": "commonjs", - "target": "es6", - "esModuleInterop": true - } -} diff --git a/tests/multisig/Anchor.toml b/tests/multisig/Anchor.toml deleted file mode 100644 index ee9f941d..00000000 --- a/tests/multisig/Anchor.toml +++ /dev/null @@ -1,11 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -multisig = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[scripts] -test = "yarn run mocha -t 1000000 tests/" - -[features] diff --git a/tests/multisig/Cargo.toml b/tests/multisig/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/multisig/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/multisig/migrations/deploy.js b/tests/multisig/migrations/deploy.js deleted file mode 100644 index 5e3df0dc..00000000 --- a/tests/multisig/migrations/deploy.js +++ /dev/null @@ -1,12 +0,0 @@ -// Migrations are an early feature. Currently, they're nothing more than this -// single deploy script that's invoked from the CLI, injecting a provider -// configured from the workspace's Anchor.toml. - -const anchor = require("@project-serum/anchor"); - -module.exports = async function (provider) { - // Configure client to use the provider. - anchor.setProvider(provider); - - // Add your deploy script here. -}; diff --git a/tests/multisig/package.json b/tests/multisig/package.json deleted file mode 100644 index 6ec3429d..00000000 --- a/tests/multisig/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "multisig", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/multisig/programs/multisig/Cargo.toml b/tests/multisig/programs/multisig/Cargo.toml deleted file mode 100644 index 66157d8f..00000000 --- a/tests/multisig/programs/multisig/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "multisig" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "multisig" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } diff --git a/tests/multisig/programs/multisig/Xargo.toml b/tests/multisig/programs/multisig/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/multisig/programs/multisig/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/multisig/programs/multisig/src/lib.rs b/tests/multisig/programs/multisig/src/lib.rs deleted file mode 100644 index 91ac9ff7..00000000 --- a/tests/multisig/programs/multisig/src/lib.rs +++ /dev/null @@ -1,281 +0,0 @@ -//! An example of a multisig to execute arbitrary Solana transactions. -//! -//! This program can be used to allow a multisig to govern anything a regular -//! Pubkey can govern. One can use the multisig as a BPF program upgrade -//! authority, a mint authority, etc. -//! -//! To use, one must first create a `Multisig` account, specifying two important -//! parameters: -//! -//! 1. Owners - the set of addresses that sign transactions for the multisig. -//! 2. Threshold - the number of signers required to execute a transaction. -//! -//! Once the `Multisig` account is created, one can create a `Transaction` -//! account, specifying the parameters for a normal solana transaction. -//! -//! To sign, owners should invoke the `approve` instruction, and finally, -//! the `execute_transaction`, once enough (i.e. `threshold`) of the owners have -//! signed. - -use anchor_lang::accounts::program_account::ProgramAccount; -use anchor_lang::prelude::*; -use anchor_lang::solana_program; -use anchor_lang::solana_program::instruction::Instruction; -use std::convert::Into; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -pub mod multisig { - use super::*; - - // Initializes a new multisig account with a set of owners and a threshold. - pub fn create_multisig( - ctx: Context, - owners: Vec, - threshold: u64, - nonce: u8, - ) -> Result<()> { - let multisig = &mut ctx.accounts.multisig; - multisig.owners = owners; - multisig.threshold = threshold; - multisig.nonce = nonce; - Ok(()) - } - - // Creates a new transaction account, automatically signed by the creator, - // which must be one of the owners of the multisig. - pub fn create_transaction( - ctx: Context, - pid: Pubkey, - accs: Vec, - data: Vec, - ) -> Result<()> { - let owner_index = ctx - .accounts - .multisig - .owners - .iter() - .position(|a| a == ctx.accounts.proposer.key) - .ok_or(error!(ErrorCode::InvalidOwner))?; - - let mut signers = Vec::new(); - signers.resize(ctx.accounts.multisig.owners.len(), false); - signers[owner_index] = true; - - let tx = &mut ctx.accounts.transaction; - tx.program_id = pid; - tx.accounts = accs; - tx.data = data; - tx.signers = signers; - tx.multisig = *ctx.accounts.multisig.to_account_info().key; - tx.did_execute = false; - - Ok(()) - } - - // Approves a transaction on behalf of an owner of the multisig. - pub fn approve(ctx: Context) -> Result<()> { - let owner_index = ctx - .accounts - .multisig - .owners - .iter() - .position(|a| a == ctx.accounts.owner.key) - .ok_or(error!(ErrorCode::InvalidOwner))?; - - ctx.accounts.transaction.signers[owner_index] = true; - - Ok(()) - } - - // Sets the owners field on the multisig. The only way this can be invoked - // is via a recursive call from execute_transaction -> set_owners. - pub fn set_owners(ctx: Context, owners: Vec) -> Result<()> { - let multisig = &mut ctx.accounts.multisig; - - if (owners.len() as u64) < multisig.threshold { - multisig.threshold = owners.len() as u64; - } - - multisig.owners = owners; - Ok(()) - } - - // Changes the execution threshold of the multisig. The only way this can be - // invoked is via a recursive call from execute_transaction -> - // change_threshold. - pub fn change_threshold(ctx: Context, threshold: u64) -> Result<()> { - if threshold > ctx.accounts.multisig.owners.len() as u64 { - return err!(ErrorCode::InvalidThreshold); - } - let multisig = &mut ctx.accounts.multisig; - multisig.threshold = threshold; - Ok(()) - } - - // Executes the given transaction if threshold owners have signed it. - pub fn execute_transaction(ctx: Context) -> Result<()> { - // Has this been executed already? - if ctx.accounts.transaction.did_execute { - return err!(ErrorCode::AlreadyExecuted); - } - - // Do we have enough signers? - let sig_count = ctx - .accounts - .transaction - .signers - .iter() - .filter_map(|s| match s { - false => None, - true => Some(true), - }) - .collect::>() - .len() as u64; - if sig_count < ctx.accounts.multisig.threshold { - return err!(ErrorCode::NotEnoughSigners); - } - - // Execute the transaction signed by the multisig. - let mut ix: Instruction = (&*ctx.accounts.transaction).into(); - ix.accounts = ix - .accounts - .iter() - .map(|acc| { - if &acc.pubkey == ctx.accounts.multisig_signer.key { - AccountMeta::new_readonly(acc.pubkey, true) - } else { - acc.clone() - } - }) - .collect(); - let seeds = &[ - ctx.accounts.multisig.to_account_info().key.as_ref(), - &[ctx.accounts.multisig.nonce], - ]; - let signer = &[&seeds[..]]; - let accounts = ctx.remaining_accounts; - solana_program::program::invoke_signed(&ix, &accounts, signer)?; - - // Burn the transaction to ensure one time use. - ctx.accounts.transaction.did_execute = true; - - Ok(()) - } -} - -#[derive(Accounts)] -pub struct CreateMultisig<'info> { - #[account(zero)] - multisig: ProgramAccount<'info, Multisig>, -} - -#[derive(Accounts)] -pub struct CreateTransaction<'info> { - multisig: ProgramAccount<'info, Multisig>, - #[account(zero)] - transaction: ProgramAccount<'info, Transaction>, - // One of the owners. Checked in the handler. - proposer: Signer<'info>, -} - -#[derive(Accounts)] -pub struct Approve<'info> { - multisig: ProgramAccount<'info, Multisig>, - #[account(mut, has_one = multisig)] - transaction: ProgramAccount<'info, Transaction>, - // One of the multisig owners. Checked in the handler. - owner: Signer<'info>, -} - -#[derive(Accounts)] -pub struct Auth<'info> { - #[account(mut)] - multisig: ProgramAccount<'info, Multisig>, - #[account( - signer, - seeds = [multisig.to_account_info().key.as_ref()], - bump = multisig.nonce, - )] - multisig_signer: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct ExecuteTransaction<'info> { - multisig: ProgramAccount<'info, Multisig>, - #[account( - seeds = [multisig.to_account_info().key.as_ref()], - bump = multisig.nonce, - )] - multisig_signer: AccountInfo<'info>, - #[account(mut, has_one = multisig)] - transaction: ProgramAccount<'info, Transaction>, -} - -#[account] -pub struct Multisig { - owners: Vec, - threshold: u64, - nonce: u8, -} - -#[account] -pub struct Transaction { - // The multisig account this transaction belongs to. - multisig: Pubkey, - // Target program to execute against. - program_id: Pubkey, - // Accounts required for the transaction. - accounts: Vec, - // Instruction data for the transaction. - data: Vec, - // signers[index] is true iff multisig.owners[index] signed the transaction. - signers: Vec, - // Boolean ensuring one time execution. - did_execute: bool, -} - -impl From<&Transaction> for Instruction { - fn from(tx: &Transaction) -> Instruction { - Instruction { - program_id: tx.program_id, - accounts: tx.accounts.clone().into_iter().map(Into::into).collect(), - data: tx.data.clone(), - } - } -} - -#[derive(AnchorSerialize, AnchorDeserialize, Clone)] -pub struct TransactionAccount { - pubkey: Pubkey, - is_signer: bool, - is_writable: bool, -} - -impl From for AccountMeta { - fn from(account: TransactionAccount) -> AccountMeta { - match account.is_writable { - false => AccountMeta::new_readonly(account.pubkey, account.is_signer), - true => AccountMeta::new(account.pubkey, account.is_signer), - } - } -} - -#[error_code] -pub enum ErrorCode { - #[msg("The given owner is not part of this multisig.")] - InvalidOwner, - #[msg("Not enough owners signed this transaction.")] - NotEnoughSigners, - #[msg("Cannot delete a transaction that has been signed by an owner.")] - TransactionAlreadySigned, - #[msg("Overflow when adding.")] - Overflow, - #[msg("Cannot delete a transaction the owner did not create.")] - UnableToDelete, - #[msg("The given transaction has already been executed.")] - AlreadyExecuted, - #[msg("Threshold must be less than or equal to the number of owners.")] - InvalidThreshold, -} diff --git a/tests/multisig/tests/multisig.js b/tests/multisig/tests/multisig.js deleted file mode 100644 index 1a700025..00000000 --- a/tests/multisig/tests/multisig.js +++ /dev/null @@ -1,135 +0,0 @@ -const anchor = require("@project-serum/anchor"); -const { assert } = require("chai"); - -describe("multisig", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.env()); - - const program = anchor.workspace.Multisig; - - it("Tests the multisig program", async () => { - const multisig = anchor.web3.Keypair.generate(); - const [multisigSigner, nonce] = - await anchor.web3.PublicKey.findProgramAddress( - [multisig.publicKey.toBuffer()], - program.programId - ); - const multisigSize = 200; // Big enough. - - const ownerA = anchor.web3.Keypair.generate(); - const ownerB = anchor.web3.Keypair.generate(); - const ownerC = anchor.web3.Keypair.generate(); - const ownerD = anchor.web3.Keypair.generate(); - const owners = [ownerA.publicKey, ownerB.publicKey, ownerC.publicKey]; - - const threshold = new anchor.BN(2); - await program.rpc.createMultisig(owners, threshold, nonce, { - accounts: { - multisig: multisig.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - instructions: [ - await program.account.multisig.createInstruction( - multisig, - multisigSize - ), - ], - signers: [multisig], - }); - - let multisigAccount = await program.account.multisig.fetch( - multisig.publicKey - ); - - assert.strictEqual(multisigAccount.nonce, nonce); - assert.isTrue(multisigAccount.threshold.eq(new anchor.BN(2))); - assert.deepEqual(multisigAccount.owners, owners); - - const pid = program.programId; - const accounts = [ - { - pubkey: multisig.publicKey, - isWritable: true, - isSigner: false, - }, - { - pubkey: multisigSigner, - isWritable: false, - isSigner: true, - }, - ]; - const newOwners = [ownerA.publicKey, ownerB.publicKey, ownerD.publicKey]; - const data = program.coder.instruction.encode("set_owners", { - owners: newOwners, - }); - - const transaction = anchor.web3.Keypair.generate(); - const txSize = 1000; // Big enough, cuz I'm lazy. - await program.rpc.createTransaction(pid, accounts, data, { - accounts: { - multisig: multisig.publicKey, - transaction: transaction.publicKey, - proposer: ownerA.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - instructions: [ - await program.account.transaction.createInstruction( - transaction, - txSize - ), - ], - signers: [transaction, ownerA], - }); - - const txAccount = await program.account.transaction.fetch( - transaction.publicKey - ); - - assert.isTrue(txAccount.programId.equals(pid)); - assert.deepEqual(txAccount.accounts, accounts); - assert.deepEqual(txAccount.data, data); - assert.isTrue(txAccount.multisig.equals(multisig.publicKey)); - assert.strictEqual(txAccount.didExecute, false); - - // Other owner approves transaction. - await program.rpc.approve({ - accounts: { - multisig: multisig.publicKey, - transaction: transaction.publicKey, - owner: ownerB.publicKey, - }, - signers: [ownerB], - }); - - // Now that we've reached the threshold, send the transaction. - await program.rpc.executeTransaction({ - accounts: { - multisig: multisig.publicKey, - multisigSigner, - transaction: transaction.publicKey, - }, - remainingAccounts: program.instruction.setOwners - .accounts({ - multisig: multisig.publicKey, - multisigSigner, - }) - // Change the signer status on the vendor signer since it's signed by the program, not the client. - .map((meta) => - meta.pubkey.equals(multisigSigner) - ? { ...meta, isSigner: false } - : meta - ) - .concat({ - pubkey: program.programId, - isWritable: false, - isSigner: false, - }), - }); - - multisigAccount = await program.account.multisig.fetch(multisig.publicKey); - - assert.strictEqual(multisigAccount.nonce, nonce); - assert.isTrue(multisigAccount.threshold.eq(new anchor.BN(2))); - assert.deepEqual(multisigAccount.owners, newOwners); - }); -}); diff --git a/tests/package.json b/tests/package.json deleted file mode 100644 index edf90aff..00000000 --- a/tests/package.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "name": "anchor-tests", - "private": true, - "scripts": { - "lint:fix": "prettier */*.js \"*/**/*{.js,.ts}\" -w", - "lint": "prettier */*.js \"*/**/*{.js,.ts}\" --check" - }, - "workspaces": [ - "cashiers-check", - "cfo", - "chat", - "composite", - "custom-coder", - "errors", - "escrow", - "events", - "floats", - "ido-pool", - "interface", - "lockup", - "misc", - "multisig", - "permissioned-markets", - "pda-derivation", - "pyth", - "spl/token-proxy", - "swap", - "system-accounts", - "sysvars", - "tictactoe", - "typescript", - "validator-clone", - "zero-copy", - "declare-id", - "cpi-returns", - "multiple-suites", - "bpf-upgradeable-state" - ], - "dependencies": { - "@project-serum/anchor": "file:../ts", - "@project-serum/common": "^0.0.1-beta.3", - "@project-serum/serum": "^0.13.60", - "@solana/spl-token": "^0.1.8" - }, - "devDependencies": { - "@types/node": "^14.14.37", - "chai": "^4.3.4", - "@types/chai": "^4.3.0", - "@types/mocha": "^9.1.0", - "mocha": "^9.1.3", - "ts-mocha": "^8.0.0", - "typescript": "^4.4.4", - "prettier": "^2.5.1" - } -} diff --git a/tests/pda-derivation/Anchor.toml b/tests/pda-derivation/Anchor.toml deleted file mode 100644 index 78935651..00000000 --- a/tests/pda-derivation/Anchor.toml +++ /dev/null @@ -1,15 +0,0 @@ -[features] -seeds = true - -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -pda_derivation = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[workspace] -members = ["programs/pda-derivation"] - -[scripts] -test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" diff --git a/tests/pda-derivation/Cargo.toml b/tests/pda-derivation/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/pda-derivation/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/pda-derivation/migrations/deploy.ts b/tests/pda-derivation/migrations/deploy.ts deleted file mode 100644 index 53e1252d..00000000 --- a/tests/pda-derivation/migrations/deploy.ts +++ /dev/null @@ -1,22 +0,0 @@ -// Migrations are an early feature. Currently, they're nothing more than this -// single deploy script that's invoked from the CLI, injecting a provider -// configured from the workspace's Anchor.toml. - -const anchor = require("@project-serum/anchor"); - -module.exports = async function (provider) { - // Configure client to use the provider. - anchor.setProvider(provider); - - // Add your deploy script here. - async function deployAsync(exampleString: string): Promise { - return new Promise((resolve) => { - setTimeout(() => { - console.log(exampleString); - resolve(); - }, 2000); - }); - } - - await deployAsync("Typescript migration example complete."); -}; diff --git a/tests/pda-derivation/package.json b/tests/pda-derivation/package.json deleted file mode 100644 index 17c94bc2..00000000 --- a/tests/pda-derivation/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "pda-derivation", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/pda-derivation/programs/pda-derivation/Cargo.toml b/tests/pda-derivation/programs/pda-derivation/Cargo.toml deleted file mode 100644 index 0cb47eb3..00000000 --- a/tests/pda-derivation/programs/pda-derivation/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "pda-derivation" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "pda_derivation" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } diff --git a/tests/pda-derivation/programs/pda-derivation/Xargo.toml b/tests/pda-derivation/programs/pda-derivation/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/pda-derivation/programs/pda-derivation/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/pda-derivation/programs/pda-derivation/src/lib.rs b/tests/pda-derivation/programs/pda-derivation/src/lib.rs deleted file mode 100644 index 43fdbc15..00000000 --- a/tests/pda-derivation/programs/pda-derivation/src/lib.rs +++ /dev/null @@ -1,84 +0,0 @@ -//! The typescript example serves to show how one would setup an Anchor -//! workspace with TypeScript tests and migrations. - -use anchor_lang::prelude::*; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -pub const MY_SEED: [u8; 2] = *b"hi"; -pub const MY_SEED_STR: &str = "hi"; -pub const MY_SEED_U8: u8 = 1; -pub const MY_SEED_U32: u32 = 2; -pub const MY_SEED_U64: u64 = 3; - -#[program] -pub mod pda_derivation { - use super::*; - - pub fn init_base(ctx: Context, data: u64, data_key: Pubkey) -> Result<()> { - let base = &mut ctx.accounts.base; - base.base_data = data; - base.base_data_key = data_key; - Ok(()) - } - - pub fn init_my_account(ctx: Context, _seed_a: u8) -> Result<()> { - ctx.accounts.account.data = 1337; - Ok(()) - } -} - -#[derive(Accounts)] -pub struct InitBase<'info> { - #[account( - init, - payer = payer, - space = 8+8+32, - )] - base: Account<'info, BaseAccount>, - #[account(mut)] - payer: Signer<'info>, - system_program: Program<'info, System>, -} - -#[derive(Accounts)] -#[instruction(seed_a: u8)] -pub struct InitMyAccount<'info> { - base: Account<'info, BaseAccount>, - base2: AccountInfo<'info>, - #[account( - init, - payer = payer, - space = 8+8, - seeds = [ - &seed_a.to_le_bytes(), - "another-seed".as_bytes(), - b"test".as_ref(), - base.key().as_ref(), - base2.key.as_ref(), - MY_SEED.as_ref(), - MY_SEED_STR.as_bytes(), - MY_SEED_U8.to_le_bytes().as_ref(), - &MY_SEED_U32.to_le_bytes(), - &MY_SEED_U64.to_le_bytes(), - base.base_data.to_le_bytes().as_ref(), - base.base_data_key.as_ref(), - ], - bump, - )] - account: Account<'info, MyAccount>, - #[account(mut)] - payer: Signer<'info>, - system_program: Program<'info, System>, -} - -#[account] -pub struct MyAccount { - data: u64, -} - -#[account] -pub struct BaseAccount { - base_data: u64, - base_data_key: Pubkey, -} diff --git a/tests/pda-derivation/tests/typescript.spec.ts b/tests/pda-derivation/tests/typescript.spec.ts deleted file mode 100644 index 490b49ab..00000000 --- a/tests/pda-derivation/tests/typescript.spec.ts +++ /dev/null @@ -1,68 +0,0 @@ -import * as anchor from "@project-serum/anchor"; -import BN from "bn.js"; -import { Keypair } from "@solana/web3.js"; -import { findProgramAddressSync } from "@project-serum/anchor/dist/cjs/utils/pubkey"; -import { Program } from "@project-serum/anchor"; -import { PdaDerivation } from "../target/types/pda_derivation"; -import { expect } from "chai"; -const encode = anchor.utils.bytes.utf8.encode; - -describe("typescript", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.env()); - - const program = anchor.workspace.PdaDerivation as Program; - const base = Keypair.generate(); - const dataKey = Keypair.generate(); - const data = new BN(1); - const seedA = 4; - - it("Inits the base account", async () => { - await program.methods - .initBase(data, dataKey.publicKey) - .accounts({ - base: base.publicKey, - }) - .signers([base]) - .rpc(); - }); - - it("Inits the derived accounts", async () => { - const MY_SEED = "hi"; - const MY_SEED_STR = "hi"; - const MY_SEED_U8 = 1; - const MY_SEED_U32 = 2; - const MY_SEED_U64 = 3; - const expectedPDAKey = findProgramAddressSync( - [ - Buffer.from([seedA]), - encode("another-seed"), - encode("test"), - base.publicKey.toBuffer(), - base.publicKey.toBuffer(), - encode(MY_SEED), - encode(MY_SEED_STR), - Buffer.from([MY_SEED_U8]), - new anchor.BN(MY_SEED_U32).toArrayLike(Buffer, "le", 4), - new anchor.BN(MY_SEED_U64).toArrayLike(Buffer, "le", 8), - new anchor.BN(data).toArrayLike(Buffer, "le", 8), - dataKey.publicKey.toBuffer(), - ], - program.programId - )[0]; - - const tx = program.methods.initMyAccount(seedA).accounts({ - base: base.publicKey, - base2: base.publicKey, - }); - - const keys = await tx.pubkeys(); - expect(keys.account.equals(expectedPDAKey)).is.true; - - await tx.rpc(); - - const actualData = (await program.account.myAccount.fetch(expectedPDAKey)) - .data; - expect(actualData.toNumber()).is.equal(1337); - }); -}); diff --git a/tests/pda-derivation/tsconfig.json b/tests/pda-derivation/tsconfig.json deleted file mode 100644 index cd5d2e3d..00000000 --- a/tests/pda-derivation/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "types": ["mocha", "chai"], - "typeRoots": ["./node_modules/@types"], - "lib": ["es2015"], - "module": "commonjs", - "target": "es6", - "esModuleInterop": true - } -} diff --git a/ts/tests/program-common.spec.ts b/tests/program-common.spec.ts similarity index 100% rename from ts/tests/program-common.spec.ts rename to tests/program-common.spec.ts diff --git a/tests/pyth/Anchor.toml b/tests/pyth/Anchor.toml deleted file mode 100644 index 68059115..00000000 --- a/tests/pyth/Anchor.toml +++ /dev/null @@ -1,11 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -pyth = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[scripts] -test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" - -[features] diff --git a/tests/pyth/Cargo.toml b/tests/pyth/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/pyth/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/pyth/Xargo.toml b/tests/pyth/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/pyth/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/pyth/package.json b/tests/pyth/package.json deleted file mode 100644 index 4a5c6a30..00000000 --- a/tests/pyth/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "pyth", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/pyth/programs/pyth/Cargo.toml b/tests/pyth/programs/pyth/Cargo.toml deleted file mode 100644 index b71aec7a..00000000 --- a/tests/pyth/programs/pyth/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "pyth" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "pyth" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } -arrayref = "0.3.6" -bytemuck = { version = "1.4.0" } diff --git a/tests/pyth/programs/pyth/Xargo.toml b/tests/pyth/programs/pyth/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/pyth/programs/pyth/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/pyth/programs/pyth/src/lib.rs b/tests/pyth/programs/pyth/src/lib.rs deleted file mode 100644 index ff858042..00000000 --- a/tests/pyth/programs/pyth/src/lib.rs +++ /dev/null @@ -1,41 +0,0 @@ -use anchor_lang::prelude::*; -mod pc; -use pc::Price; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -pub mod pyth { - use super::*; - - pub fn initialize(ctx: Context, price: i64, expo: i32, conf: u64) -> Result<()> { - let oracle = &ctx.accounts.price; - - let mut price_oracle = Price::load(&oracle).unwrap(); - - price_oracle.agg.price = price; - price_oracle.agg.conf = conf; - price_oracle.expo = expo; - price_oracle.ptype = pc::PriceType::Price; - Ok(()) - } - - pub fn set_price(ctx: Context, price: i64) -> Result<()> { - let oracle = &ctx.accounts.price; - let mut price_oracle = Price::load(&oracle).unwrap(); - price_oracle.agg.price = price as i64; - Ok(()) - } -} - -#[derive(Accounts)] -pub struct SetPrice<'info> { - #[account(mut)] - pub price: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct Initialize<'info> { - #[account(mut)] - pub price: AccountInfo<'info>, -} diff --git a/tests/pyth/programs/pyth/src/pc.rs b/tests/pyth/programs/pyth/src/pc.rs deleted file mode 100644 index 9ee2ef3a..00000000 --- a/tests/pyth/programs/pyth/src/pc.rs +++ /dev/null @@ -1,118 +0,0 @@ -use crate::*; -use anchor_lang::prelude::AccountInfo; -use bytemuck::{cast_slice_mut, from_bytes_mut, try_cast_slice_mut, Pod, Zeroable}; -use std::cell::RefMut; - -#[derive(Default, Copy, Clone)] -#[repr(C)] -pub struct AccKey { - pub val: [u8; 32], -} - -#[derive(Copy, Clone)] -#[repr(C)] -pub enum PriceStatus { - Unknown, - Trading, - Halted, - Auction, -} - -impl Default for PriceStatus { - fn default() -> Self { - PriceStatus::Trading - } -} - -#[derive(Copy, Clone)] -#[repr(C)] -pub enum CorpAction { - NoCorpAct, -} - -impl Default for CorpAction { - fn default() -> Self { - CorpAction::NoCorpAct - } -} - -#[derive(Default, Copy, Clone)] -#[repr(C)] -pub struct PriceInfo { - pub price: i64, - pub conf: u64, - pub status: PriceStatus, - pub corp_act: CorpAction, - pub pub_slot: u64, -} -#[derive(Default, Copy, Clone)] -#[repr(C)] -pub struct PriceComp { - publisher: AccKey, - agg: PriceInfo, - latest: PriceInfo, -} - -#[derive(Copy, Clone)] -#[repr(C)] -pub enum PriceType { - Unknown, - Price, - TWAP, - Volatility, -} - -impl Default for PriceType { - fn default() -> Self { - PriceType::Price - } -} - -#[derive(Default, Copy, Clone)] -#[repr(C)] -pub struct Price { - pub magic: u32, // Pyth magic number. - pub ver: u32, // Program version. - pub atype: u32, // Account type. - pub size: u32, // Price account size. - pub ptype: PriceType, // Price or calculation type. - pub expo: i32, // Price exponent. - pub num: u32, // Number of component prices. - pub unused: u32, - pub curr_slot: u64, // Currently accumulating price slot. - pub valid_slot: u64, // Valid slot-time of agg. price. - pub twap: i64, // Time-weighted average price. - pub avol: u64, // Annualized price volatility. - pub drv0: i64, // Space for future derived values. - pub drv1: i64, // Space for future derived values. - pub drv2: i64, // Space for future derived values. - pub drv3: i64, // Space for future derived values. - pub drv4: i64, // Space for future derived values. - pub drv5: i64, // Space for future derived values. - pub prod: AccKey, // Product account key. - pub next: AccKey, // Next Price account in linked list. - pub agg_pub: AccKey, // Quoter who computed last aggregate price. - pub agg: PriceInfo, // Aggregate price info. - pub comp: [PriceComp; 32], // Price components one per quoter. -} - -impl Price { - #[inline] - pub fn load<'a>(price_feed: &'a AccountInfo) -> Result> { - let account_data: RefMut<'a, [u8]>; - let state: RefMut<'a, Self>; - - account_data = RefMut::map(price_feed.try_borrow_mut_data().unwrap(), |data| *data); - - state = RefMut::map(account_data, |data| { - from_bytes_mut(cast_slice_mut::(try_cast_slice_mut(data).unwrap())) - }); - Ok(state) - } -} - -#[cfg(target_endian = "little")] -unsafe impl Zeroable for Price {} - -#[cfg(target_endian = "little")] -unsafe impl Pod for Price {} diff --git a/tests/pyth/tests/oracleUtils.ts b/tests/pyth/tests/oracleUtils.ts deleted file mode 100644 index 746b409b..00000000 --- a/tests/pyth/tests/oracleUtils.ts +++ /dev/null @@ -1,313 +0,0 @@ -import { Buffer } from "buffer"; -import { BN, Program, web3 } from "@project-serum/anchor"; - -export const Magic = 0xa1b2c3d4; -export const Version1 = 1; -export const Version = Version1; -export const PriceStatus = ["Unknown", "Trading", "Halted", "Auction"]; -export const CorpAction = ["NoCorpAct"]; -export const PriceType = ["Unknown", "Price", "TWAP", "Volatility"]; - -const empty32Buffer = Buffer.alloc(32); -const PKorNull = (data: Buffer) => - data.equals(empty32Buffer) ? null : new web3.PublicKey(data); - -interface ICreatePriceFeed { - oracleProgram: Program; - initPrice: number; - confidence?: BN; - expo?: number; -} -export const createPriceFeed = async ({ - oracleProgram, - initPrice, - confidence, - expo = -4, -}: ICreatePriceFeed) => { - const conf = confidence || new BN((initPrice / 10) * 10 ** -expo); - const collateralTokenFeed = new web3.Account(); - await oracleProgram.rpc.initialize( - new BN(initPrice * 10 ** -expo), - expo, - conf, - { - accounts: { price: collateralTokenFeed.publicKey }, - signers: [collateralTokenFeed], - instructions: [ - web3.SystemProgram.createAccount({ - fromPubkey: oracleProgram.provider.wallet.publicKey, - newAccountPubkey: collateralTokenFeed.publicKey, - space: 3312, - lamports: - await oracleProgram.provider.connection.getMinimumBalanceForRentExemption( - 3312 - ), - programId: oracleProgram.programId, - }), - ], - } - ); - return collateralTokenFeed.publicKey; -}; -export const setFeedPrice = async ( - oracleProgram: Program, - newPrice: number, - priceFeed: web3.PublicKey -) => { - const info = await oracleProgram.provider.connection.getAccountInfo( - priceFeed - ); - const data = parsePriceData(info.data); - await oracleProgram.rpc.setPrice(new BN(newPrice * 10 ** -data.exponent), { - accounts: { price: priceFeed }, - }); -}; -export const getFeedData = async ( - oracleProgram: Program, - priceFeed: web3.PublicKey -) => { - const info = await oracleProgram.provider.connection.getAccountInfo( - priceFeed - ); - return parsePriceData(info.data); -}; - -// https://github.com/nodejs/node/blob/v14.17.0/lib/internal/errors.js#L758 -const ERR_BUFFER_OUT_OF_BOUNDS = () => - new Error("Attempt to access memory outside buffer bounds"); - -// https://github.com/nodejs/node/blob/v14.17.0/lib/internal/errors.js#L968 -const ERR_INVALID_ARG_TYPE = (name: string, expected: string, actual: any) => - new Error( - `The "${name}" argument must be of type ${expected}. Received ${actual}` - ); - -// https://github.com/nodejs/node/blob/v14.17.0/lib/internal/errors.js#L1262 -const ERR_OUT_OF_RANGE = (str: string, range: string, received: number) => - new Error( - `The value of "${str} is out of range. It must be ${range}. Received ${received}` - ); - -// https://github.com/nodejs/node/blob/v14.17.0/lib/internal/validators.js#L127-L130 -function validateNumber(value: any, name: string) { - if (typeof value !== "number") - throw ERR_INVALID_ARG_TYPE(name, "number", value); -} - -// https://github.com/nodejs/node/blob/v14.17.0/lib/internal/buffer.js#L68-L80 -function boundsError(value: number, length: number) { - if (Math.floor(value) !== value) { - validateNumber(value, "offset"); - throw ERR_OUT_OF_RANGE("offset", "an integer", value); - } - - if (length < 0) throw ERR_BUFFER_OUT_OF_BOUNDS(); - - throw ERR_OUT_OF_RANGE("offset", `>= 0 and <= ${length}`, value); -} - -export function readBigInt64LE(buffer: Buffer, offset = 0): bigint { - validateNumber(offset, "offset"); - const first = buffer[offset]; - const last = buffer[offset + 7]; - if (first === undefined || last === undefined) - boundsError(offset, buffer.length - 8); - const val = - buffer[offset + 4] + - buffer[offset + 5] * 2 ** 8 + - buffer[offset + 6] * 2 ** 16 + - (last << 24); // Overflow - return ( - (BigInt(val) << BigInt(32)) + - BigInt( - first + - buffer[++offset] * 2 ** 8 + - buffer[++offset] * 2 ** 16 + - buffer[++offset] * 2 ** 24 - ) - ); -} - -// https://github.com/nodejs/node/blob/v14.17.0/lib/internal/buffer.js#L89-L107 -export function readBigUInt64LE(buffer: Buffer, offset = 0): bigint { - validateNumber(offset, "offset"); - const first = buffer[offset]; - const last = buffer[offset + 7]; - if (first === undefined || last === undefined) - boundsError(offset, buffer.length - 8); - - const lo = - first + - buffer[++offset] * 2 ** 8 + - buffer[++offset] * 2 ** 16 + - buffer[++offset] * 2 ** 24; - - const hi = - buffer[++offset] + - buffer[++offset] * 2 ** 8 + - buffer[++offset] * 2 ** 16 + - last * 2 ** 24; - - return BigInt(lo) + (BigInt(hi) << BigInt(32)); // tslint:disable-line:no-bitwise -} - -export const parsePriceData = (data: Buffer) => { - // Pyth magic number. - const magic = data.readUInt32LE(0); - // Program version. - const version = data.readUInt32LE(4); - // Account type. - const type = data.readUInt32LE(8); - // Price account size. - const size = data.readUInt32LE(12); - // Price or calculation type. - const priceType = data.readUInt32LE(16); - // Price exponent. - const exponent = data.readInt32LE(20); - // Number of component prices. - const numComponentPrices = data.readUInt32LE(24); - // unused - // const unused = accountInfo.data.readUInt32LE(28) - // Currently accumulating price slot. - const currentSlot = readBigUInt64LE(data, 32); - // Valid on-chain slot of aggregate price. - const validSlot = readBigUInt64LE(data, 40); - // Time-weighted average price. - const twapComponent = readBigInt64LE(data, 48); - const twap = Number(twapComponent) * 10 ** exponent; - // Annualized price volatility. - const avolComponent = readBigUInt64LE(data, 56); - const avol = Number(avolComponent) * 10 ** exponent; - // Space for future derived values. - const drv0Component = readBigInt64LE(data, 64); - const drv0 = Number(drv0Component) * 10 ** exponent; - const drv1Component = readBigInt64LE(data, 72); - const drv1 = Number(drv1Component) * 10 ** exponent; - const drv2Component = readBigInt64LE(data, 80); - const drv2 = Number(drv2Component) * 10 ** exponent; - const drv3Component = readBigInt64LE(data, 88); - const drv3 = Number(drv3Component) * 10 ** exponent; - const drv4Component = readBigInt64LE(data, 96); - const drv4 = Number(drv4Component) * 10 ** exponent; - const drv5Component = readBigInt64LE(data, 104); - const drv5 = Number(drv5Component) * 10 ** exponent; - // Product id / reference account. - const productAccountKey = new web3.PublicKey(data.slice(112, 144)); - // Next price account in list. - const nextPriceAccountKey = PKorNull(data.slice(144, 176)); - // Aggregate price updater. - const aggregatePriceUpdaterAccountKey = new web3.PublicKey( - data.slice(176, 208) - ); - const aggregatePriceInfo = parsePriceInfo(data.slice(208, 240), exponent); - // Price components - up to 32. - const priceComponents = []; - let offset = 240; - let shouldContinue = true; - while (offset < data.length && shouldContinue) { - const publisher = PKorNull(data.slice(offset, offset + 32)); - offset += 32; - if (publisher) { - const aggregate = parsePriceInfo( - data.slice(offset, offset + 32), - exponent - ); - offset += 32; - const latest = parsePriceInfo(data.slice(offset, offset + 32), exponent); - offset += 32; - priceComponents.push({ publisher, aggregate, latest }); - } else { - shouldContinue = false; - } - } - return { - magic, - version, - type, - size, - priceType, - exponent, - numComponentPrices, - currentSlot, - validSlot, - twapComponent, - twap, - avolComponent, - avol, - drv0Component, - drv0, - drv1Component, - drv1, - drv2Component, - drv2, - drv3Component, - drv3, - drv4Component, - drv4, - drv5Component, - drv5, - productAccountKey, - nextPriceAccountKey, - aggregatePriceUpdaterAccountKey, - ...aggregatePriceInfo, - priceComponents, - }; -}; - -interface ProductAttributes { - [index: string]: string; -} - -export const parseProductData = (data: Buffer) => { - // Pyth magic number. - const magic = data.readUInt32LE(0); - // Program version. - const version = data.readUInt32LE(4); - // Account type. - const type = data.readUInt32LE(8); - // Price account size. - const size = data.readUInt32LE(12); - // First price account in list. - const priceAccountBytes = data.slice(16, 48); - const priceAccountKey = new web3.PublicKey(priceAccountBytes); - const product: ProductAttributes = {}; - let idx = 48; - while (idx < data.length) { - const keyLength = data[idx]; - idx++; - if (keyLength) { - const key = data.slice(idx, idx + keyLength).toString(); - idx += keyLength; - const valueLength = data[idx]; - idx++; - const value = data.slice(idx, idx + valueLength).toString(); - idx += valueLength; - product[key] = value; - } - } - return { magic, version, type, size, priceAccountKey, product }; -}; - -const parsePriceInfo = (data: Buffer, exponent: number) => { - // Aggregate price. - const priceComponent = data.readBigUInt64LE(0); - const price = Number(priceComponent) * 10 ** exponent; - // Aggregate confidence. - const confidenceComponent = data.readBigUInt64LE(8); - const confidence = Number(confidenceComponent) * 10 ** exponent; - // Aggregate status. - const status = data.readUInt32LE(16); - // Aggregate corporate action. - const corporateAction = data.readUInt32LE(20); - // Aggregate publish slot. - const publishSlot = data.readBigUInt64LE(24); - return { - priceComponent, - price, - confidenceComponent, - confidence, - status, - corporateAction, - publishSlot, - }; -}; diff --git a/tests/pyth/tests/pyth.spec.ts b/tests/pyth/tests/pyth.spec.ts deleted file mode 100644 index 579f68c6..00000000 --- a/tests/pyth/tests/pyth.spec.ts +++ /dev/null @@ -1,39 +0,0 @@ -import * as anchor from "@project-serum/anchor"; -import { BN, Program, web3 } from "@project-serum/anchor"; -import { assert } from "chai"; -import { createPriceFeed, setFeedPrice, getFeedData } from "./oracleUtils"; - -describe("pyth-oracle", () => { - anchor.setProvider(anchor.AnchorProvider.env()); - const program = anchor.workspace.Pyth as Program; - - it("initialize", async () => { - const price = 50000; - const priceFeedAddress = await createPriceFeed({ - oracleProgram: program, - initPrice: price, - expo: -6, - }); - const feedData = await getFeedData(program, priceFeedAddress); - assert.strictEqual(feedData.price, price); - }); - - it("change feed price", async () => { - const price = 50000; - const expo = -7; - const priceFeedAddress = await createPriceFeed({ - oracleProgram: program, - initPrice: price, - expo: expo, - }); - const feedDataBefore = await getFeedData(program, priceFeedAddress); - assert.strictEqual(feedDataBefore.price, price); - assert.strictEqual(feedDataBefore.exponent, expo); - - const newPrice = 55000; - await setFeedPrice(program, newPrice, priceFeedAddress); - const feedDataAfter = await getFeedData(program, priceFeedAddress); - assert.strictEqual(feedDataAfter.price, newPrice); - assert.strictEqual(feedDataAfter.exponent, expo); - }); -}); diff --git a/tests/pyth/tsconfig.json b/tests/pyth/tsconfig.json deleted file mode 100644 index cd5d2e3d..00000000 --- a/tests/pyth/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "types": ["mocha", "chai"], - "typeRoots": ["./node_modules/@types"], - "lib": ["es2015"], - "module": "commonjs", - "target": "es6", - "esModuleInterop": true - } -} diff --git a/tests/safety-checks/.gitignore b/tests/safety-checks/.gitignore deleted file mode 100644 index 51448d4d..00000000 --- a/tests/safety-checks/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ - -.anchor -.DS_Store -target -**/*.rs.bk -node_modules diff --git a/tests/safety-checks/Anchor.toml b/tests/safety-checks/Anchor.toml deleted file mode 100644 index c9b6f551..00000000 --- a/tests/safety-checks/Anchor.toml +++ /dev/null @@ -1,13 +0,0 @@ -[programs.localnet] -unchecked_account = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" -account_info = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[registry] -url = "https://anchor.projectserum.com" - -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[scripts] -test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" diff --git a/tests/safety-checks/Cargo.toml b/tests/safety-checks/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/safety-checks/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/safety-checks/migrations/deploy.ts b/tests/safety-checks/migrations/deploy.ts deleted file mode 100644 index 5e3df0dc..00000000 --- a/tests/safety-checks/migrations/deploy.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Migrations are an early feature. Currently, they're nothing more than this -// single deploy script that's invoked from the CLI, injecting a provider -// configured from the workspace's Anchor.toml. - -const anchor = require("@project-serum/anchor"); - -module.exports = async function (provider) { - // Configure client to use the provider. - anchor.setProvider(provider); - - // Add your deploy script here. -}; diff --git a/tests/safety-checks/programs/account-info/Cargo.toml b/tests/safety-checks/programs/account-info/Cargo.toml deleted file mode 100644 index e222f06e..00000000 --- a/tests/safety-checks/programs/account-info/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "account-info" -version = "0.1.0" -description = "Created with Anchor" -edition = "2018" - -[lib] -crate-type = ["cdylib", "lib"] -name = "account_info" - -[features] -no-entrypoint = [] -no-idl = [] -no-log-ix-name = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } diff --git a/tests/safety-checks/programs/account-info/Xargo.toml b/tests/safety-checks/programs/account-info/Xargo.toml deleted file mode 100644 index 475fb71e..00000000 --- a/tests/safety-checks/programs/account-info/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] diff --git a/tests/safety-checks/programs/account-info/src/lib.rs b/tests/safety-checks/programs/account-info/src/lib.rs deleted file mode 100644 index 74d8f121..00000000 --- a/tests/safety-checks/programs/account-info/src/lib.rs +++ /dev/null @@ -1,16 +0,0 @@ -use anchor_lang::prelude::*; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -pub mod account_info { - use super::*; - pub fn initialize(ctx: Context) -> Result<()> { - Ok(()) - } -} - -#[derive(Accounts)] -pub struct Initialize<'info> { - unchecked: AccountInfo<'info>, -} diff --git a/tests/safety-checks/programs/unchecked-account/Cargo.toml b/tests/safety-checks/programs/unchecked-account/Cargo.toml deleted file mode 100644 index 69443051..00000000 --- a/tests/safety-checks/programs/unchecked-account/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "unchecked-account" -version = "0.1.0" -description = "Created with Anchor" -edition = "2018" - -[lib] -crate-type = ["cdylib", "lib"] -name = "safety_checks" - -[features] -no-entrypoint = [] -no-idl = [] -no-log-ix-name = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } diff --git a/tests/safety-checks/programs/unchecked-account/Xargo.toml b/tests/safety-checks/programs/unchecked-account/Xargo.toml deleted file mode 100644 index 475fb71e..00000000 --- a/tests/safety-checks/programs/unchecked-account/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] diff --git a/tests/safety-checks/programs/unchecked-account/src/lib.rs b/tests/safety-checks/programs/unchecked-account/src/lib.rs deleted file mode 100644 index d9f59cf8..00000000 --- a/tests/safety-checks/programs/unchecked-account/src/lib.rs +++ /dev/null @@ -1,16 +0,0 @@ -use anchor_lang::prelude::*; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -pub mod unchecked_account { - use super::*; - pub fn initialize(ctx: Context) -> Result<()> { - Ok(()) - } -} - -#[derive(Accounts)] -pub struct Initialize<'info> { - unchecked: UncheckedAccount<'info>, -} diff --git a/tests/safety-checks/test.sh b/tests/safety-checks/test.sh deleted file mode 100755 index 935c4a59..00000000 --- a/tests/safety-checks/test.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash - -echo "Building programs" - -# -# Build the UncheckedAccount variant. -# -pushd programs/unchecked-account/ -output=$(anchor build 2>&1 > /dev/null) -if ! [[ $output =~ "Struct field \"unchecked\" is unsafe" ]]; then - echo "Error: expected /// CHECK error" - exit 1 -fi -popd - -# -# Build the AccountInfo variant. -# -pushd programs/account-info/ -output=$(anchor build 2>&1 > /dev/null) -if ! [[ $output =~ "Struct field \"unchecked\" is unsafe" ]]; then - echo "Error: expected /// CHECK error" - exit 1 -fi -popd - -echo "Success. All builds failed." diff --git a/tests/safety-checks/tests/safety-checks.ts b/tests/safety-checks/tests/safety-checks.ts deleted file mode 100644 index ba4bc4c5..00000000 --- a/tests/safety-checks/tests/safety-checks.ts +++ /dev/null @@ -1,16 +0,0 @@ -import * as anchor from "@project-serum/anchor"; -import { Program } from "@project-serum/anchor"; -import { SafetyChecks } from "../target/types/safety_checks"; - -describe("safety-checks", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.env()); - - const program = anchor.workspace.SafetyChecks as Program; - - it("Is initialized!", async () => { - // Add your test here. - const tx = await program.rpc.initialize({}); - console.log("Your transaction signature", tx); - }); -}); diff --git a/tests/safety-checks/tsconfig.json b/tests/safety-checks/tsconfig.json deleted file mode 100644 index cd5d2e3d..00000000 --- a/tests/safety-checks/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "types": ["mocha", "chai"], - "typeRoots": ["./node_modules/@types"], - "lib": ["es2015"], - "module": "commonjs", - "target": "es6", - "esModuleInterop": true - } -} diff --git a/tests/spl/token-proxy/Anchor.toml b/tests/spl/token-proxy/Anchor.toml deleted file mode 100644 index ac6cd695..00000000 --- a/tests/spl/token-proxy/Anchor.toml +++ /dev/null @@ -1,11 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -token_proxy = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[scripts] -test = "yarn run mocha -t 1000000 tests/" - -[features] diff --git a/tests/spl/token-proxy/Cargo.toml b/tests/spl/token-proxy/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/spl/token-proxy/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/spl/token-proxy/package.json b/tests/spl/token-proxy/package.json deleted file mode 100644 index 4b7e5894..00000000 --- a/tests/spl/token-proxy/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "token-proxy", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/spl/token-proxy/programs/token-proxy/Cargo.toml b/tests/spl/token-proxy/programs/token-proxy/Cargo.toml deleted file mode 100644 index 9559a968..00000000 --- a/tests/spl/token-proxy/programs/token-proxy/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "token-proxy" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "token_proxy" - -[features] -no-entrypoint = [] -cpi = ["no-entrypoint"] - -[dependencies] -anchor-lang = { path = "../../../../../lang" } -anchor-spl = { path = "../../../../../spl" } -spl-token = { version = "3.1.1", features = ["no-entrypoint"] } diff --git a/tests/spl/token-proxy/programs/token-proxy/Xargo.toml b/tests/spl/token-proxy/programs/token-proxy/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/spl/token-proxy/programs/token-proxy/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/spl/token-proxy/programs/token-proxy/src/lib.rs b/tests/spl/token-proxy/programs/token-proxy/src/lib.rs deleted file mode 100644 index 7ef43cb7..00000000 --- a/tests/spl/token-proxy/programs/token-proxy/src/lib.rs +++ /dev/null @@ -1,151 +0,0 @@ -//! This example demonstrates the use of the `anchor_spl::token` CPI client. - -use anchor_lang::prelude::*; -use anchor_spl::token::{self, Burn, MintTo, SetAuthority, Transfer}; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -mod token_proxy { - use super::*; - - pub fn proxy_transfer(ctx: Context, amount: u64) -> Result<()> { - token::transfer(ctx.accounts.into(), amount) - } - - pub fn proxy_mint_to(ctx: Context, amount: u64) -> Result<()> { - token::mint_to(ctx.accounts.into(), amount) - } - - pub fn proxy_burn(ctx: Context, amount: u64) -> Result<()> { - token::burn(ctx.accounts.into(), amount) - } - - pub fn proxy_set_authority( - ctx: Context, - authority_type: AuthorityType, - new_authority: Option, - ) -> Result<()> { - token::set_authority(ctx.accounts.into(), authority_type.into(), new_authority) - } -} - -#[derive(AnchorSerialize, AnchorDeserialize)] -pub enum AuthorityType { - /// Authority to mint new tokens - MintTokens, - /// Authority to freeze any account associated with the Mint - FreezeAccount, - /// Owner of a given token account - AccountOwner, - /// Authority to close a token account - CloseAccount, -} - -#[derive(Accounts)] -pub struct ProxyTransfer<'info> { - #[account(signer)] - pub authority: AccountInfo<'info>, - #[account(mut)] - pub from: AccountInfo<'info>, - #[account(mut)] - pub to: AccountInfo<'info>, - pub token_program: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct ProxyMintTo<'info> { - #[account(signer)] - pub authority: AccountInfo<'info>, - #[account(mut)] - pub mint: AccountInfo<'info>, - #[account(mut)] - pub to: AccountInfo<'info>, - pub token_program: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct ProxyBurn<'info> { - #[account(signer)] - pub authority: AccountInfo<'info>, - #[account(mut)] - pub mint: AccountInfo<'info>, - #[account(mut)] - pub from: AccountInfo<'info>, - pub token_program: AccountInfo<'info>, -} - -#[derive(Accounts)] -pub struct ProxySetAuthority<'info> { - #[account(signer)] - pub current_authority: AccountInfo<'info>, - #[account(mut)] - pub account_or_mint: AccountInfo<'info>, - pub token_program: AccountInfo<'info>, -} - -impl<'a, 'b, 'c, 'info> From<&mut ProxyTransfer<'info>> - for CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> -{ - fn from(accounts: &mut ProxyTransfer<'info>) -> CpiContext<'a, 'b, 'c, 'info, Transfer<'info>> { - let cpi_accounts = Transfer { - from: accounts.from.clone(), - to: accounts.to.clone(), - authority: accounts.authority.clone(), - }; - let cpi_program = accounts.token_program.clone(); - CpiContext::new(cpi_program, cpi_accounts) - } -} - -impl<'a, 'b, 'c, 'info> From<&mut ProxyMintTo<'info>> - for CpiContext<'a, 'b, 'c, 'info, MintTo<'info>> -{ - fn from(accounts: &mut ProxyMintTo<'info>) -> CpiContext<'a, 'b, 'c, 'info, MintTo<'info>> { - let cpi_accounts = MintTo { - mint: accounts.mint.clone(), - to: accounts.to.clone(), - authority: accounts.authority.clone(), - }; - let cpi_program = accounts.token_program.clone(); - CpiContext::new(cpi_program, cpi_accounts) - } -} - -impl<'a, 'b, 'c, 'info> From<&mut ProxyBurn<'info>> for CpiContext<'a, 'b, 'c, 'info, Burn<'info>> { - fn from(accounts: &mut ProxyBurn<'info>) -> CpiContext<'a, 'b, 'c, 'info, Burn<'info>> { - let cpi_accounts = Burn { - mint: accounts.mint.clone(), - from: accounts.from.clone(), - authority: accounts.authority.clone(), - }; - let cpi_program = accounts.token_program.clone(); - CpiContext::new(cpi_program, cpi_accounts) - } -} - -impl<'a, 'b, 'c, 'info> From<&mut ProxySetAuthority<'info>> - for CpiContext<'a, 'b, 'c, 'info, SetAuthority<'info>> -{ - fn from( - accounts: &mut ProxySetAuthority<'info>, - ) -> CpiContext<'a, 'b, 'c, 'info, SetAuthority<'info>> { - let cpi_accounts = SetAuthority { - account_or_mint: accounts.account_or_mint.clone(), - current_authority: accounts.current_authority.clone(), - }; // TODO: Support multisig signers - let cpi_program = accounts.token_program.clone(); - CpiContext::new(cpi_program, cpi_accounts) - } -} - -impl From for spl_token::instruction::AuthorityType { - fn from(authority_ty: AuthorityType) -> spl_token::instruction::AuthorityType { - match authority_ty { - AuthorityType::MintTokens => spl_token::instruction::AuthorityType::MintTokens, - AuthorityType::FreezeAccount => spl_token::instruction::AuthorityType::FreezeAccount, - AuthorityType::AccountOwner => spl_token::instruction::AuthorityType::AccountOwner, - AuthorityType::CloseAccount => spl_token::instruction::AuthorityType::CloseAccount, - } - } -} diff --git a/tests/spl/token-proxy/tests/token-proxy.js b/tests/spl/token-proxy/tests/token-proxy.js deleted file mode 100644 index 503c976f..00000000 --- a/tests/spl/token-proxy/tests/token-proxy.js +++ /dev/null @@ -1,178 +0,0 @@ -const anchor = require("@project-serum/anchor"); -const { assert } = require("chai"); - -describe("token", () => { - const provider = anchor.AnchorProvider.local(); - - // Configure the client to use the local cluster. - anchor.setProvider(provider); - - const program = anchor.workspace.TokenProxy; - - let mint = null; - let from = null; - let to = null; - - it("Initializes test state", async () => { - mint = await createMint(provider); - from = await createTokenAccount(provider, mint, provider.wallet.publicKey); - to = await createTokenAccount(provider, mint, provider.wallet.publicKey); - }); - - it("Mints a token", async () => { - await program.rpc.proxyMintTo(new anchor.BN(1000), { - accounts: { - authority: provider.wallet.publicKey, - mint, - to: from, - tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID, - }, - }); - - const fromAccount = await getTokenAccount(provider, from); - - assert.isTrue(fromAccount.amount.eq(new anchor.BN(1000))); - }); - - it("Transfers a token", async () => { - await program.rpc.proxyTransfer(new anchor.BN(400), { - accounts: { - authority: provider.wallet.publicKey, - to, - from, - tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID, - }, - }); - - const fromAccount = await getTokenAccount(provider, from); - const toAccount = await getTokenAccount(provider, to); - - assert.isTrue(fromAccount.amount.eq(new anchor.BN(600))); - assert.isTrue(toAccount.amount.eq(new anchor.BN(400))); - }); - - it("Burns a token", async () => { - await program.rpc.proxyBurn(new anchor.BN(399), { - accounts: { - authority: provider.wallet.publicKey, - mint, - from: to, - tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID, - }, - }); - - const toAccount = await getTokenAccount(provider, to); - assert.isTrue(toAccount.amount.eq(new anchor.BN(1))); - }); - - it("Set new mint authority", async () => { - const newMintAuthority = anchor.web3.Keypair.generate(); - await program.rpc.proxySetAuthority( - { mintTokens: {} }, - newMintAuthority.publicKey, - { - accounts: { - accountOrMint: mint, - currentAuthority: provider.wallet.publicKey, - tokenProgram: TokenInstructions.TOKEN_PROGRAM_ID, - }, - } - ); - - const mintInfo = await getMintInfo(provider, mint); - assert.isTrue(mintInfo.mintAuthority.equals(newMintAuthority.publicKey)); - }); -}); - -// SPL token client boilerplate for test initialization. Everything below here is -// mostly irrelevant to the point of the example. - -const serumCmn = require("@project-serum/common"); -const TokenInstructions = require("@project-serum/serum").TokenInstructions; - -// TODO: remove this constant once @project-serum/serum uses the same version -// of @solana/web3.js as anchor (or switch packages). -const TOKEN_PROGRAM_ID = new anchor.web3.PublicKey( - TokenInstructions.TOKEN_PROGRAM_ID.toString() -); - -async function getTokenAccount(provider, addr) { - return await serumCmn.getTokenAccount(provider, addr); -} - -async function getMintInfo(provider, mintAddr) { - return await serumCmn.getMintInfo(provider, mintAddr); -} - -async function createMint(provider, authority) { - if (authority === undefined) { - authority = provider.wallet.publicKey; - } - const mint = anchor.web3.Keypair.generate(); - const instructions = await createMintInstructions( - provider, - authority, - mint.publicKey - ); - - const tx = new anchor.web3.Transaction(); - tx.add(...instructions); - - await provider.sendAndConfirm(tx, [mint]); - - return mint.publicKey; -} - -async function createMintInstructions(provider, authority, mint) { - let instructions = [ - anchor.web3.SystemProgram.createAccount({ - fromPubkey: provider.wallet.publicKey, - newAccountPubkey: mint, - space: 82, - lamports: await provider.connection.getMinimumBalanceForRentExemption(82), - programId: TOKEN_PROGRAM_ID, - }), - TokenInstructions.initializeMint({ - mint, - decimals: 0, - mintAuthority: authority, - }), - ]; - return instructions; -} - -async function createTokenAccount(provider, mint, owner) { - const vault = anchor.web3.Keypair.generate(); - const tx = new anchor.web3.Transaction(); - tx.add( - ...(await createTokenAccountInstrs(provider, vault.publicKey, mint, owner)) - ); - await provider.sendAndConfirm(tx, [vault]); - return vault.publicKey; -} - -async function createTokenAccountInstrs( - provider, - newAccountPubkey, - mint, - owner, - lamports -) { - if (lamports === undefined) { - lamports = await provider.connection.getMinimumBalanceForRentExemption(165); - } - return [ - anchor.web3.SystemProgram.createAccount({ - fromPubkey: provider.wallet.publicKey, - newAccountPubkey, - space: 165, - lamports, - programId: TOKEN_PROGRAM_ID, - }), - TokenInstructions.initializeAccount({ - account: newAccountPubkey, - mint, - owner, - }), - ]; -} diff --git a/tests/swap/Anchor.toml b/tests/swap/Anchor.toml deleted file mode 100644 index fa7d2b91..00000000 --- a/tests/swap/Anchor.toml +++ /dev/null @@ -1,15 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -swap = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[[test.genesis]] -address = "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin" -program = "./deps/serum-dex/dex/target/deploy/serum_dex.so" - -[scripts] -test = "yarn run mocha -t 1000000 tests/" - -[features] diff --git a/tests/swap/Cargo.toml b/tests/swap/Cargo.toml deleted file mode 100644 index 52ed11f4..00000000 --- a/tests/swap/Cargo.toml +++ /dev/null @@ -1,7 +0,0 @@ -[workspace] -members = [ - "programs/*" -] -exclude = [ - "deps/serum-dex" -] \ No newline at end of file diff --git a/tests/swap/README.md b/tests/swap/README.md deleted file mode 100644 index 60778c27..00000000 --- a/tests/swap/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# Swap - -An example swap program that provides a convenient API to the Serum orderbook -for performing instantly settled token swaps. - -## Usage - -This example requires building the Serum DEX from source, which is done using -git submodules. - -### Install Submodules - -Pull the source - -``` -git submodule init -git submodule update -``` - -### Build the DEX - -Build it - -``` -cd deps/serum-dex/dex/ && cargo build-bpf && cd ../../../ -``` - -### Run the Test - -Run the test - -``` -anchor test -``` diff --git a/tests/swap/deps/serum-dex b/tests/swap/deps/serum-dex deleted file mode 160000 index 54206467..00000000 --- a/tests/swap/deps/serum-dex +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5420646709df6edaaea90162d4f7476328077293 diff --git a/tests/swap/migrations/deploy.js b/tests/swap/migrations/deploy.js deleted file mode 100644 index 5e3df0dc..00000000 --- a/tests/swap/migrations/deploy.js +++ /dev/null @@ -1,12 +0,0 @@ -// Migrations are an early feature. Currently, they're nothing more than this -// single deploy script that's invoked from the CLI, injecting a provider -// configured from the workspace's Anchor.toml. - -const anchor = require("@project-serum/anchor"); - -module.exports = async function (provider) { - // Configure client to use the provider. - anchor.setProvider(provider); - - // Add your deploy script here. -}; diff --git a/tests/swap/package.json b/tests/swap/package.json deleted file mode 100644 index 18d73f98..00000000 --- a/tests/swap/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "swap", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/swap/programs/swap/Cargo.toml b/tests/swap/programs/swap/Cargo.toml deleted file mode 100644 index af3e0763..00000000 --- a/tests/swap/programs/swap/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "swap" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "swap" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } -anchor-spl = { path = "../../../../spl", features = ["dex"] } -serum_dex = { git = "https://github.com/project-serum/serum-dex", rev = "1be91f2", version = "0.4.0", features = ["no-entrypoint"] } diff --git a/tests/swap/programs/swap/Xargo.toml b/tests/swap/programs/swap/Xargo.toml deleted file mode 100644 index 475fb71e..00000000 --- a/tests/swap/programs/swap/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] diff --git a/tests/swap/programs/swap/src/lib.rs b/tests/swap/programs/swap/src/lib.rs deleted file mode 100644 index 1e4fa5f4..00000000 --- a/tests/swap/programs/swap/src/lib.rs +++ /dev/null @@ -1,496 +0,0 @@ -//! Program to perform instantly settled token swaps on the Serum DEX. -//! -//! Before using any instruction here, a user must first create an open orders -//! account on all markets being used. This only needs to be done once. As a -//! convention established by the DEX, this should be done via the system -//! program create account instruction in the same transaction as the user's -//! first trade. Then, the DEX will lazily initialize the open orders account. - -use anchor_lang::prelude::*; -use anchor_spl::dex; -use anchor_spl::token; -use serum_dex::instruction::SelfTradeBehavior; -use serum_dex::matching::{OrderType, Side as SerumSide}; -use serum_dex::state::MarketState; -use std::num::NonZeroU64; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -pub mod swap { - use super::*; - - /// Swaps two tokens on a single A/B market, where A is the base currency - /// and B is the quote currency. This is just a direct IOC trade that - /// instantly settles. - /// - /// When side is "bid", then swaps B for A. When side is "ask", then swaps - /// A for B. - /// - /// Arguments: - /// - /// * `side` - The direction to swap. - /// * `amount` - The amount to swap *from* - /// * `min_expected_swap_amount` - The minimum amount of the *to* token the - /// client expects to receive from the swap. The instruction fails if - /// execution would result in less. - #[access_control(is_valid_swap(&ctx))] - pub fn swap<'info>( - ctx: Context<'_, '_, '_, 'info, Swap<'info>>, - side: Side, - amount: u64, - min_expected_swap_amount: u64, - ) -> Result<()> { - // Optional referral account (earns a referral fee). - let referral = ctx.remaining_accounts.iter().next().map(Clone::clone); - - // Side determines swap direction. - let (from_token, to_token) = match side { - Side::Bid => (&ctx.accounts.pc_wallet, &ctx.accounts.market.coin_wallet), - Side::Ask => (&ctx.accounts.market.coin_wallet, &ctx.accounts.pc_wallet), - }; - - // Token balances before the trade. - let from_amount_before = token::accessor::amount(from_token)?; - let to_amount_before = token::accessor::amount(to_token)?; - - // Execute trade. - let orderbook: OrderbookClient<'info> = (&*ctx.accounts).into(); - match side { - Side::Bid => orderbook.buy(amount, referral.clone())?, - Side::Ask => orderbook.sell(amount, referral.clone())?, - }; - orderbook.settle(referral)?; - - // Token balances after the trade. - let from_amount_after = token::accessor::amount(from_token)?; - let to_amount_after = token::accessor::amount(to_token)?; - - // Calculate the delta, i.e. the amount swapped. - let from_amount = from_amount_before.checked_sub(from_amount_after).unwrap(); - let to_amount = to_amount_after.checked_sub(to_amount_before).unwrap(); - - // Safety checks. - apply_risk_checks(DidSwap { - authority: *ctx.accounts.authority.key, - given_amount: amount, - min_expected_swap_amount, - from_amount, - to_amount, - spill_amount: 0, - from_mint: token::accessor::mint(from_token)?, - to_mint: token::accessor::mint(to_token)?, - quote_mint: match side { - Side::Bid => token::accessor::mint(from_token)?, - Side::Ask => token::accessor::mint(to_token)?, - }, - })?; - - Ok(()) - } - - /// Swaps two base currencies across two different markets. - /// - /// That is, suppose there are two markets, A/USD(x) and B/USD(x). - /// Then swaps token A for token B via - /// - /// * IOC (immediate or cancel) sell order on A/USD(x) market. - /// * Settle open orders to get USD(x). - /// * IOC buy order on B/USD(x) market to convert USD(x) to token B. - /// * Settle open orders to get token B. - /// - /// Arguments: - /// - /// * `amount` - The amount to swap *from*. - /// * `min_expected_swap_amount - The minimum amount of the *to* token the - /// client expects to receive from the swap. The instruction fails if - /// execution would result in less. - #[access_control(is_valid_swap_transitive(&ctx))] - pub fn swap_transitive<'info>( - ctx: Context<'_, '_, '_, 'info, SwapTransitive<'info>>, - amount: u64, - min_expected_swap_amount: u64, - ) -> Result<()> { - // Optional referral account (earns a referral fee). - let referral = ctx.remaining_accounts.iter().next().map(Clone::clone); - - // Leg 1: Sell Token A for USD(x) (or whatever quote currency is used). - let (from_amount, sell_proceeds) = { - // Token balances before the trade. - let base_before = token::accessor::amount(&ctx.accounts.from.coin_wallet)?; - let quote_before = token::accessor::amount(&ctx.accounts.pc_wallet)?; - - // Execute the trade. - let orderbook = ctx.accounts.orderbook_from(); - orderbook.sell(amount, referral.clone())?; - orderbook.settle(referral.clone())?; - - // Token balances after the trade. - let base_after = token::accessor::amount(&ctx.accounts.from.coin_wallet)?; - let quote_after = token::accessor::amount(&ctx.accounts.pc_wallet)?; - - // Report the delta. - ( - base_before.checked_sub(base_after).unwrap(), - quote_after.checked_sub(quote_before).unwrap(), - ) - }; - - // Leg 2: Buy Token B with USD(x) (or whatever quote currency is used). - let (to_amount, spill_amount) = { - // Token balances before the trade. - let base_before = token::accessor::amount(&ctx.accounts.to.coin_wallet)?; - let quote_before = token::accessor::amount(&ctx.accounts.pc_wallet)?; - - // Execute the trade. - let orderbook = ctx.accounts.orderbook_to(); - orderbook.buy(sell_proceeds, referral.clone())?; - orderbook.settle(referral)?; - - // Token balances after the trade. - let base_after = token::accessor::amount(&ctx.accounts.to.coin_wallet)?; - let quote_after = token::accessor::amount(&ctx.accounts.pc_wallet)?; - - // Report the delta. - ( - base_after.checked_sub(base_before).unwrap(), - quote_before.checked_sub(quote_after).unwrap(), - ) - }; - - // Safety checks. - apply_risk_checks(DidSwap { - given_amount: amount, - min_expected_swap_amount, - from_amount, - to_amount, - spill_amount, - from_mint: token::accessor::mint(&ctx.accounts.from.coin_wallet)?, - to_mint: token::accessor::mint(&ctx.accounts.to.coin_wallet)?, - quote_mint: token::accessor::mint(&ctx.accounts.pc_wallet)?, - authority: *ctx.accounts.authority.key, - })?; - - Ok(()) - } -} - -// Asserts the swap event is valid. -fn apply_risk_checks(event: DidSwap) -> Result<()> { - // Reject if the resulting amount is less than the client's expectation. - if event.to_amount < event.min_expected_swap_amount { - return err!(ErrorCode::SlippageExceeded); - } - emit!(event); - Ok(()) -} - -// The only constraint imposed on these accounts is that the market's base -// currency mint is not equal to the quote currency's. All other checks are -// done by the DEX on CPI. -#[derive(Accounts)] -pub struct Swap<'info> { - market: MarketAccounts<'info>, - #[account(signer)] - authority: AccountInfo<'info>, - #[account(mut)] - pc_wallet: AccountInfo<'info>, - // Programs. - dex_program: AccountInfo<'info>, - token_program: AccountInfo<'info>, - // Sysvars. - rent: AccountInfo<'info>, -} - -impl<'info> From<&Swap<'info>> for OrderbookClient<'info> { - fn from(accounts: &Swap<'info>) -> OrderbookClient<'info> { - OrderbookClient { - market: accounts.market.clone(), - authority: accounts.authority.clone(), - pc_wallet: accounts.pc_wallet.clone(), - dex_program: accounts.dex_program.clone(), - token_program: accounts.token_program.clone(), - rent: accounts.rent.clone(), - } - } -} - -// The only constraint imposed on these accounts is that the from market's -// base currency's is not equal to the to market's base currency. All other -// checks are done by the DEX on CPI (and the quote currency is ensured to be -// the same on both markets since there's only one account field for it). -#[derive(Accounts)] -pub struct SwapTransitive<'info> { - from: MarketAccounts<'info>, - to: MarketAccounts<'info>, - // Must be the authority over all open orders accounts used. - #[account(signer)] - authority: AccountInfo<'info>, - #[account(mut)] - pc_wallet: AccountInfo<'info>, - // Programs. - dex_program: AccountInfo<'info>, - token_program: AccountInfo<'info>, - // Sysvars. - rent: AccountInfo<'info>, -} - -impl<'info> SwapTransitive<'info> { - fn orderbook_from(&self) -> OrderbookClient<'info> { - OrderbookClient { - market: self.from.clone(), - authority: self.authority.clone(), - pc_wallet: self.pc_wallet.clone(), - dex_program: self.dex_program.clone(), - token_program: self.token_program.clone(), - rent: self.rent.clone(), - } - } - fn orderbook_to(&self) -> OrderbookClient<'info> { - OrderbookClient { - market: self.to.clone(), - authority: self.authority.clone(), - pc_wallet: self.pc_wallet.clone(), - dex_program: self.dex_program.clone(), - token_program: self.token_program.clone(), - rent: self.rent.clone(), - } - } -} - -// Client for sending orders to the Serum DEX. -struct OrderbookClient<'info> { - market: MarketAccounts<'info>, - authority: AccountInfo<'info>, - pc_wallet: AccountInfo<'info>, - dex_program: AccountInfo<'info>, - token_program: AccountInfo<'info>, - rent: AccountInfo<'info>, -} - -impl<'info> OrderbookClient<'info> { - // Executes the sell order portion of the swap, purchasing as much of the - // quote currency as possible for the given `base_amount`. - // - // `base_amount` is the "native" amount of the base currency, i.e., token - // amount including decimals. - fn sell(&self, base_amount: u64, referral: Option>) -> Result<()> { - let limit_price = 1; - let max_coin_qty = { - // The loaded market must be dropped before CPI. - let market = MarketState::load(&self.market.market, &dex::ID) - .map_err(|de| ProgramError::from(de))?; - coin_lots(&market, base_amount) - }; - let max_native_pc_qty = u64::MAX; - self.order_cpi( - limit_price, - max_coin_qty, - max_native_pc_qty, - Side::Ask, - referral, - ) - } - - // Executes the buy order portion of the swap, purchasing as much of the - // base currency as possible, for the given `quote_amount`. - // - // `quote_amount` is the "native" amount of the quote currency, i.e., token - // amount including decimals. - fn buy(&self, quote_amount: u64, referral: Option>) -> Result<()> { - let limit_price = u64::MAX; - let max_coin_qty = u64::MAX; - let max_native_pc_qty = quote_amount; - self.order_cpi( - limit_price, - max_coin_qty, - max_native_pc_qty, - Side::Bid, - referral, - ) - } - - // Executes a new order on the serum dex via CPI. - // - // * `limit_price` - the limit order price in lot units. - // * `max_coin_qty`- the max number of the base currency lot units. - // * `max_native_pc_qty` - the max number of quote currency in native token - // units (includes decimals). - // * `side` - bid or ask, i.e. the type of order. - // * `referral` - referral account, earning a fee. - fn order_cpi( - &self, - limit_price: u64, - max_coin_qty: u64, - max_native_pc_qty: u64, - side: Side, - referral: Option>, - ) -> Result<()> { - // Client order id is only used for cancels. Not used here so hardcode. - let client_order_id = 0; - // Limit is the dex's custom compute budge parameter, setting an upper - // bound on the number of matching cycles the program can perform - // before giving up and posting the remaining unmatched order. - let limit = 65535; - - let dex_accs = dex::NewOrderV3 { - market: self.market.market.clone(), - open_orders: self.market.open_orders.clone(), - request_queue: self.market.request_queue.clone(), - event_queue: self.market.event_queue.clone(), - market_bids: self.market.bids.clone(), - market_asks: self.market.asks.clone(), - order_payer_token_account: self.market.order_payer_token_account.clone(), - open_orders_authority: self.authority.clone(), - coin_vault: self.market.coin_vault.clone(), - pc_vault: self.market.pc_vault.clone(), - token_program: self.token_program.clone(), - rent: self.rent.clone(), - }; - let mut ctx = CpiContext::new(self.dex_program.clone(), dex_accs); - if let Some(referral) = referral { - ctx = ctx.with_remaining_accounts(vec![referral]); - } - dex::new_order_v3( - ctx, - side.into(), - NonZeroU64::new(limit_price).unwrap(), - NonZeroU64::new(max_coin_qty).unwrap(), - NonZeroU64::new(max_native_pc_qty).unwrap(), - SelfTradeBehavior::DecrementTake, - OrderType::ImmediateOrCancel, - client_order_id, - limit, - ) - } - - fn settle(&self, referral: Option>) -> Result<()> { - let settle_accs = dex::SettleFunds { - market: self.market.market.clone(), - open_orders: self.market.open_orders.clone(), - open_orders_authority: self.authority.clone(), - coin_vault: self.market.coin_vault.clone(), - pc_vault: self.market.pc_vault.clone(), - coin_wallet: self.market.coin_wallet.clone(), - pc_wallet: self.pc_wallet.clone(), - vault_signer: self.market.vault_signer.clone(), - token_program: self.token_program.clone(), - }; - let mut ctx = CpiContext::new(self.dex_program.clone(), settle_accs); - if let Some(referral) = referral { - ctx = ctx.with_remaining_accounts(vec![referral]); - } - dex::settle_funds(ctx) - } -} - -// Returns the amount of lots for the base currency of a trade with `size`. -fn coin_lots(market: &MarketState, size: u64) -> u64 { - size.checked_div(market.coin_lot_size).unwrap() -} - -// Market accounts are the accounts used to place orders against the dex minus -// common accounts, i.e., program ids, sysvars, and the `pc_wallet`. -#[derive(Accounts, Clone)] -pub struct MarketAccounts<'info> { - #[account(mut)] - market: AccountInfo<'info>, - #[account(mut)] - open_orders: AccountInfo<'info>, - #[account(mut)] - request_queue: AccountInfo<'info>, - #[account(mut)] - event_queue: AccountInfo<'info>, - #[account(mut)] - bids: AccountInfo<'info>, - #[account(mut)] - asks: AccountInfo<'info>, - // The `spl_token::Account` that funds will be taken from, i.e., transferred - // from the user into the market's vault. - // - // For bids, this is the base currency. For asks, the quote. - #[account(mut)] - order_payer_token_account: AccountInfo<'info>, - // Also known as the "base" currency. For a given A/B market, - // this is the vault for the A mint. - #[account(mut)] - coin_vault: AccountInfo<'info>, - // Also known as the "quote" currency. For a given A/B market, - // this is the vault for the B mint. - #[account(mut)] - pc_vault: AccountInfo<'info>, - // PDA owner of the DEX's token accounts for base + quote currencies. - vault_signer: AccountInfo<'info>, - // User wallets. - #[account(mut)] - coin_wallet: AccountInfo<'info>, -} - -#[derive(AnchorSerialize, AnchorDeserialize)] -pub enum Side { - Bid, - Ask, -} - -impl From for SerumSide { - fn from(side: Side) -> SerumSide { - match side { - Side::Bid => SerumSide::Bid, - Side::Ask => SerumSide::Ask, - } - } -} - -// Access control modifiers. - -fn is_valid_swap(ctx: &Context) -> Result<()> { - _is_valid_swap(&ctx.accounts.market.coin_wallet, &ctx.accounts.pc_wallet) -} - -fn is_valid_swap_transitive(ctx: &Context) -> Result<()> { - _is_valid_swap(&ctx.accounts.from.coin_wallet, &ctx.accounts.to.coin_wallet) -} - -// Validates the tokens being swapped are of different mints. -fn _is_valid_swap<'info>(from: &AccountInfo<'info>, to: &AccountInfo<'info>) -> Result<()> { - let from_token_mint = token::accessor::mint(from)?; - let to_token_mint = token::accessor::mint(to)?; - if from_token_mint == to_token_mint { - return err!(ErrorCode::SwapTokensCannotMatch); - } - Ok(()) -} - -// Event emitted when a swap occurs for two base currencies on two different -// markets (quoted in the same token). -#[event] -pub struct DidSwap { - // User given (max) amount to swap. - pub given_amount: u64, - // The minimum amount of the *to* token expected to be received from - // executing the swap. - pub min_expected_swap_amount: u64, - // Amount of the `from` token sold. - pub from_amount: u64, - // Amount of the `to` token purchased. - pub to_amount: u64, - // Amount of the quote currency accumulated from the swap. - pub spill_amount: u64, - // Mint sold. - pub from_mint: Pubkey, - // Mint purchased. - pub to_mint: Pubkey, - // Mint of the token used as the quote currency in the two markets used - // for swapping. - pub quote_mint: Pubkey, - // User that signed the transaction. - pub authority: Pubkey, -} - -#[error_code] -pub enum ErrorCode { - #[msg("The tokens being swapped must have different mints")] - SwapTokensCannotMatch, - #[msg("Slippage tolerance exceeded")] - SlippageExceeded, -} diff --git a/tests/swap/tests/swap.js b/tests/swap/tests/swap.js deleted file mode 100644 index 21dd0778..00000000 --- a/tests/swap/tests/swap.js +++ /dev/null @@ -1,315 +0,0 @@ -const { assert } = require("chai"); -const anchor = require("@project-serum/anchor"); -const BN = anchor.BN; -const OpenOrders = require("@project-serum/serum").OpenOrders; -const TOKEN_PROGRAM_ID = require("@solana/spl-token").TOKEN_PROGRAM_ID; -const serumCmn = require("@project-serum/common"); -const utils = require("./utils"); - -// Taker fee rate (bps). -const TAKER_FEE = 0.0022; - -describe("swap", () => { - // Configure the client to use the local cluster. - const provider = anchor.AnchorProvider.env(); - // hack so we don't have to update serum-common library - // to the new AnchorProvider class and Provider interface - provider.send = provider.sendAndConfirm; - anchor.setProvider(provider); - - // Swap program client. - const program = anchor.workspace.Swap; - - // Accounts used to setup the orderbook. - let ORDERBOOK_ENV, - // Accounts used for A -> USDC swap transactions. - SWAP_A_USDC_ACCOUNTS, - // Accounts used for USDC -> A swap transactions. - SWAP_USDC_A_ACCOUNTS, - // Serum DEX vault PDA for market A/USDC. - marketAVaultSigner, - // Serum DEX vault PDA for market B/USDC. - marketBVaultSigner; - - // Open orders accounts on the two markets for the provider. - const openOrdersA = anchor.web3.Keypair.generate(); - const openOrdersB = anchor.web3.Keypair.generate(); - - it("BOILERPLATE: Sets up two markets with resting orders", async () => { - ORDERBOOK_ENV = await utils.setupTwoMarkets({ - provider: program.provider, - }); - }); - - it("BOILERPLATE: Sets up reusable accounts", async () => { - const marketA = ORDERBOOK_ENV.marketA; - const marketB = ORDERBOOK_ENV.marketB; - - const [vaultSignerA] = await utils.getVaultOwnerAndNonce( - marketA._decoded.ownAddress - ); - const [vaultSignerB] = await utils.getVaultOwnerAndNonce( - marketB._decoded.ownAddress - ); - marketAVaultSigner = vaultSignerA; - marketBVaultSigner = vaultSignerB; - - SWAP_USDC_A_ACCOUNTS = { - market: { - market: marketA._decoded.ownAddress, - requestQueue: marketA._decoded.requestQueue, - eventQueue: marketA._decoded.eventQueue, - bids: marketA._decoded.bids, - asks: marketA._decoded.asks, - coinVault: marketA._decoded.baseVault, - pcVault: marketA._decoded.quoteVault, - vaultSigner: marketAVaultSigner, - // User params. - openOrders: openOrdersA.publicKey, - orderPayerTokenAccount: ORDERBOOK_ENV.godUsdc, - coinWallet: ORDERBOOK_ENV.godA, - }, - pcWallet: ORDERBOOK_ENV.godUsdc, - authority: program.provider.wallet.publicKey, - dexProgram: utils.DEX_PID, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }; - SWAP_A_USDC_ACCOUNTS = { - ...SWAP_USDC_A_ACCOUNTS, - market: { - ...SWAP_USDC_A_ACCOUNTS.market, - orderPayerTokenAccount: ORDERBOOK_ENV.godA, - }, - }; - }); - - it("Swaps from USDC to Token A", async () => { - const marketA = ORDERBOOK_ENV.marketA; - - // Swap exactly enough USDC to get 1.2 A tokens (best offer price is 6.041 USDC). - const expectedResultantAmount = 7.2; - const bestOfferPrice = 6.041; - const amountToSpend = expectedResultantAmount * bestOfferPrice; - const swapAmount = new BN((amountToSpend / (1 - TAKER_FEE)) * 10 ** 6); - - const [tokenAChange, usdcChange] = await withBalanceChange( - program.provider, - [ORDERBOOK_ENV.godA, ORDERBOOK_ENV.godUsdc], - async () => { - await program.rpc.swap(Side.Bid, swapAmount, new BN(1.0), { - accounts: SWAP_USDC_A_ACCOUNTS, - instructions: [ - // First order to this market so one must create the open orders account. - await OpenOrders.makeCreateAccountTransaction( - program.provider.connection, - marketA._decoded.ownAddress, - program.provider.wallet.publicKey, - openOrdersA.publicKey, - utils.DEX_PID - ), - // Might as well create the second open orders account while we're here. - // In prod, this should actually be done within the same tx as an - // order to market B. - await OpenOrders.makeCreateAccountTransaction( - program.provider.connection, - ORDERBOOK_ENV.marketB._decoded.ownAddress, - program.provider.wallet.publicKey, - openOrdersB.publicKey, - utils.DEX_PID - ), - ], - signers: [openOrdersA, openOrdersB], - }); - } - ); - - assert.strictEqual(tokenAChange, expectedResultantAmount); - assert.strictEqual(usdcChange, -swapAmount.toNumber() / 10 ** 6); - }); - - it("Swaps from Token A to USDC", async () => { - const marketA = ORDERBOOK_ENV.marketA; - - // Swap out A tokens for USDC. - const swapAmount = 8.1; - const bestBidPrice = 6.004; - const amountToFill = swapAmount * bestBidPrice; - const takerFee = 0.0022; - const resultantAmount = new BN(amountToFill * (1 - TAKER_FEE) * 10 ** 6); - - const [tokenAChange, usdcChange] = await withBalanceChange( - program.provider, - [ORDERBOOK_ENV.godA, ORDERBOOK_ENV.godUsdc], - async () => { - await program.rpc.swap( - Side.Ask, - new BN(swapAmount * 10 ** 6), - new BN(swapAmount), - { - accounts: SWAP_A_USDC_ACCOUNTS, - } - ); - } - ); - - assert.strictEqual(tokenAChange, -swapAmount); - assert.strictEqual(usdcChange, resultantAmount.toNumber() / 10 ** 6); - }); - - it("Swaps from Token A to Token B", async () => { - const marketA = ORDERBOOK_ENV.marketA; - const marketB = ORDERBOOK_ENV.marketB; - const swapAmount = 10; - const [tokenAChange, tokenBChange, usdcChange] = await withBalanceChange( - program.provider, - [ORDERBOOK_ENV.godA, ORDERBOOK_ENV.godB, ORDERBOOK_ENV.godUsdc], - async () => { - // Perform the actual swap. - await program.rpc.swapTransitive( - new BN(swapAmount * 10 ** 6), - new BN(swapAmount - 1), - { - accounts: { - from: { - market: marketA._decoded.ownAddress, - requestQueue: marketA._decoded.requestQueue, - eventQueue: marketA._decoded.eventQueue, - bids: marketA._decoded.bids, - asks: marketA._decoded.asks, - coinVault: marketA._decoded.baseVault, - pcVault: marketA._decoded.quoteVault, - vaultSigner: marketAVaultSigner, - // User params. - openOrders: openOrdersA.publicKey, - // Swapping from A -> USDC. - orderPayerTokenAccount: ORDERBOOK_ENV.godA, - coinWallet: ORDERBOOK_ENV.godA, - }, - to: { - market: marketB._decoded.ownAddress, - requestQueue: marketB._decoded.requestQueue, - eventQueue: marketB._decoded.eventQueue, - bids: marketB._decoded.bids, - asks: marketB._decoded.asks, - coinVault: marketB._decoded.baseVault, - pcVault: marketB._decoded.quoteVault, - vaultSigner: marketBVaultSigner, - // User params. - openOrders: openOrdersB.publicKey, - // Swapping from USDC -> B. - orderPayerTokenAccount: ORDERBOOK_ENV.godUsdc, - coinWallet: ORDERBOOK_ENV.godB, - }, - pcWallet: ORDERBOOK_ENV.godUsdc, - authority: program.provider.wallet.publicKey, - dexProgram: utils.DEX_PID, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - } - ); - } - ); - - assert.strictEqual(tokenAChange, -swapAmount); - // TODO: calculate this dynamically from the swap amount. - assert.strictEqual(tokenBChange, 9.8); - assert.strictEqual(usdcChange, 0); - }); - - it("Swaps from Token B to Token A", async () => { - const marketA = ORDERBOOK_ENV.marketA; - const marketB = ORDERBOOK_ENV.marketB; - const swapAmount = 23; - const [tokenAChange, tokenBChange, usdcChange] = await withBalanceChange( - program.provider, - [ORDERBOOK_ENV.godA, ORDERBOOK_ENV.godB, ORDERBOOK_ENV.godUsdc], - async () => { - // Perform the actual swap. - await program.rpc.swapTransitive( - new BN(swapAmount * 10 ** 6), - new BN(swapAmount - 1), - { - accounts: { - from: { - market: marketB._decoded.ownAddress, - requestQueue: marketB._decoded.requestQueue, - eventQueue: marketB._decoded.eventQueue, - bids: marketB._decoded.bids, - asks: marketB._decoded.asks, - coinVault: marketB._decoded.baseVault, - pcVault: marketB._decoded.quoteVault, - vaultSigner: marketBVaultSigner, - // User params. - openOrders: openOrdersB.publicKey, - // Swapping from B -> USDC. - orderPayerTokenAccount: ORDERBOOK_ENV.godB, - coinWallet: ORDERBOOK_ENV.godB, - }, - to: { - market: marketA._decoded.ownAddress, - requestQueue: marketA._decoded.requestQueue, - eventQueue: marketA._decoded.eventQueue, - bids: marketA._decoded.bids, - asks: marketA._decoded.asks, - coinVault: marketA._decoded.baseVault, - pcVault: marketA._decoded.quoteVault, - vaultSigner: marketAVaultSigner, - // User params. - openOrders: openOrdersA.publicKey, - // Swapping from USDC -> A. - orderPayerTokenAccount: ORDERBOOK_ENV.godUsdc, - coinWallet: ORDERBOOK_ENV.godA, - }, - pcWallet: ORDERBOOK_ENV.godUsdc, - authority: program.provider.wallet.publicKey, - dexProgram: utils.DEX_PID, - tokenProgram: TOKEN_PROGRAM_ID, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - } - ); - } - ); - - // TODO: calculate this dynamically from the swap amount. - assert.strictEqual(tokenAChange, 22.6); - assert.strictEqual(tokenBChange, -swapAmount); - assert.strictEqual(usdcChange, 0); - }); -}); - -// Side rust enum used for the program's RPC API. -const Side = { - Bid: { bid: {} }, - Ask: { ask: {} }, -}; - -// Executes a closure. Returning the change in balances from before and after -// its execution. -async function withBalanceChange(provider, addrs, fn) { - const beforeBalances = []; - for (let k = 0; k < addrs.length; k += 1) { - beforeBalances.push( - (await serumCmn.getTokenAccount(provider, addrs[k])).amount - ); - } - - await fn(); - - const afterBalances = []; - for (let k = 0; k < addrs.length; k += 1) { - afterBalances.push( - (await serumCmn.getTokenAccount(provider, addrs[k])).amount - ); - } - - const deltas = []; - for (let k = 0; k < addrs.length; k += 1) { - deltas.push( - (afterBalances[k].toNumber() - beforeBalances[k].toNumber()) / 10 ** 6 - ); - } - return deltas; -} diff --git a/tests/swap/tests/utils/index.js b/tests/swap/tests/utils/index.js deleted file mode 100644 index 37503051..00000000 --- a/tests/swap/tests/utils/index.js +++ /dev/null @@ -1,513 +0,0 @@ -// Boilerplate utils to bootstrap an orderbook for testing on a localnet. -// not super relevant to the point of the example, though may be useful to -// include into your own workspace for testing. -// -// TODO: Modernize all these apis. This is all quite clunky. - -const Token = require("@solana/spl-token").Token; -const TOKEN_PROGRAM_ID = require("@solana/spl-token").TOKEN_PROGRAM_ID; -const TokenInstructions = require("@project-serum/serum").TokenInstructions; -const Market = require("@project-serum/serum").Market; -const DexInstructions = require("@project-serum/serum").DexInstructions; -const web3 = require("@project-serum/anchor").web3; -const Connection = web3.Connection; -const BN = require("@project-serum/anchor").BN; -const serumCmn = require("@project-serum/common"); -const Account = web3.Account; -const Transaction = web3.Transaction; -const PublicKey = web3.PublicKey; -const SystemProgram = web3.SystemProgram; -const DEX_PID = new PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"); - -async function setupTwoMarkets({ provider }) { - // Setup mints with initial tokens owned by the provider. - const decimals = 6; - const [MINT_A, GOD_A] = await serumCmn.createMintAndVault( - provider, - new BN(1000000000000000), - undefined, - decimals - ); - const [MINT_B, GOD_B] = await serumCmn.createMintAndVault( - provider, - new BN(1000000000000000), - undefined, - decimals - ); - const [USDC, GOD_USDC] = await serumCmn.createMintAndVault( - provider, - new BN(1000000000000000), - undefined, - decimals - ); - - // Create a funded account to act as market maker. - const amount = 100000 * 10 ** decimals; - const marketMaker = await fundAccount({ - provider, - mints: [ - { god: GOD_A, mint: MINT_A, amount, decimals }, - { god: GOD_B, mint: MINT_B, amount, decimals }, - { god: GOD_USDC, mint: USDC, amount, decimals }, - ], - }); - - // Setup A/USDC and B/USDC markets with resting orders. - const asks = [ - [6.041, 7.8], - [6.051, 72.3], - [6.055, 5.4], - [6.067, 15.7], - [6.077, 390.0], - [6.09, 24.0], - [6.11, 36.3], - [6.133, 300.0], - [6.167, 687.8], - ]; - const bids = [ - [6.004, 8.5], - [5.995, 12.9], - [5.987, 6.2], - [5.978, 15.3], - [5.965, 82.8], - [5.961, 25.4], - ]; - - MARKET_A_USDC = await setupMarket({ - baseMint: MINT_A, - quoteMint: USDC, - marketMaker: { - account: marketMaker.account, - baseToken: marketMaker.tokens[MINT_A.toString()], - quoteToken: marketMaker.tokens[USDC.toString()], - }, - bids, - asks, - provider, - }); - MARKET_B_USDC = await setupMarket({ - baseMint: MINT_B, - quoteMint: USDC, - marketMaker: { - account: marketMaker.account, - baseToken: marketMaker.tokens[MINT_B.toString()], - quoteToken: marketMaker.tokens[USDC.toString()], - }, - bids, - asks, - provider, - }); - - return { - marketA: MARKET_A_USDC, - marketB: MARKET_B_USDC, - marketMaker, - mintA: MINT_A, - mintB: MINT_B, - usdc: USDC, - godA: GOD_A, - godB: GOD_B, - godUsdc: GOD_USDC, - }; -} - -// Creates everything needed for an orderbook to be running -// -// * Mints for both the base and quote currencies. -// * Lists the market. -// * Provides resting orders on the market. -// -// Returns a client that can be used to interact with the market -// (and some other data, e.g., the mints and market maker account). -async function initOrderbook({ provider, bids, asks }) { - if (!bids || !asks) { - asks = [ - [6.041, 7.8], - [6.051, 72.3], - [6.055, 5.4], - [6.067, 15.7], - [6.077, 390.0], - [6.09, 24.0], - [6.11, 36.3], - [6.133, 300.0], - [6.167, 687.8], - ]; - bids = [ - [6.004, 8.5], - [5.995, 12.9], - [5.987, 6.2], - [5.978, 15.3], - [5.965, 82.8], - [5.961, 25.4], - ]; - } - // Create base and quote currency mints. - const decimals = 6; - const [MINT_A, GOD_A] = await serumCmn.createMintAndVault( - provider, - new BN(1000000000000000), - undefined, - decimals - ); - const [USDC, GOD_USDC] = await serumCmn.createMintAndVault( - provider, - new BN(1000000000000000), - undefined, - decimals - ); - - // Create a funded account to act as market maker. - const amount = 100000 * 10 ** decimals; - const marketMaker = await fundAccount({ - provider, - mints: [ - { god: GOD_A, mint: MINT_A, amount, decimals }, - { god: GOD_USDC, mint: USDC, amount, decimals }, - ], - }); - - marketClient = await setupMarket({ - baseMint: MINT_A, - quoteMint: USDC, - marketMaker: { - account: marketMaker.account, - baseToken: marketMaker.tokens[MINT_A.toString()], - quoteToken: marketMaker.tokens[USDC.toString()], - }, - bids, - asks, - provider, - }); - - return { - marketClient, - baseMint: MINT_A, - quoteMint: USDC, - marketMaker, - }; -} - -async function fundAccount({ provider, mints }) { - const MARKET_MAKER = new Account(); - - const marketMaker = { - tokens: {}, - account: MARKET_MAKER, - }; - - // Transfer lamports to market maker. - await provider.sendAndConfirm( - (() => { - const tx = new Transaction(); - tx.add( - SystemProgram.transfer({ - fromPubkey: provider.wallet.publicKey, - toPubkey: MARKET_MAKER.publicKey, - lamports: 100000000000, - }) - ); - return tx; - })() - ); - - // Transfer SPL tokens to the market maker. - for (let k = 0; k < mints.length; k += 1) { - const { mint, god, amount, decimals } = mints[k]; - let MINT_A = mint; - let GOD_A = god; - // Setup token accounts owned by the market maker. - const mintAClient = new Token( - provider.connection, - MINT_A, - TOKEN_PROGRAM_ID, - provider.wallet.payer // node only - ); - const marketMakerTokenA = await mintAClient.createAccount( - MARKET_MAKER.publicKey - ); - - await provider.sendAndConfirm( - (() => { - const tx = new Transaction(); - tx.add( - Token.createTransferCheckedInstruction( - TOKEN_PROGRAM_ID, - GOD_A, - MINT_A, - marketMakerTokenA, - provider.wallet.publicKey, - [], - amount, - decimals - ) - ); - return tx; - })() - ); - - marketMaker.tokens[mint.toString()] = marketMakerTokenA; - } - - return marketMaker; -} - -async function setupMarket({ - provider, - marketMaker, - baseMint, - quoteMint, - bids, - asks, -}) { - const marketAPublicKey = await listMarket({ - connection: provider.connection, - wallet: provider.wallet, - baseMint: baseMint, - quoteMint: quoteMint, - baseLotSize: 100000, - quoteLotSize: 100, - dexProgramId: DEX_PID, - feeRateBps: 0, - }); - const MARKET_A_USDC = await Market.load( - provider.connection, - marketAPublicKey, - { commitment: "processed" }, - DEX_PID - ); - for (let k = 0; k < asks.length; k += 1) { - let ask = asks[k]; - const { transaction, signers } = - await MARKET_A_USDC.makePlaceOrderTransaction(provider.connection, { - owner: marketMaker.account, - payer: marketMaker.baseToken, - side: "sell", - price: ask[0], - size: ask[1], - orderType: "postOnly", - clientId: undefined, - openOrdersAddressKey: undefined, - openOrdersAccount: undefined, - feeDiscountPubkey: null, - selfTradeBehavior: "abortTransaction", - }); - await provider.sendAndConfirm( - transaction, - signers.concat(marketMaker.account) - ); - } - - for (let k = 0; k < bids.length; k += 1) { - let bid = bids[k]; - const { transaction, signers } = - await MARKET_A_USDC.makePlaceOrderTransaction(provider.connection, { - owner: marketMaker.account, - payer: marketMaker.quoteToken, - side: "buy", - price: bid[0], - size: bid[1], - orderType: "postOnly", - clientId: undefined, - openOrdersAddressKey: undefined, - openOrdersAccount: undefined, - feeDiscountPubkey: null, - selfTradeBehavior: "abortTransaction", - }); - await provider.sendAndConfirm( - transaction, - signers.concat(marketMaker.account) - ); - } - - return MARKET_A_USDC; -} - -async function listMarket({ - connection, - wallet, - baseMint, - quoteMint, - baseLotSize, - quoteLotSize, - dexProgramId, - feeRateBps, -}) { - const market = new Account(); - const requestQueue = new Account(); - const eventQueue = new Account(); - const bids = new Account(); - const asks = new Account(); - const baseVault = new Account(); - const quoteVault = new Account(); - const quoteDustThreshold = new BN(100); - - const [vaultOwner, vaultSignerNonce] = await getVaultOwnerAndNonce( - market.publicKey, - dexProgramId - ); - - const tx1 = new Transaction(); - tx1.add( - SystemProgram.createAccount({ - fromPubkey: wallet.publicKey, - newAccountPubkey: baseVault.publicKey, - lamports: await connection.getMinimumBalanceForRentExemption(165), - space: 165, - programId: TOKEN_PROGRAM_ID, - }), - SystemProgram.createAccount({ - fromPubkey: wallet.publicKey, - newAccountPubkey: quoteVault.publicKey, - lamports: await connection.getMinimumBalanceForRentExemption(165), - space: 165, - programId: TOKEN_PROGRAM_ID, - }), - TokenInstructions.initializeAccount({ - account: baseVault.publicKey, - mint: baseMint, - owner: vaultOwner, - }), - TokenInstructions.initializeAccount({ - account: quoteVault.publicKey, - mint: quoteMint, - owner: vaultOwner, - }) - ); - - const tx2 = new Transaction(); - tx2.add( - SystemProgram.createAccount({ - fromPubkey: wallet.publicKey, - newAccountPubkey: market.publicKey, - lamports: await connection.getMinimumBalanceForRentExemption( - Market.getLayout(dexProgramId).span - ), - space: Market.getLayout(dexProgramId).span, - programId: dexProgramId, - }), - SystemProgram.createAccount({ - fromPubkey: wallet.publicKey, - newAccountPubkey: requestQueue.publicKey, - lamports: await connection.getMinimumBalanceForRentExemption(5120 + 12), - space: 5120 + 12, - programId: dexProgramId, - }), - SystemProgram.createAccount({ - fromPubkey: wallet.publicKey, - newAccountPubkey: eventQueue.publicKey, - lamports: await connection.getMinimumBalanceForRentExemption(262144 + 12), - space: 262144 + 12, - programId: dexProgramId, - }), - SystemProgram.createAccount({ - fromPubkey: wallet.publicKey, - newAccountPubkey: bids.publicKey, - lamports: await connection.getMinimumBalanceForRentExemption(65536 + 12), - space: 65536 + 12, - programId: dexProgramId, - }), - SystemProgram.createAccount({ - fromPubkey: wallet.publicKey, - newAccountPubkey: asks.publicKey, - lamports: await connection.getMinimumBalanceForRentExemption(65536 + 12), - space: 65536 + 12, - programId: dexProgramId, - }), - DexInstructions.initializeMarket({ - market: market.publicKey, - requestQueue: requestQueue.publicKey, - eventQueue: eventQueue.publicKey, - bids: bids.publicKey, - asks: asks.publicKey, - baseVault: baseVault.publicKey, - quoteVault: quoteVault.publicKey, - baseMint, - quoteMint, - baseLotSize: new BN(baseLotSize), - quoteLotSize: new BN(quoteLotSize), - feeRateBps, - vaultSignerNonce, - quoteDustThreshold, - programId: dexProgramId, - }) - ); - - const signedTransactions = await signTransactions({ - transactionsAndSigners: [ - { transaction: tx1, signers: [baseVault, quoteVault] }, - { - transaction: tx2, - signers: [market, requestQueue, eventQueue, bids, asks], - }, - ], - wallet, - connection, - }); - for (let signedTransaction of signedTransactions) { - await sendAndConfirmRawTransaction( - connection, - signedTransaction.serialize() - ); - } - const acc = await connection.getAccountInfo(market.publicKey); - - return market.publicKey; -} - -async function signTransactions({ - transactionsAndSigners, - wallet, - connection, -}) { - const blockhash = (await connection.getRecentBlockhash("finalized")) - .blockhash; - transactionsAndSigners.forEach(({ transaction, signers = [] }) => { - transaction.recentBlockhash = blockhash; - transaction.setSigners( - wallet.publicKey, - ...signers.map((s) => s.publicKey) - ); - if (signers.length > 0) { - transaction.partialSign(...signers); - } - }); - return await wallet.signAllTransactions( - transactionsAndSigners.map(({ transaction }) => transaction) - ); -} - -async function sendAndConfirmRawTransaction( - connection, - raw, - commitment = "processed" -) { - let tx = await connection.sendRawTransaction(raw, { - skipPreflight: true, - }); - return await connection.confirmTransaction(tx, commitment); -} - -async function getVaultOwnerAndNonce(marketPublicKey, dexProgramId = DEX_PID) { - const nonce = new BN(0); - while (nonce.toNumber() < 255) { - try { - const vaultOwner = await PublicKey.createProgramAddress( - [marketPublicKey.toBuffer(), nonce.toArrayLike(Buffer, "le", 8)], - dexProgramId - ); - return [vaultOwner, nonce]; - } catch (e) { - nonce.iaddn(1); - } - } - throw new Error("Unable to find nonce"); -} - -module.exports = { - fundAccount, - setupMarket, - initOrderbook, - setupTwoMarkets, - DEX_PID, - getVaultOwnerAndNonce, -}; diff --git a/tests/system-accounts/Anchor.toml b/tests/system-accounts/Anchor.toml deleted file mode 100644 index c782253e..00000000 --- a/tests/system-accounts/Anchor.toml +++ /dev/null @@ -1,9 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -system_accounts = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[scripts] -test = "yarn run mocha -t 1000000 tests/" diff --git a/tests/system-accounts/Cargo.toml b/tests/system-accounts/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/system-accounts/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/system-accounts/package.json b/tests/system-accounts/package.json deleted file mode 100644 index ed7c82cb..00000000 --- a/tests/system-accounts/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "system-accounts", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/system-accounts/programs/system-accounts/Cargo.toml b/tests/system-accounts/programs/system-accounts/Cargo.toml deleted file mode 100644 index de8235fc..00000000 --- a/tests/system-accounts/programs/system-accounts/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "system-accounts" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "system_accounts" - -[features] -no-entrypoint = [] -cpi = ["no-entrypoint"] - -[dependencies] -anchor-lang = { path = "../../../../lang" } diff --git a/tests/system-accounts/programs/system-accounts/Xargo.toml b/tests/system-accounts/programs/system-accounts/Xargo.toml deleted file mode 100644 index 475fb71e..00000000 --- a/tests/system-accounts/programs/system-accounts/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] diff --git a/tests/system-accounts/programs/system-accounts/src/lib.rs b/tests/system-accounts/programs/system-accounts/src/lib.rs deleted file mode 100644 index 36ffd160..00000000 --- a/tests/system-accounts/programs/system-accounts/src/lib.rs +++ /dev/null @@ -1,18 +0,0 @@ -use anchor_lang::prelude::*; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -mod system_accounts { - use super::*; - - pub fn initialize(_ctx: Context) -> Result<()> { - Ok(()) - } -} - -#[derive(Accounts)] -pub struct Initialize<'info> { - pub authority: Signer<'info>, - pub wallet: SystemAccount<'info>, -} diff --git a/tests/system-accounts/tests/system-accounts.js b/tests/system-accounts/tests/system-accounts.js deleted file mode 100644 index 5cd2f60a..00000000 --- a/tests/system-accounts/tests/system-accounts.js +++ /dev/null @@ -1,59 +0,0 @@ -const anchor = require("@project-serum/anchor"); -const splToken = require("@solana/spl-token"); -const { assert } = require("chai"); - -describe("system_accounts", () => { - anchor.setProvider(anchor.AnchorProvider.local()); - const program = anchor.workspace.SystemAccounts; - const authority = program.provider.wallet.payer; - const wallet = anchor.web3.Keypair.generate(); - - it("Is initialized!", async () => { - const tx = await program.rpc.initialize({ - accounts: { - authority: authority.publicKey, - wallet: wallet.publicKey, - }, - signers: [authority], - }); - - console.log("Your transaction signature", tx); - }); - - it("Emits an AccountNotSystemOwned error", async () => { - const mint = await splToken.Token.createMint( - program.provider.connection, - authority, - authority.publicKey, - null, - 9, - splToken.TOKEN_PROGRAM_ID - ); - - const tokenAccount = await mint.createAssociatedTokenAccount( - wallet.publicKey - ); - - await mint.mintTo( - tokenAccount, - authority.publicKey, - [], - 1 * anchor.web3.LAMPORTS_PER_SOL - ); - - try { - await program.rpc.initialize({ - accounts: { - authority: authority.publicKey, - wallet: tokenAccount, - }, - signers: [authority], - }); - assert.ok(false); - } catch (err) { - const errMsg = "The given account is not owned by the system program"; - assert.strictEqual(err.error.errorMessage, errMsg); - assert.strictEqual(err.error.errorCode.number, 3011); - } - }); -}); diff --git a/tests/sysvars/Anchor.toml b/tests/sysvars/Anchor.toml deleted file mode 100644 index 9f766242..00000000 --- a/tests/sysvars/Anchor.toml +++ /dev/null @@ -1,9 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -sysvars = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[scripts] -test = "yarn run mocha -t 1000000 tests/" diff --git a/tests/sysvars/Cargo.toml b/tests/sysvars/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/sysvars/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/sysvars/package.json b/tests/sysvars/package.json deleted file mode 100644 index 7e41b8d1..00000000 --- a/tests/sysvars/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "sysvars", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/sysvars/programs/sysvars/Cargo.toml b/tests/sysvars/programs/sysvars/Cargo.toml deleted file mode 100644 index 43a178a0..00000000 --- a/tests/sysvars/programs/sysvars/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "sysvars" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "sysvars" - -[features] -no-entrypoint = [] -cpi = ["no-entrypoint"] - -[dependencies] -anchor-lang = { path = "../../../../lang" } -anchor-spl = { path = "../../../../spl" } diff --git a/tests/sysvars/programs/sysvars/Xargo.toml b/tests/sysvars/programs/sysvars/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/sysvars/programs/sysvars/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/sysvars/programs/sysvars/src/lib.rs b/tests/sysvars/programs/sysvars/src/lib.rs deleted file mode 100644 index c6dca4e3..00000000 --- a/tests/sysvars/programs/sysvars/src/lib.rs +++ /dev/null @@ -1,18 +0,0 @@ -use anchor_lang::prelude::*; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -mod sysvars { - use super::*; - pub fn sysvars(_ctx: Context) -> Result<()> { - Ok(()) - } -} - -#[derive(Accounts)] -pub struct Sysvars<'info> { - pub clock: Sysvar<'info, Clock>, - pub rent: Sysvar<'info, Rent>, - pub stake_history: Sysvar<'info, StakeHistory>, -} diff --git a/tests/sysvars/tests/sysvars.js b/tests/sysvars/tests/sysvars.js deleted file mode 100644 index c69afa97..00000000 --- a/tests/sysvars/tests/sysvars.js +++ /dev/null @@ -1,38 +0,0 @@ -const anchor = require("@project-serum/anchor"); -const { assert } = require("chai"); - -describe("sysvars", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.local()); - const program = anchor.workspace.Sysvars; - - it("Is initialized!", async () => { - const tx = await program.methods - .sysvars() - .accounts({ - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - stakeHistory: anchor.web3.SYSVAR_STAKE_HISTORY_PUBKEY, - }) - .rpc(); - console.log("Your transaction signature", tx); - }); - - it("Fails when the wrong pubkeys are provided", async () => { - try { - await program.methods - .sysvars() - .accounts({ - clock: anchor.web3.SYSVAR_CLOCK_PUBKEY, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - stakeHistory: anchor.web3.SYSVAR_REWARDS_PUBKEY, - }) - .rpc(); - assert.ok(false); - } catch (err) { - const errMsg = "The given public key does not match the required sysvar"; - assert.strictEqual(err.error.errorMessage, errMsg); - assert.strictEqual(err.error.errorCode.number, 3015); - } - }); -}); diff --git a/tests/tictactoe/Anchor.toml b/tests/tictactoe/Anchor.toml deleted file mode 100644 index 12a3b6ad..00000000 --- a/tests/tictactoe/Anchor.toml +++ /dev/null @@ -1,9 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -tictactoe = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[scripts] -test = "yarn run mocha -t 1000000 tests/" diff --git a/tests/tictactoe/Cargo.toml b/tests/tictactoe/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/tictactoe/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/tictactoe/migrations/deploy.js b/tests/tictactoe/migrations/deploy.js deleted file mode 100644 index 5e3df0dc..00000000 --- a/tests/tictactoe/migrations/deploy.js +++ /dev/null @@ -1,12 +0,0 @@ -// Migrations are an early feature. Currently, they're nothing more than this -// single deploy script that's invoked from the CLI, injecting a provider -// configured from the workspace's Anchor.toml. - -const anchor = require("@project-serum/anchor"); - -module.exports = async function (provider) { - // Configure client to use the provider. - anchor.setProvider(provider); - - // Add your deploy script here. -}; diff --git a/tests/tictactoe/package.json b/tests/tictactoe/package.json deleted file mode 100644 index dd7fefc0..00000000 --- a/tests/tictactoe/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "tictactoe", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/tictactoe/programs/tictactoe/Cargo.toml b/tests/tictactoe/programs/tictactoe/Cargo.toml deleted file mode 100644 index 445607fd..00000000 --- a/tests/tictactoe/programs/tictactoe/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "tictactoe" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "tictactoe" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } -anchor-spl = { path = "../../../../spl" } diff --git a/tests/tictactoe/programs/tictactoe/Xargo.toml b/tests/tictactoe/programs/tictactoe/Xargo.toml deleted file mode 100644 index 475fb71e..00000000 --- a/tests/tictactoe/programs/tictactoe/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] diff --git a/tests/tictactoe/programs/tictactoe/src/lib.rs b/tests/tictactoe/programs/tictactoe/src/lib.rs deleted file mode 100644 index 5df4cee9..00000000 --- a/tests/tictactoe/programs/tictactoe/src/lib.rs +++ /dev/null @@ -1,213 +0,0 @@ -use anchor_lang::prelude::*; - -const BOARD_ITEM_FREE: u8 = 0; // Free slot -const BOARD_ITEM_X: u8 = 1; // Player X -const BOARD_ITEM_O: u8 = 2; // Player O - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -/// Game State -/// 0 - Waiting -/// 1 - XMove -/// 2 - OMove -/// 3 - XWon -/// 4 - OWon -/// 5 - Draw - -#[program] -pub mod tictactoe { - use super::*; - - pub fn initialize_dashboard(ctx: Context) -> Result<()> { - let dashboard = &mut ctx.accounts.dashboard; - dashboard.game_count = 0; - dashboard.address = *dashboard.to_account_info().key; - Ok(()) - } - - pub fn initialize(ctx: Context) -> Result<()> { - let dashboard = &mut ctx.accounts.dashboard; - let game = &mut ctx.accounts.game; - dashboard.game_count = dashboard.game_count + 1; - dashboard.latest_game = *game.to_account_info().key; - game.player_x = *ctx.accounts.player_x.key; - Ok(()) - } - - pub fn player_join(ctx: Context) -> Result<()> { - let game = &mut ctx.accounts.game; - game.player_o = *ctx.accounts.player_o.key; - game.game_state = 1; - Ok(()) - } - - #[access_control(Playermove::accounts(&ctx, x_or_o, player_move))] - pub fn player_move(ctx: Context, x_or_o: u8, player_move: u8) -> Result<()> { - let game = &mut ctx.accounts.game; - game.board[player_move as usize] = x_or_o; - game.status(x_or_o); - Ok(()) - } - - pub fn status(ctx: Context) -> Result<()> { - Ok(()) - } -} - -#[derive(Accounts)] -pub struct Status<'info> { - dashboard: Account<'info, Dashboard>, - game: Account<'info, Game>, -} - -#[derive(Accounts)] -pub struct Initializedashboard<'info> { - #[account(zero)] - dashboard: Account<'info, Dashboard>, - authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct Initialize<'info> { - player_x: Signer<'info>, - #[account(mut)] - dashboard: Account<'info, Dashboard>, - #[account(zero)] - game: Account<'info, Game>, -} - -#[derive(Accounts)] -pub struct Playerjoin<'info> { - player_o: Signer<'info>, - #[account(mut, constraint = game.game_state != 0 && game.player_x != Pubkey::default())] - game: Account<'info, Game>, -} - -#[derive(Accounts)] -pub struct Playermove<'info> { - player: Signer<'info>, - #[account(mut)] - game: Account<'info, Game>, -} - -impl<'info> Playermove<'info> { - pub fn accounts(ctx: &Context, x_or_o: u8, player_move: u8) -> Result<()> { - if ctx.accounts.game.board[player_move as usize] != 0 { - return Err(ErrorCode::Illegalmove.into()); - } - if x_or_o == BOARD_ITEM_X { - return Playermove::player_x_checks(ctx); - } else if x_or_o == BOARD_ITEM_O { - return Playermove::player_o_checks(ctx); - } else { - return Err(ErrorCode::UnexpectedValue.into()); - } - } - - pub fn player_x_checks(ctx: &Context) -> Result<()> { - if ctx.accounts.game.player_x != *ctx.accounts.player.key { - return Err(ErrorCode::Unauthorized.into()); - } - if ctx.accounts.game.game_state != 1 { - return Err(ErrorCode::Gamestate.into()); - } - Ok(()) - } - - pub fn player_o_checks(ctx: &Context) -> Result<()> { - if ctx.accounts.game.player_o != *ctx.accounts.player.key { - return Err(ErrorCode::Unauthorized.into()); - } - if ctx.accounts.game.game_state != 2 { - return Err(ErrorCode::Gamestate.into()); - } - Ok(()) - } -} - -#[account] -pub struct Dashboard { - game_count: u64, - latest_game: Pubkey, - address: Pubkey, -} - -#[account] -#[derive(Default)] -pub struct Game { - keep_alive: [u64; 2], - player_x: Pubkey, - player_o: Pubkey, - game_state: u8, - board: [u8; 9], -} - -#[event] -pub struct GameStatus { - keep_alive: [u64; 2], - player_x: Pubkey, - player_o: Pubkey, - game_state: u8, - board: [u8; 9], -} - -impl From for Game { - fn from(status: GameStatus) -> Self { - Self { - keep_alive: status.keep_alive, - player_x: status.player_x, - player_o: status.player_o, - game_state: status.game_state, - board: status.board, - } - } -} - -impl Game { - pub fn status(self: &mut Game, x_or_o: u8) { - let winner = - // Check rows. - Game::same(x_or_o, &self.board[0..3]) - || Game::same(x_or_o, &self.board[3..6]) - || Game::same(x_or_o, &self.board[6..9]) - // Check columns. - || Game::same(x_or_o, &[self.board[0], self.board[3], self.board[6]]) - || Game::same(x_or_o, &[self.board[1], self.board[4], self.board[7]]) - || Game::same(x_or_o, &[self.board[2], self.board[5], self.board[8]]) - // Check both diagonals. - || Game::same(x_or_o, &[self.board[0], self.board[4], self.board[8]]) - || Game::same(x_or_o, &[self.board[2], self.board[4], self.board[6]]); - - if winner { - self.game_state = x_or_o + 2; - } else if self.board.iter().all(|&p| p != BOARD_ITEM_FREE) { - self.game_state = 5; - } else { - if x_or_o == BOARD_ITEM_X { - self.game_state = 2; - } else { - self.game_state = 1; - } - } - } - - pub fn same(x_or_o: u8, triple: &[u8]) -> bool { - triple.iter().all(|&i| i == x_or_o) - } -} - -#[error] -pub enum ErrorCode { - #[msg("You are not authorized to perform this action.")] - Unauthorized, - #[msg("Wrong dashboard")] - Wrongdashboard, - #[msg("Wrong expected state")] - Gamestate, - #[msg("Dashboard already initialized")] - Initialized, - #[msg("Unexpected value")] - UnexpectedValue, - #[msg("Illegal move")] - Illegalmove, -} diff --git a/tests/tictactoe/tests/tictactoe.js b/tests/tictactoe/tests/tictactoe.js deleted file mode 100644 index 5c4147a3..00000000 --- a/tests/tictactoe/tests/tictactoe.js +++ /dev/null @@ -1,157 +0,0 @@ -const anchor = require("@project-serum/anchor"); - -describe("tictactoe", () => { - anchor.setProvider(anchor.AnchorProvider.env()); - const program = anchor.workspace.Tictactoe; - let dashboard = anchor.web3.Keypair.generate(); - let game = anchor.web3.Keypair.generate(); - let player_o = anchor.web3.Keypair.generate(); - - it("Initialize Dashboard", async () => { - const tx = await program.rpc.initializeDashboard({ - accounts: { - authority: program.provider.wallet.publicKey, - dashboard: dashboard.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [dashboard], - instructions: [ - await program.account.dashboard.createInstruction(dashboard), - ], - }); - - console.log("transaction: ", tx); - }); - - it("Initialize Game", async () => { - const tx = await program.rpc.initialize({ - accounts: { - playerX: program.provider.wallet.publicKey, - dashboard: dashboard.publicKey, - game: game.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - signers: [game], - instructions: [await program.account.game.createInstruction(game)], - }); - - console.log("transaction: ", tx); - }); - - it("Player O joins", async () => { - const tx = await program.rpc.playerJoin({ - accounts: { - playerO: player_o.publicKey, - game: game.publicKey, - }, - signers: [player_o], - }); - - console.log("transaction: ", tx); - }); - - it("Player x plays", async () => { - const tx = await program.rpc.playerMove(1, 0, { - accounts: { - player: program.provider.wallet.publicKey, - game: game.publicKey, - }, - }); - console.log("transaction: ", tx); - }); - - it("Player o plays", async () => { - const tx = await program.rpc.playerMove(2, 1, { - accounts: { - player: player_o.publicKey, - game: game.publicKey, - }, - signers: [player_o], - }); - console.log("transaction: ", tx); - }); - - it("Player x plays", async () => { - const tx = await program.rpc.playerMove(1, 3, { - accounts: { - player: program.provider.wallet.publicKey, - game: game.publicKey, - }, - }); - console.log("transaction: ", tx); - }); - - it("Player o plays", async () => { - const tx = await program.rpc.playerMove(2, 6, { - accounts: { - player: player_o.publicKey, - game: game.publicKey, - }, - signers: [player_o], - }); - console.log("transaction: ", tx); - }); - - it("Player x plays", async () => { - const tx = await program.rpc.playerMove(1, 2, { - accounts: { - player: program.provider.wallet.publicKey, - game: game.publicKey, - }, - }); - console.log("transaction: ", tx); - }); - - it("Player o plays", async () => { - const tx = await program.rpc.playerMove(2, 4, { - accounts: { - player: player_o.publicKey, - game: game.publicKey, - }, - signers: [player_o], - }); - console.log("transaction: ", tx); - }); - - it("Player x plays", async () => { - const tx = await program.rpc.playerMove(1, 5, { - accounts: { - player: program.provider.wallet.publicKey, - game: game.publicKey, - }, - }); - console.log("transaction: ", tx); - }); - - it("Player o plays", async () => { - const tx = await program.rpc.playerMove(2, 8, { - accounts: { - player: player_o.publicKey, - game: game.publicKey, - }, - signers: [player_o], - }); - console.log("transaction: ", tx); - }); - - it("Player x plays", async () => { - const tx = await program.rpc.playerMove(1, 7, { - accounts: { - player: program.provider.wallet.publicKey, - game: game.publicKey, - }, - }); - console.log("transaction: ", tx); - }); - - it("Status", async () => { - const tx = await program.rpc.status({ - accounts: { - dashboard: dashboard.publicKey, - game: game.publicKey, - }, - }); - - console.log("transaction: ", tx); - }); -}); diff --git a/ts/tests/transaction.spec.ts b/tests/transaction.spec.ts similarity index 100% rename from ts/tests/transaction.spec.ts rename to tests/transaction.spec.ts diff --git a/tests/typescript/Anchor.toml b/tests/typescript/Anchor.toml deleted file mode 100644 index 89853bfc..00000000 --- a/tests/typescript/Anchor.toml +++ /dev/null @@ -1,12 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[programs.localnet] -typescript = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[workspace] -members = ["programs/typescript"] - -[scripts] -test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" diff --git a/tests/typescript/Cargo.toml b/tests/typescript/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/typescript/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/typescript/migrations/deploy.ts b/tests/typescript/migrations/deploy.ts deleted file mode 100644 index 53e1252d..00000000 --- a/tests/typescript/migrations/deploy.ts +++ /dev/null @@ -1,22 +0,0 @@ -// Migrations are an early feature. Currently, they're nothing more than this -// single deploy script that's invoked from the CLI, injecting a provider -// configured from the workspace's Anchor.toml. - -const anchor = require("@project-serum/anchor"); - -module.exports = async function (provider) { - // Configure client to use the provider. - anchor.setProvider(provider); - - // Add your deploy script here. - async function deployAsync(exampleString: string): Promise { - return new Promise((resolve) => { - setTimeout(() => { - console.log(exampleString); - resolve(); - }, 2000); - }); - } - - await deployAsync("Typescript migration example complete."); -}; diff --git a/tests/typescript/package.json b/tests/typescript/package.json deleted file mode 100644 index aa3f555e..00000000 --- a/tests/typescript/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "typescript-example", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/typescript/programs/shared/Cargo.toml b/tests/typescript/programs/shared/Cargo.toml deleted file mode 100644 index c0dff4a0..00000000 --- a/tests/typescript/programs/shared/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "shared" -version = "0.1.0" -rust-version = "1.56" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/tests/typescript/programs/shared/src/lib.rs b/tests/typescript/programs/shared/src/lib.rs deleted file mode 100644 index 31e1bb20..00000000 --- a/tests/typescript/programs/shared/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} diff --git a/tests/typescript/programs/typescript/Cargo.toml b/tests/typescript/programs/typescript/Cargo.toml deleted file mode 100644 index 4d043312..00000000 --- a/tests/typescript/programs/typescript/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "typescript" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "typescript" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } diff --git a/tests/typescript/programs/typescript/Xargo.toml b/tests/typescript/programs/typescript/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/typescript/programs/typescript/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/typescript/programs/typescript/src/lib.rs b/tests/typescript/programs/typescript/src/lib.rs deleted file mode 100644 index 63fd8e6e..00000000 --- a/tests/typescript/programs/typescript/src/lib.rs +++ /dev/null @@ -1,17 +0,0 @@ -//! The typescript example serves to show how one would setup an Anchor -//! workspace with TypeScript tests and migrations. - -use anchor_lang::prelude::*; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -pub mod typescript { - use super::*; - pub fn initialize(ctx: Context) -> Result<()> { - Ok(()) - } -} - -#[derive(Accounts)] -pub struct Initialize {} diff --git a/tests/typescript/tests/typescript.spec.ts b/tests/typescript/tests/typescript.spec.ts deleted file mode 100644 index d4c34f50..00000000 --- a/tests/typescript/tests/typescript.spec.ts +++ /dev/null @@ -1,13 +0,0 @@ -import * as anchor from "@project-serum/anchor"; - -describe("typescript", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.env()); - - it("Is initialized!", async () => { - // Add your test here. - const program = anchor.workspace.Typescript; - const tx = await program.rpc.initialize(); - console.log("Your transaction signature", tx); - }); -}); diff --git a/tests/typescript/tsconfig.json b/tests/typescript/tsconfig.json deleted file mode 100644 index cd5d2e3d..00000000 --- a/tests/typescript/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "types": ["mocha", "chai"], - "typeRoots": ["./node_modules/@types"], - "lib": ["es2015"], - "module": "commonjs", - "target": "es6", - "esModuleInterop": true - } -} diff --git a/tests/validator-clone/Anchor.toml b/tests/validator-clone/Anchor.toml deleted file mode 100644 index 11a76652..00000000 --- a/tests/validator-clone/Anchor.toml +++ /dev/null @@ -1,38 +0,0 @@ -[features] -seeds = false -[programs.localnet] -validator_clone = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[registry] -url = "https://anchor.projectserum.com" - -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[scripts] -test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts" - -[test] -startup_wait = 20000 - -[test.validator] -url = "https://api.mainnet-beta.solana.com" - -[[test.validator.clone]] -address = "AqH29mZfQFgRpfwaPoTMWSKJ5kqauoc1FwVBRksZyQrt" - -[[test.validator.clone]] -address = "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" - -[[test.validator.clone]] -address = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" - -[[test.validator.clone]] -address = "So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo" - -[[test.validator.clone]] -address = "mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68" - -[[test.validator.clone]] -address = "8DKwAVrCEVStDYNPCsmxHtUj8LH9oXNtkVRrBfpNKvhp" diff --git a/tests/validator-clone/Cargo.toml b/tests/validator-clone/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/validator-clone/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/validator-clone/package.json b/tests/validator-clone/package.json deleted file mode 100644 index 6bd2fd50..00000000 --- a/tests/validator-clone/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "validator-clone", - "version": "0.22.0", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor run test-with-build" - } -} diff --git a/tests/validator-clone/programs/validator-clone/Cargo.toml b/tests/validator-clone/programs/validator-clone/Cargo.toml deleted file mode 100644 index d246a8c8..00000000 --- a/tests/validator-clone/programs/validator-clone/Cargo.toml +++ /dev/null @@ -1,19 +0,0 @@ -[package] -name = "validator-clone" -version = "0.1.0" -description = "Created with Anchor" -edition = "2018" - -[lib] -crate-type = ["cdylib", "lib"] -name = "validator_clone" - -[features] -no-entrypoint = [] -no-idl = [] -no-log-ix-name = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } diff --git a/tests/validator-clone/programs/validator-clone/Xargo.toml b/tests/validator-clone/programs/validator-clone/Xargo.toml deleted file mode 100644 index 475fb71e..00000000 --- a/tests/validator-clone/programs/validator-clone/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] diff --git a/tests/validator-clone/programs/validator-clone/src/lib.rs b/tests/validator-clone/programs/validator-clone/src/lib.rs deleted file mode 100644 index 97fc44a2..00000000 --- a/tests/validator-clone/programs/validator-clone/src/lib.rs +++ /dev/null @@ -1,15 +0,0 @@ -use anchor_lang::prelude::*; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -pub mod validator_clone { - use super::*; - - pub fn initialize(_ctx: Context) -> Result<()> { - Ok(()) - } -} - -#[derive(Accounts)] -pub struct Initialize {} diff --git a/tests/validator-clone/tests/validator-clone.ts b/tests/validator-clone/tests/validator-clone.ts deleted file mode 100644 index 92b2fc1f..00000000 --- a/tests/validator-clone/tests/validator-clone.ts +++ /dev/null @@ -1,70 +0,0 @@ -import * as anchor from "@project-serum/anchor"; -import { Program } from "@project-serum/anchor"; -import { assert } from "chai"; -import { ValidatorClone } from "../target/types/validator_clone"; - -describe("validator-clone", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.env()); - - const program = anchor.workspace.ValidatorClone as Program; - const connection = program.provider.connection; - - it("Cloned non-executable account", async () => { - // Metadata program upgrade authority - const account = "AqH29mZfQFgRpfwaPoTMWSKJ5kqauoc1FwVBRksZyQrt"; - const [accountInfo] = await anchor.utils.rpc.getMultipleAccounts( - connection, - [new anchor.web3.PublicKey(account)] - ); - assert.isNotNull(accountInfo, "Account " + account + " not found"); - }); - - it("Cloned bpf2-program account", async () => { - // Memo program - const account = "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"; - const [accountInfo] = await anchor.utils.rpc.getMultipleAccounts( - connection, - [new anchor.web3.PublicKey(account)] - ); - assert.isNotNull(accountInfo, "Account " + account + " not found"); - }); - - it("Cloned bpf3-program accounts and their program data", async () => { - const accounts = [ - // Metadata program - "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s", - // Metadata program executable data - "PwDiXFxQsGra4sFFTT8r1QWRMd4vfumiWC1jfWNfdYT", - // Solend program - "So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo", - // Solend program executable data - "DMCvGv1fS5rMcAvEDPDDBawPqbDRSzJh2Bo6qXCmgJkR", - ]; - const accountInfos = await anchor.utils.rpc.getMultipleAccounts( - connection, - accounts.map((acc) => new anchor.web3.PublicKey(acc)) - ); - - accountInfos.forEach((acc, i) => { - assert.isNotNull(acc, "Account " + accounts[i] + " not found"); - }); - }); - - it("Cloned bpf3-program account and its program data (both explicitly declared)", async () => { - const accounts = [ - // Mango v3 program - "mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68", - // Mango v3 program executable data - "8DKwAVrCEVStDYNPCsmxHtUj8LH9oXNtkVRrBfpNKvhp", - ]; - const accountInfos = await anchor.utils.rpc.getMultipleAccounts( - connection, - accounts.map((acc) => new anchor.web3.PublicKey(acc)) - ); - - accountInfos.forEach((acc, i) => { - assert.isNotNull(acc, "Account " + accounts[i] + " not found"); - }); - }); -}); diff --git a/tests/validator-clone/tsconfig.json b/tests/validator-clone/tsconfig.json deleted file mode 100644 index cd5d2e3d..00000000 --- a/tests/validator-clone/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "compilerOptions": { - "types": ["mocha", "chai"], - "typeRoots": ["./node_modules/@types"], - "lib": ["es2015"], - "module": "commonjs", - "target": "es6", - "esModuleInterop": true - } -} diff --git a/tests/yarn.lock b/tests/yarn.lock deleted file mode 100644 index 8e1bd756..00000000 --- a/tests/yarn.lock +++ /dev/null @@ -1,1350 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5": - version "7.16.3" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5" - integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ== - dependencies: - regenerator-runtime "^0.13.4" - -"@ethersproject/bytes@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.5.0.tgz#cb11c526de657e7b45d2e0f0246fb3b9d29a601c" - integrity sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog== - dependencies: - "@ethersproject/logger" "^5.5.0" - -"@ethersproject/logger@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.5.0.tgz#0c2caebeff98e10aefa5aef27d7441c7fd18cf5d" - integrity sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg== - -"@ethersproject/sha2@^5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.5.0.tgz#a40a054c61f98fd9eee99af2c3cc6ff57ec24db7" - integrity sha512-B5UBoglbCiHamRVPLA110J+2uqsifpZaTmid2/7W5rbtYVz6gus6/hSDieIU/6gaKIDcOj12WnOdiymEUHIAOA== - dependencies: - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - hash.js "1.1.7" - -"@project-serum/anchor@^0.11.1": - version "0.11.1" - resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.11.1.tgz#155bff2c70652eafdcfd5559c81a83bb19cec9ff" - integrity sha512-oIdm4vTJkUy6GmE6JgqDAuQPKI7XM4TPJkjtoIzp69RZe0iAD9JP2XHx7lV1jLdYXeYHqDXfBt3zcq7W91K6PA== - dependencies: - "@project-serum/borsh" "^0.2.2" - "@solana/web3.js" "^1.17.0" - base64-js "^1.5.1" - bn.js "^5.1.2" - bs58 "^4.0.1" - buffer-layout "^1.2.0" - camelcase "^5.3.1" - crypto-hash "^1.3.0" - eventemitter3 "^4.0.7" - find "^0.3.0" - js-sha256 "^0.9.0" - pako "^2.0.3" - snake-case "^3.0.4" - toml "^3.0.0" - -"@project-serum/anchor@file:../ts": - version "0.24.0" - dependencies: - "@project-serum/borsh" "^0.2.5" - "@solana/web3.js" "^1.36.0" - base64-js "^1.5.1" - bn.js "^5.1.2" - bs58 "^4.0.1" - buffer-layout "^1.2.2" - camelcase "^5.3.1" - cross-fetch "^3.1.5" - crypto-hash "^1.3.0" - eventemitter3 "^4.0.7" - js-sha256 "^0.9.0" - pako "^2.0.3" - snake-case "^3.0.4" - toml "^3.0.0" - -"@project-serum/borsh@^0.2.2": - version "0.2.2" - resolved "https://registry.yarnpkg.com/@project-serum/borsh/-/borsh-0.2.2.tgz#63e558f2d6eb6ab79086bf499dea94da3182498f" - integrity sha512-Ms+aWmGVW6bWd3b0+MWwoaYig2QD0F90h0uhr7AzY3dpCb5e2S6RsRW02vFTfa085pY2VLB7nTZNbFECQ1liTg== - dependencies: - bn.js "^5.1.2" - buffer-layout "^1.2.0" - -"@project-serum/borsh@^0.2.5": - version "0.2.5" - resolved "https://registry.yarnpkg.com/@project-serum/borsh/-/borsh-0.2.5.tgz#6059287aa624ecebbfc0edd35e4c28ff987d8663" - integrity sha512-UmeUkUoKdQ7rhx6Leve1SssMR/Ghv8qrEiyywyxSWg7ooV7StdpPBhciiy5eB3T0qU1BXvdRNC8TdrkxK7WC5Q== - dependencies: - bn.js "^5.1.2" - buffer-layout "^1.2.0" - -"@project-serum/common@^0.0.1-beta.3": - version "0.0.1-beta.3" - resolved "https://registry.yarnpkg.com/@project-serum/common/-/common-0.0.1-beta.3.tgz#53586eaff9d9fd7e8938b1e12080c935b8b6ad07" - integrity sha512-gnQE/eUydTtto5okCgLWj1M97R9RRPJqnhKklikYI7jP/pnNhDmngSXC/dmfzED2GXSJEIKNIlxVw1k+E2Aw3w== - dependencies: - "@project-serum/serum" "^0.13.21" - bn.js "^5.1.2" - superstruct "0.8.3" - -"@project-serum/serum@^0.13.21", "@project-serum/serum@^0.13.60": - version "0.13.60" - resolved "https://registry.yarnpkg.com/@project-serum/serum/-/serum-0.13.60.tgz#abeb3355ebc1895d685250df5965f688502ebcbb" - integrity sha512-fGsp9F0ZAS48YQ2HNy+6CNoifJESFXxVsOLPd9QK1XNV8CTuQoECOnVXxV6s5cKGre8pLNq5hrhi5J6aCGauEQ== - dependencies: - "@project-serum/anchor" "^0.11.1" - "@solana/spl-token" "^0.1.6" - "@solana/web3.js" "^1.21.0" - bn.js "^5.1.2" - buffer-layout "^1.2.0" - -"@solana/buffer-layout@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-3.0.0.tgz#b9353caeb9a1589cb77a1b145bcb1a9a93114326" - integrity sha512-MVdgAKKL39tEs0l8je0hKaXLQFb7Rdfb0Xg2LjFZd8Lfdazkg6xiS98uAZrEKvaoF3i4M95ei9RydkGIDMeo3w== - dependencies: - buffer "~6.0.3" - -"@solana/spl-token@^0.1.6", "@solana/spl-token@^0.1.8": - version "0.1.8" - resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.1.8.tgz#f06e746341ef8d04165e21fc7f555492a2a0faa6" - integrity sha512-LZmYCKcPQDtJgecvWOgT/cnoIQPWjdH+QVyzPcFvyDUiT0DiRjZaam4aqNUyvchLFhzgunv3d9xOoyE34ofdoQ== - dependencies: - "@babel/runtime" "^7.10.5" - "@solana/web3.js" "^1.21.0" - bn.js "^5.1.0" - buffer "6.0.3" - buffer-layout "^1.2.0" - dotenv "10.0.0" - -"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.21.0": - version "1.30.2" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.30.2.tgz#e85da75e0825dc64f53eb64a1ff0115b27bec135" - integrity sha512-hznCj+rkfvM5taRP3Z+l5lumB7IQnDrB4l55Wpsg4kDU9Zds8pE5YOH5Z9bbF/pUzZJKQjyBjnY/6kScBm3Ugg== - dependencies: - "@babel/runtime" "^7.12.5" - "@ethersproject/sha2" "^5.5.0" - "@solana/buffer-layout" "^3.0.0" - bn.js "^5.0.0" - borsh "^0.4.0" - bs58 "^4.0.1" - buffer "6.0.1" - cross-fetch "^3.1.4" - jayson "^3.4.4" - js-sha3 "^0.8.0" - rpc-websockets "^7.4.2" - secp256k1 "^4.0.2" - superstruct "^0.14.2" - tweetnacl "^1.0.0" - -"@solana/web3.js@^1.36.0": - version "1.36.0" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.36.0.tgz#79d7d5217b49b80139f4de68953adc5b9a9a264f" - integrity sha512-RNT1451iRR7TyW7EJKMCrH/0OXawIe4zVm0DWQASwXlR/u1jmW6FrmH0lujIh7cGTlfOVbH+2ZU9AVUPLBFzwA== - dependencies: - "@babel/runtime" "^7.12.5" - "@ethersproject/sha2" "^5.5.0" - "@solana/buffer-layout" "^3.0.0" - bn.js "^5.0.0" - borsh "^0.4.0" - bs58 "^4.0.1" - buffer "6.0.1" - cross-fetch "^3.1.4" - jayson "^3.4.4" - js-sha3 "^0.8.0" - rpc-websockets "^7.4.2" - secp256k1 "^4.0.2" - superstruct "^0.14.2" - tweetnacl "^1.0.0" - -"@types/bn.js@^4.11.5": - version "4.11.6" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" - integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== - dependencies: - "@types/node" "*" - -"@types/chai@^4.3.0": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.0.tgz#23509ebc1fa32f1b4d50d6a66c4032d5b8eaabdc" - integrity sha512-/ceqdqeRraGolFTcfoXNiqjyQhZzbINDngeoAq9GoHa8PPK1yNzTaxWjA6BFWp5Ua9JpXEMSS4s5i9tS0hOJtw== - -"@types/connect@^3.4.33": - version "3.4.35" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" - integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== - dependencies: - "@types/node" "*" - -"@types/express-serve-static-core@^4.17.9": - version "4.17.25" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.25.tgz#e42f7046adc65ece2eb6059b77aecfbe9e9f82e0" - integrity sha512-OUJIVfRMFijZukGGwTpKNFprqCCXk5WjNGvUgB/CxxBR40QWSjsNK86+yvGKlCOGc7sbwfHLaXhkG+NsytwBaQ== - dependencies: - "@types/node" "*" - "@types/qs" "*" - "@types/range-parser" "*" - -"@types/json5@^0.0.29": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= - -"@types/lodash@^4.14.159": - version "4.14.176" - resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.176.tgz#641150fc1cda36fbfa329de603bbb175d7ee20c0" - integrity sha512-xZmuPTa3rlZoIbtDUyJKZQimJV3bxCmzMIO2c9Pz9afyDro6kr7R79GwcB6mRhuoPmV2p1Vb66WOJH7F886WKQ== - -"@types/mocha@^9.1.0": - version "9.1.0" - resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-9.1.0.tgz#baf17ab2cca3fcce2d322ebc30454bff487efad5" - integrity sha512-QCWHkbMv4Y5U9oW10Uxbr45qMMSzl4OzijsozynUAgx3kEHUdXB00udx2dWDQ7f2TU2a2uuiFaRZjCe3unPpeg== - -"@types/node@*": - version "16.11.7" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.7.tgz#36820945061326978c42a01e56b61cd223dfdc42" - integrity sha512-QB5D2sqfSjCmTuWcBWyJ+/44bcjO7VbjSbOE0ucoVbAsSNQc4Lt6QkgkVXkTDwkL4z/beecZNDvVX15D4P8Jbw== - -"@types/node@^12.12.54": - version "12.20.37" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.37.tgz#abb38afa9d6e8a2f627a8cb52290b3c80fbe61ed" - integrity sha512-i1KGxqcvJaLQali+WuypQnXwcplhtNtjs66eNsZpp2P2FL/trJJxx/VWsM0YCL2iMoIJrbXje48lvIQAQ4p2ZA== - -"@types/node@^14.14.37": - version "14.17.33" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.17.33.tgz#011ee28e38dc7aee1be032ceadf6332a0ab15b12" - integrity sha512-noEeJ06zbn3lOh4gqe2v7NMGS33jrulfNqYFDjjEbhpDEHR5VTxgYNQSBqBlJIsBJW3uEYDgD6kvMnrrhGzq8g== - -"@types/qs@*": - version "6.9.7" - resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" - integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== - -"@types/range-parser@*": - version "1.2.4" - resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" - integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== - -"@types/ws@^7.4.4": - version "7.4.7" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" - integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww== - dependencies: - "@types/node" "*" - -"@ungap/promise-all-settled@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" - integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== - -JSONStream@^1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" - integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== - dependencies: - jsonparse "^1.2.0" - through ">=2.2.7 <3" - -ansi-colors@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -anymatch@~3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -arrify@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" - integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= - -assertion-error@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" - integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -base-x@^3.0.2: - version "3.0.9" - resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" - integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== - dependencies: - safe-buffer "^5.0.1" - -base64-js@^1.3.1, base64-js@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== - -bn.js@^4.11.9: - version "4.12.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" - integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== - -bn.js@^5.0.0, bn.js@^5.1.0, bn.js@^5.1.2: - version "5.2.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" - integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== - -borsh@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.4.0.tgz#9dd6defe741627f1315eac2a73df61421f6ddb9f" - integrity sha512-aX6qtLya3K0AkT66CmYWCCDr77qsE9arV05OmdFpmat9qu8Pg9J5tBUPDztAW5fNh/d/MyVG/OYziP52Ndzx1g== - dependencies: - "@types/bn.js" "^4.11.5" - bn.js "^5.0.0" - bs58 "^4.0.0" - text-encoding-utf-8 "^1.0.2" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -brorand@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" - integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= - -browser-stdout@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" - integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== - -bs58@^4.0.0, bs58@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" - integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo= - dependencies: - base-x "^3.0.2" - -buffer-from@^1.0.0, buffer-from@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - -buffer-layout@^1.2.0, buffer-layout@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/buffer-layout/-/buffer-layout-1.2.2.tgz#b9814e7c7235783085f9ca4966a0cfff112259d5" - integrity sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA== - -buffer@6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.1.tgz#3cbea8c1463e5a0779e30b66d4c88c6ffa182ac2" - integrity sha512-rVAXBwEcEoYtxnHSO5iWyhzV/O1WMtkUYWlfdLS7FjU4PnSJJHEfHXi/uHPI5EwltmOA794gN3bm3/pzuctWjQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - -buffer@6.0.3, buffer@~6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - -bufferutil@^4.0.1: - version "4.0.5" - resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.5.tgz#da9ea8166911cc276bf677b8aed2d02d31f59028" - integrity sha512-HTm14iMQKK2FjFLRTM5lAVcyaUzOnqbPtesFIvREgXpJHdQm8bWS+GkQgIkfaBYRHuCnea7w8UVNfwiAQhlr9A== - dependencies: - node-gyp-build "^4.3.0" - -camelcase@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -camelcase@^6.0.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" - integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== - -chai@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.4.tgz#b55e655b31e1eac7099be4c08c21964fce2e6c49" - integrity sha512-yS5H68VYOCtN1cjfwumDSuzn/9c+yza4f3reKXlE5rUg7SFcCEy90gJvydNgOYtblyf4Zi6jIWRnXOgErta0KA== - dependencies: - assertion-error "^1.1.0" - check-error "^1.0.2" - deep-eql "^3.0.1" - get-func-name "^2.0.0" - pathval "^1.1.1" - type-detect "^4.0.5" - -chalk@^4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -check-error@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" - integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= - -chokidar@3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.2.tgz#dba3976fcadb016f66fd365021d91600d01c1e75" - integrity sha512-ekGhOnNVPgT77r4K/U3GDhu+FQ2S8TnK/s2KbIGXi0SZWuwkZ2QNyfWdZW+TVfn84DpEP7rLeCt2UI6bJ8GwbQ== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - -circular-json@^0.5.9: - version "0.5.9" - resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.5.9.tgz#932763ae88f4f7dead7a0d09c8a51a4743a53b1d" - integrity sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ== - -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -commander@^2.20.3: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -cross-fetch@^3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39" - integrity sha512-1eAtFWdIubi6T4XPy6ei9iUFoKpUkIF971QLN8lIvvvwueI65+Nw5haMNKUwfJxabqlIIDODJKGrQ66gxC0PbQ== - dependencies: - node-fetch "2.6.1" - -cross-fetch@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" - integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== - dependencies: - node-fetch "2.6.7" - -crypto-hash@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-1.3.0.tgz#b402cb08f4529e9f4f09346c3e275942f845e247" - integrity sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg== - -debug@4.3.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" - integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== - dependencies: - ms "2.1.2" - -decamelize@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" - integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== - -deep-eql@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df" - integrity sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== - dependencies: - type-detect "^4.0.0" - -delay@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" - integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== - -diff@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" - integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== - -diff@^3.1.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" - integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== - -dot-case@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" - integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== - dependencies: - no-case "^3.0.4" - tslib "^2.0.3" - -dotenv@10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" - integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== - -elliptic@^6.5.2: - version "6.5.4" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== - dependencies: - bn.js "^4.11.9" - brorand "^1.1.0" - hash.js "^1.0.0" - hmac-drbg "^1.0.1" - inherits "^2.0.4" - minimalistic-assert "^1.0.1" - minimalistic-crypto-utils "^1.0.1" - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -es6-promise@^4.0.3: - version "4.2.8" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" - integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== - -es6-promisify@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" - integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= - dependencies: - es6-promise "^4.0.3" - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-string-regexp@4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -eventemitter3@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" - integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== - -eyes@^0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" - integrity sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A= - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -find-up@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -find@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/find/-/find-0.3.0.tgz#4082e8fc8d8320f1a382b5e4f521b9bc50775cb8" - integrity sha512-iSd+O4OEYV/I36Zl8MdYJO0xD82wH528SaCieTVHhclgiYNe9y+yPKSwK+A7/WsmHL1EZ+pYUJBXWTL5qofksw== - dependencies: - traverse-chain "~0.1.0" - -flat@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" - integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-func-name@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" - integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= - -glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob@7.1.7: - version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" - integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== - dependencies: - 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" - -growl@1.10.5: - version "1.10.5" - resolved "https://registry.yarnpkg.com/growl/-/growl-1.10.5.tgz#f2735dc2283674fa67478b10181059355c369e5e" - integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - -he@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" - integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== - -hmac-drbg@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" - integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - -ieee754@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@^2.0.3, inherits@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-plain-obj@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" - integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== - -is-unicode-supported@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" - integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - -isomorphic-ws@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" - integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== - -jayson@^3.4.4: - version "3.6.5" - resolved "https://registry.yarnpkg.com/jayson/-/jayson-3.6.5.tgz#e560bcad4daf098c7391f46ba8efc9d6f34a4102" - integrity sha512-wmOjX+eQcnCDyPF4KORomaIj9wj3h0B5VEbeD0+2VHfTfErB+h1zpR7oBkgCZp36AFjp3+a4CLz6U72BYpFHAw== - dependencies: - "@types/connect" "^3.4.33" - "@types/express-serve-static-core" "^4.17.9" - "@types/lodash" "^4.14.159" - "@types/node" "^12.12.54" - "@types/ws" "^7.4.4" - JSONStream "^1.3.5" - commander "^2.20.3" - delay "^5.0.0" - es6-promisify "^5.0.0" - eyes "^0.1.8" - isomorphic-ws "^4.0.1" - json-stringify-safe "^5.0.1" - lodash "^4.17.20" - uuid "^3.4.0" - ws "^7.4.5" - -js-sha256@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" - integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== - -js-sha3@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" - integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== - -js-yaml@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - -json-stringify-safe@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== - dependencies: - minimist "^1.2.0" - -jsonparse@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" - integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= - -kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - -lodash@^4.17.20: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -log-symbols@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" - integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== - dependencies: - chalk "^4.1.0" - is-unicode-supported "^0.1.0" - -lower-case@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" - integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== - dependencies: - tslib "^2.0.3" - -make-error@^1.1.1: - version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" - integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== - -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" - integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= - -minimatch@3.0.4, minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimist@^1.2.0, minimist@^1.2.5: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== - -mkdirp@^0.5.1: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" - -mocha@^9.1.3: - version "9.1.3" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-9.1.3.tgz#8a623be6b323810493d8c8f6f7667440fa469fdb" - integrity sha512-Xcpl9FqXOAYqI3j79pEtHBBnQgVXIhpULjGQa7DVb0Po+VzmSIK9kanAiWLHoRR/dbZ2qpdPshuXr8l1VaHCzw== - dependencies: - "@ungap/promise-all-settled" "1.1.2" - ansi-colors "4.1.1" - browser-stdout "1.3.1" - chokidar "3.5.2" - debug "4.3.2" - diff "5.0.0" - escape-string-regexp "4.0.0" - find-up "5.0.0" - glob "7.1.7" - growl "1.10.5" - he "1.2.0" - js-yaml "4.1.0" - log-symbols "4.1.0" - minimatch "3.0.4" - ms "2.1.3" - nanoid "3.1.25" - serialize-javascript "6.0.0" - strip-json-comments "3.1.1" - supports-color "8.1.1" - which "2.0.2" - workerpool "6.1.5" - yargs "16.2.0" - yargs-parser "20.2.4" - yargs-unparser "2.0.0" - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@2.1.3: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -nanoid@3.1.25: - version "3.1.25" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.25.tgz#09ca32747c0e543f0e1814b7d3793477f9c8e152" - integrity sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q== - -no-case@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" - integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== - dependencies: - lower-case "^2.0.2" - tslib "^2.0.3" - -node-addon-api@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" - integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== - -node-fetch@2.6.1: - version "2.6.1" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" - integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== - -node-fetch@2.6.7: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - -node-gyp-build@^4.2.0, node-gyp-build@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" - integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - -pako@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pako/-/pako-2.0.4.tgz#6cebc4bbb0b6c73b0d5b8d7e8476e2b2fbea576d" - integrity sha512-v8tweI900AUkZN6heMU/4Uy4cXRc2AYNRggVmTR+dEncawDJgCdLMximOVA2p4qO57WMynangsfGRb5WD6L1Bg== - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -pathval@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" - integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== - -picomatch@^2.0.4, picomatch@^2.2.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.0.tgz#f1f061de8f6a4bf022892e2d128234fb98302972" - integrity sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw== - -prettier@^2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.5.1.tgz#fff75fa9d519c54cf0fce328c1017d94546bc56a" - integrity sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg== - -randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - -regenerator-runtime@^0.13.4: - version "0.13.9" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" - integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= - -rpc-websockets@^7.4.2: - version "7.4.16" - resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.4.16.tgz#eb701cdef577d4357ba5f526d50e25f370396fac" - integrity sha512-0b7OVhutzwRIaYAtJo5tqtaQTWKfwAsKnaThOSOy+VkhVdleNUgb8eZnWSdWITRZZEigV5uPEIDr5KZe4DBrdQ== - dependencies: - "@babel/runtime" "^7.11.2" - circular-json "^0.5.9" - eventemitter3 "^4.0.7" - uuid "^8.3.0" - ws "^7.4.5" - optionalDependencies: - bufferutil "^4.0.1" - utf-8-validate "^5.0.2" - -safe-buffer@^5.0.1, safe-buffer@^5.1.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -secp256k1@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.2.tgz#15dd57d0f0b9fdb54ac1fa1694f40e5e9a54f4a1" - integrity sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg== - dependencies: - elliptic "^6.5.2" - node-addon-api "^2.0.0" - node-gyp-build "^4.2.0" - -serialize-javascript@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" - integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== - dependencies: - randombytes "^2.1.0" - -snake-case@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" - integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg== - dependencies: - dot-case "^3.0.4" - tslib "^2.0.3" - -source-map-support@^0.5.6: - version "0.5.20" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.20.tgz#12166089f8f5e5e8c56926b377633392dd2cb6c9" - integrity sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= - -strip-json-comments@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -superstruct@0.8.3: - version "0.8.3" - resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.8.3.tgz#fb4d8901aca3bf9f79afab1bbab7a7f335cc4ef2" - integrity sha512-LbtbFpktW1FcwxVIJlxdk7bCyBq/GzOx2FSFLRLTUhWIA1gHkYPIl3aXRG5mBdGZtnPNT6t+4eEcLDCMOuBHww== - dependencies: - kind-of "^6.0.2" - tiny-invariant "^1.0.6" - -superstruct@^0.14.2: - version "0.14.2" - resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.14.2.tgz#0dbcdf3d83676588828f1cf5ed35cda02f59025b" - integrity sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ== - -supports-color@8.1.1: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -text-encoding-utf-8@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13" - integrity sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg== - -"through@>=2.2.7 <3": - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - -tiny-invariant@^1.0.6: - version "1.2.0" - resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.2.0.tgz#a1141f86b672a9148c72e978a19a73b9b94a15a9" - integrity sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg== - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -toml@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee" - integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= - -traverse-chain@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1" - integrity sha1-YdvC1Ttp/2CRoSoWj9fUMxB+QPE= - -ts-mocha@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/ts-mocha/-/ts-mocha-8.0.0.tgz#962d0fa12eeb6468aa1a6b594bb3bbc818da3ef0" - integrity sha512-Kou1yxTlubLnD5C3unlCVO7nh0HERTezjoVhVw/M5S1SqoUec0WgllQvPk3vzPMc6by8m6xD1uR1yRf8lnVUbA== - dependencies: - ts-node "7.0.1" - optionalDependencies: - tsconfig-paths "^3.5.0" - -ts-node@7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-7.0.1.tgz#9562dc2d1e6d248d24bc55f773e3f614337d9baf" - integrity sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw== - dependencies: - arrify "^1.0.0" - buffer-from "^1.1.0" - diff "^3.1.0" - make-error "^1.1.1" - minimist "^1.2.0" - mkdirp "^0.5.1" - source-map-support "^0.5.6" - yn "^2.0.0" - -tsconfig-paths@^3.5.0: - version "3.11.0" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.11.0.tgz#954c1fe973da6339c78e06b03ce2e48810b65f36" - integrity sha512-7ecdYDnIdmv639mmDwslG6KQg1Z9STTz1j7Gcz0xa+nshh/gKDAHcPxRbWOsA3SPp0tXP2leTcY9Kw+NAkfZzA== - dependencies: - "@types/json5" "^0.0.29" - json5 "^1.0.1" - minimist "^1.2.0" - strip-bom "^3.0.0" - -tslib@^2.0.3: - version "2.3.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" - integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== - -tweetnacl@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" - integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== - -type-detect@^4.0.0, type-detect@^4.0.5: - version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" - integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== - -typescript@^4.4.4: - version "4.4.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c" - integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA== - -utf-8-validate@^5.0.2: - version "5.0.7" - resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.7.tgz#c15a19a6af1f7ad9ec7ddc425747ca28c3644922" - integrity sha512-vLt1O5Pp+flcArHGIyKEQq883nBt8nN8tVBcoL0qUXj2XT1n7p70yGIq2VK98I5FdZ1YHc0wk/koOnHjnXWk1Q== - dependencies: - node-gyp-build "^4.3.0" - -uuid@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -uuid@^8.3.0: - version "8.3.2" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0= - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -which@2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -workerpool@6.1.5: - version "6.1.5" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.5.tgz#0f7cf076b6215fd7e1da903ff6f22ddd1886b581" - integrity sha512-XdKkCK0Zqc6w3iTxLckiuJ81tiD/o5rBE/m+nXpRCB+/Sq4DqkfXZ/x0jW02DG1tGsfUGXbTJyZDP+eu67haSw== - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -ws@^7.4.5: - version "7.5.5" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881" - integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w== - -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - -yargs-parser@20.2.4: - version "20.2.4" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" - integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== - -yargs-parser@^20.2.2: - version "20.2.9" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" - integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== - -yargs-unparser@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" - integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== - dependencies: - camelcase "^6.0.0" - decamelize "^4.0.0" - flat "^5.0.2" - is-plain-obj "^2.1.0" - -yargs@16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.0" - y18n "^5.0.5" - yargs-parser "^20.2.2" - -yn@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/yn/-/yn-2.0.0.tgz#e5adabc8acf408f6385fc76495684c88e6af689a" - integrity sha1-5a2ryKz0CPY4X8dklWhMiOavaJo= - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/tests/zero-copy/Anchor.toml b/tests/zero-copy/Anchor.toml deleted file mode 100644 index ab1af598..00000000 --- a/tests/zero-copy/Anchor.toml +++ /dev/null @@ -1,16 +0,0 @@ -[provider] -cluster = "localnet" -wallet = "~/.config/solana/id.json" - -[workspace] -members = ["programs/zero-copy", "programs/zero-cpi"] - -[scripts] -test = "yarn run mocha -t 1000000 tests/" - -[programs.localnet] -zero_cpi = "ErjUjtqKE5AGWUsjseSJCVLtddM6rhaMbDqmhzraF9h6" -zero_copy = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS" - -[features] - diff --git a/tests/zero-copy/Cargo.toml b/tests/zero-copy/Cargo.toml deleted file mode 100644 index a60de986..00000000 --- a/tests/zero-copy/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[workspace] -members = [ - "programs/*" -] diff --git a/tests/zero-copy/migrations/deploy.js b/tests/zero-copy/migrations/deploy.js deleted file mode 100644 index 5e3df0dc..00000000 --- a/tests/zero-copy/migrations/deploy.js +++ /dev/null @@ -1,12 +0,0 @@ -// Migrations are an early feature. Currently, they're nothing more than this -// single deploy script that's invoked from the CLI, injecting a provider -// configured from the workspace's Anchor.toml. - -const anchor = require("@project-serum/anchor"); - -module.exports = async function (provider) { - // Configure client to use the provider. - anchor.setProvider(provider); - - // Add your deploy script here. -}; diff --git a/tests/zero-copy/package.json b/tests/zero-copy/package.json deleted file mode 100644 index 40842704..00000000 --- a/tests/zero-copy/package.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "zero-copy", - "version": "0.24.2", - "license": "(MIT OR Apache-2.0)", - "homepage": "https://github.com/project-serum/anchor#readme", - "bugs": { - "url": "https://github.com/project-serum/anchor/issues" - }, - "repository": { - "type": "git", - "url": "https://github.com/project-serum/anchor.git" - }, - "engines": { - "node": ">=11" - }, - "scripts": { - "test": "anchor test" - } -} diff --git a/tests/zero-copy/programs/shared/Cargo.toml b/tests/zero-copy/programs/shared/Cargo.toml deleted file mode 100644 index c0dff4a0..00000000 --- a/tests/zero-copy/programs/shared/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "shared" -version = "0.1.0" -rust-version = "1.56" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] diff --git a/tests/zero-copy/programs/shared/src/lib.rs b/tests/zero-copy/programs/shared/src/lib.rs deleted file mode 100644 index 31e1bb20..00000000 --- a/tests/zero-copy/programs/shared/src/lib.rs +++ /dev/null @@ -1,7 +0,0 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} diff --git a/tests/zero-copy/programs/zero-copy/Cargo.toml b/tests/zero-copy/programs/zero-copy/Cargo.toml deleted file mode 100644 index 4f771190..00000000 --- a/tests/zero-copy/programs/zero-copy/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "zero-copy" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "zero_copy" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = [] -test-bpf = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } - -[dev-dependencies] -anchor-client = { path = "../../../../client", features = ["debug"] } -bytemuck = "1.4.0" -solana-program-test = "1.9.13" diff --git a/tests/zero-copy/programs/zero-copy/Xargo.toml b/tests/zero-copy/programs/zero-copy/Xargo.toml deleted file mode 100644 index 1744f098..00000000 --- a/tests/zero-copy/programs/zero-copy/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] \ No newline at end of file diff --git a/tests/zero-copy/programs/zero-copy/src/lib.rs b/tests/zero-copy/programs/zero-copy/src/lib.rs deleted file mode 100644 index c6076ea9..00000000 --- a/tests/zero-copy/programs/zero-copy/src/lib.rs +++ /dev/null @@ -1,191 +0,0 @@ -//! This example demonstrates the use of zero-copy deserialization for accounts. -//! Zero-copy is a deserialization technique that creates data structures by borrowing -//! (not copying!) from the array holding the input, avoiding the expensive memory -//! allocation and processing of traditional deserialization. -//! With zero-copy, we can create accounts larger than the size of the stack or heap, -//! as is demonstrated by the event queue in this example. - -use anchor_lang::prelude::*; - -declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"); - -#[program] -pub mod zero_copy { - use super::*; - - pub fn create_foo(ctx: Context) -> Result<()> { - let foo = &mut ctx.accounts.foo.load_init()?; - foo.authority = *ctx.accounts.authority.key; - foo.set_second_authority(ctx.accounts.authority.key); - Ok(()) - } - - pub fn update_foo(ctx: Context, data: u64) -> Result<()> { - let mut foo = ctx.accounts.foo.load_mut()?; - foo.data = data; - Ok(()) - } - - pub fn update_foo_second(ctx: Context, second_data: u64) -> Result<()> { - let mut foo = ctx.accounts.foo.load_mut()?; - foo.second_data = second_data; - Ok(()) - } - - pub fn create_bar(ctx: Context) -> Result<()> { - let bar = &mut ctx.accounts.bar.load_init()?; - bar.authority = *ctx.accounts.authority.key; - Ok(()) - } - - pub fn update_bar(ctx: Context, data: u64) -> Result<()> { - let bar = &mut ctx.accounts.bar.load_mut()?; - bar.data = data; - Ok(()) - } - - pub fn create_large_account(_ctx: Context) -> Result<()> { - Ok(()) - } - - pub fn update_large_account( - ctx: Context, - idx: u32, - data: u64, - ) -> Result<()> { - let event_q = &mut ctx.accounts.event_q.load_mut()?; - event_q.events[idx as usize] = Event { - data, - from: *ctx.accounts.from.key, - }; - Ok(()) - } -} - -#[derive(Accounts)] -pub struct SetEvent<'info> { - authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct CreateFoo<'info> { - #[account(zero)] - foo: AccountLoader<'info, Foo>, - authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct UpdateFoo<'info> { - #[account(mut, has_one = authority)] - foo: AccountLoader<'info, Foo>, - authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct UpdateFooSecond<'info> { - #[account( - mut, - constraint = &foo.load()?.get_second_authority() == second_authority.key, - )] - foo: AccountLoader<'info, Foo>, - second_authority: Signer<'info>, -} - -#[derive(Accounts)] -pub struct CreateBar<'info> { - #[account( - init, - seeds = [authority.key().as_ref(), foo.key().as_ref()], - bump, - payer = authority, owner = *program_id, - space = Bar::LEN + 8 - )] - bar: AccountLoader<'info, Bar>, - #[account(mut)] - authority: Signer<'info>, - foo: AccountLoader<'info, Foo>, - system_program: AccountInfo<'info>, -} -#[derive(Accounts)] -pub struct UpdateBar<'info> { - #[account( - mut, - has_one = authority, - seeds = [authority.key().as_ref(), foo.key().as_ref()], - bump, - )] - pub bar: AccountLoader<'info, Bar>, - pub authority: Signer<'info>, - pub foo: AccountLoader<'info, Foo>, -} - -#[derive(Accounts)] -pub struct CreateLargeAccount<'info> { - #[account(zero)] - event_q: AccountLoader<'info, EventQ>, -} - -#[derive(Accounts)] -pub struct UpdateLargeAccount<'info> { - #[account(mut)] - event_q: AccountLoader<'info, EventQ>, - from: Signer<'info>, -} - -#[account(zero_copy)] -#[repr(packed)] -#[derive(Default)] -pub struct Foo { - pub authority: Pubkey, - pub data: u64, - pub second_data: u64, - #[accessor(Pubkey)] // The `accessor` api will likely be removed. - pub second_authority: [u8; 32], -} - -#[account(zero_copy)] -pub struct Bar { - pub authority: Pubkey, // 32 - pub data: u64, // 8 -} - -impl Bar { - pub const LEN: usize = 32 + 8; -} - -#[account(zero_copy)] -pub struct EventQ { - pub events: [Event; 25000], -} - -#[zero_copy] -pub struct Event { - pub from: Pubkey, - pub data: u64, -} - -// A separate type is used for the RPC interface for two main reasons. -// -// 1. AnchorSerialize and AnchorDeserialize must be derived. Anchor requires -// *all* instructions to implement the AnchorSerialize and AnchorDeserialize -// traits, so any types in method signatures must as well. -// 2. All types for zero copy deserialization are `#[repr(packed)]`. However, -// the implementation of AnchorSerialize (i.e. borsh), uses references -// to the fields it serializes. So if we were to just throw tehse derives -// onto the other `Event` struct, we would have references to -// `#[repr(packed)]` fields, which is unsafe. To avoid the unsafeness, we -// just use a separate type. -#[derive(AnchorSerialize, AnchorDeserialize)] -pub struct RpcEvent { - pub from: Pubkey, - pub data: u64, -} - -impl From for Event { - fn from(e: RpcEvent) -> Event { - Event { - from: e.from, - data: e.data, - } - } -} diff --git a/tests/zero-copy/programs/zero-copy/tests/compute_unit_test.rs b/tests/zero-copy/programs/zero-copy/tests/compute_unit_test.rs deleted file mode 100644 index 0e4e8430..00000000 --- a/tests/zero-copy/programs/zero-copy/tests/compute_unit_test.rs +++ /dev/null @@ -1,70 +0,0 @@ -#![cfg(feature = "test-bpf")] - -use { - anchor_client::{ - anchor_lang::Discriminator, - solana_sdk::{ - account::Account, - commitment_config::CommitmentConfig, - pubkey::Pubkey, - signature::{Keypair, Signer}, - transaction::Transaction, - }, - Client, Cluster, - }, - solana_program_test::{tokio, ProgramTest}, - std::rc::Rc, -}; - -#[tokio::test] -async fn update_foo() { - let authority = Keypair::new(); - let foo_pubkey = Pubkey::new_unique(); - let foo_account = { - let mut foo_data = Vec::new(); - foo_data.extend_from_slice(&zero_copy::Foo::discriminator()); - foo_data.extend_from_slice(bytemuck::bytes_of(&zero_copy::Foo { - authority: authority.pubkey(), - ..zero_copy::Foo::default() - })); - - Account { - lamports: 1, - data: foo_data, - owner: zero_copy::id(), - ..Account::default() - } - }; - - let mut pt = ProgramTest::new("zero_copy", zero_copy::id(), None); - pt.add_account(foo_pubkey, foo_account); - pt.set_compute_max_units(3157); - let (mut banks_client, payer, recent_blockhash) = pt.start().await; - - let client = Client::new_with_options( - Cluster::Debug, - Rc::new(Keypair::new()), - CommitmentConfig::processed(), - ); - let program = client.program(zero_copy::id()); - let update_ix = program - .request() - .accounts(zero_copy::accounts::UpdateFoo { - foo: foo_pubkey, - authority: authority.pubkey(), - }) - .args(zero_copy::instruction::UpdateFoo { data: 1u64 }) - .instructions() - .unwrap() - .pop() - .unwrap(); - - let transaction = Transaction::new_signed_with_payer( - &[update_ix], - Some(&payer.pubkey()), - &[&payer, &authority], - recent_blockhash, - ); - - banks_client.process_transaction(transaction).await.unwrap(); -} diff --git a/tests/zero-copy/programs/zero-cpi/Cargo.toml b/tests/zero-copy/programs/zero-cpi/Cargo.toml deleted file mode 100644 index c4b4e440..00000000 --- a/tests/zero-copy/programs/zero-cpi/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "zero-cpi" -version = "0.1.0" -description = "Created with Anchor" -rust-version = "1.56" -edition = "2021" - -[lib] -crate-type = ["cdylib", "lib"] -name = "zero_cpi" - -[features] -no-entrypoint = [] -no-idl = [] -cpi = ["no-entrypoint"] -default = [] - -[dependencies] -anchor-lang = { path = "../../../../lang" } -zero-copy = { path = "../zero-copy", features = ["cpi"] } diff --git a/tests/zero-copy/programs/zero-cpi/Xargo.toml b/tests/zero-copy/programs/zero-cpi/Xargo.toml deleted file mode 100644 index 475fb71e..00000000 --- a/tests/zero-copy/programs/zero-cpi/Xargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[target.bpfel-unknown-unknown.dependencies.std] -features = [] diff --git a/tests/zero-copy/programs/zero-cpi/src/lib.rs b/tests/zero-copy/programs/zero-cpi/src/lib.rs deleted file mode 100644 index c1d41c2e..00000000 --- a/tests/zero-copy/programs/zero-cpi/src/lib.rs +++ /dev/null @@ -1,35 +0,0 @@ -use anchor_lang::prelude::*; -use zero_copy::cpi::accounts::UpdateBar; -use zero_copy::program::ZeroCopy; -use zero_copy::{self, Bar, Foo}; - -declare_id!("ErjUjtqKE5AGWUsjseSJCVLtddM6rhaMbDqmhzraF9h6"); - -#[program] -pub mod zero_cpi { - use super::*; - pub fn check_cpi(ctx: Context, data: u64) -> Result<()> { - let cpi_program = ctx.accounts.zero_copy_program.to_account_info(); - let cpi_accounts = UpdateBar { - authority: ctx.accounts.authority.clone(), - bar: ctx.accounts.bar.to_account_info(), - foo: ctx.accounts.foo.to_account_info(), - }; - let cpi_ctx = CpiContext::new(cpi_program, cpi_accounts); - zero_copy::cpi::update_bar(cpi_ctx, data); - Ok(()) - } -} - -#[derive(Accounts)] -pub struct CheckCpi<'info> { - #[account( - mut, - has_one = authority, - )] - bar: AccountLoader<'info, Bar>, - #[account(signer)] - authority: AccountInfo<'info>, - foo: AccountLoader<'info, Foo>, - zero_copy_program: Program<'info, ZeroCopy>, -} diff --git a/tests/zero-copy/tests/zero-copy.js b/tests/zero-copy/tests/zero-copy.js deleted file mode 100644 index f4e9f121..00000000 --- a/tests/zero-copy/tests/zero-copy.js +++ /dev/null @@ -1,264 +0,0 @@ -const anchor = require("@project-serum/anchor"); -const { assert } = require("chai"); -const nativeAssert = require("assert"); -const PublicKey = anchor.web3.PublicKey; -const BN = anchor.BN; - -describe("zero-copy", () => { - // Configure the client to use the local cluster. - anchor.setProvider(anchor.AnchorProvider.env()); - - const program = anchor.workspace.ZeroCopy; - const programCpi = anchor.workspace.ZeroCpi; - - const foo = anchor.web3.Keypair.generate(); - it("Is creates a zero copy account", async () => { - await program.rpc.createFoo({ - accounts: { - foo: foo.publicKey, - authority: program.provider.wallet.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - instructions: [await program.account.foo.createInstruction(foo)], - signers: [foo], - }); - const account = await program.account.foo.fetch(foo.publicKey); - assert.strictEqual( - JSON.stringify(account.authority.toBuffer()), - JSON.stringify(program.provider.wallet.publicKey.toBuffer()) - ); - assert.strictEqual(account.data.toNumber(), 0); - assert.strictEqual(account.secondData.toNumber(), 0); - assert.strictEqual( - JSON.stringify(account.secondAuthority), - JSON.stringify([...program.provider.wallet.publicKey.toBuffer()]) - ); - }); - - it("Updates a zero copy account field", async () => { - await program.rpc.updateFoo(new BN(1234), { - accounts: { - foo: foo.publicKey, - authority: program.provider.wallet.publicKey, - }, - }); - - const account = await program.account.foo.fetch(foo.publicKey); - - assert.strictEqual( - JSON.stringify(account.authority.toBuffer()), - JSON.stringify(program.provider.wallet.publicKey.toBuffer()) - ); - assert.strictEqual(account.data.toNumber(), 1234); - assert.strictEqual(account.secondData.toNumber(), 0); - assert.strictEqual( - JSON.stringify(account.secondAuthority), - JSON.stringify([...program.provider.wallet.publicKey.toBuffer()]) - ); - }); - - it("Updates a a second zero copy account field", async () => { - await program.rpc.updateFooSecond(new BN(55), { - accounts: { - foo: foo.publicKey, - secondAuthority: program.provider.wallet.publicKey, - }, - }); - - const account = await program.account.foo.fetch(foo.publicKey); - - assert.strictEqual( - JSON.stringify(account.authority.toBuffer()), - JSON.stringify(program.provider.wallet.publicKey.toBuffer()) - ); - assert.strictEqual(account.data.toNumber(), 1234); - assert.strictEqual(account.secondData.toNumber(), 55); - assert.strictEqual( - JSON.stringify(account.secondAuthority), - JSON.stringify([...program.provider.wallet.publicKey.toBuffer()]) - ); - }); - - it("Creates an associated zero copy account", async () => { - await program.rpc.createBar({ - accounts: { - bar: ( - await PublicKey.findProgramAddress( - [ - program.provider.wallet.publicKey.toBuffer(), - foo.publicKey.toBuffer(), - ], - program.programId - ) - )[0], - authority: program.provider.wallet.publicKey, - foo: foo.publicKey, - systemProgram: anchor.web3.SystemProgram.programId, - }, - }); - - const bar = ( - await PublicKey.findProgramAddress( - [ - program.provider.wallet.publicKey.toBuffer(), - foo.publicKey.toBuffer(), - ], - program.programId - ) - )[0]; - const barAccount = await program.account.bar.fetch(bar); - assert.isTrue( - barAccount.authority.equals(program.provider.wallet.publicKey) - ); - assert.strictEqual(barAccount.data.toNumber(), 0); - }); - - it("Updates an associated zero copy account", async () => { - const bar = ( - await PublicKey.findProgramAddress( - [ - program.provider.wallet.publicKey.toBuffer(), - foo.publicKey.toBuffer(), - ], - program.programId - ) - )[0]; - await program.rpc.updateBar(new BN(99), { - accounts: { - bar, - authority: program.provider.wallet.publicKey, - foo: foo.publicKey, - }, - }); - const barAccount = await program.account.bar.fetch(bar); - assert.isTrue( - barAccount.authority.equals(program.provider.wallet.publicKey) - ); - assert.strictEqual(barAccount.data.toNumber(), 99); - // Check zero_copy CPI - await programCpi.rpc.checkCpi(new BN(1337), { - accounts: { - bar, - authority: program.provider.wallet.publicKey, - foo: foo.publicKey, - zeroCopyProgram: program.programId, - }, - }); - const barAccountAfterCpi = await program.account.bar.fetch(bar); - assert.isTrue( - barAccountAfterCpi.authority.equals(program.provider.wallet.publicKey) - ); - assert.strictEqual(barAccountAfterCpi.data.toNumber(), 1337); - }); - - const eventQ = anchor.web3.Keypair.generate(); - const size = 1000000 + 8; // Account size in bytes. - - it("Creates a large event queue", async () => { - await program.rpc.createLargeAccount({ - accounts: { - eventQ: eventQ.publicKey, - rent: anchor.web3.SYSVAR_RENT_PUBKEY, - }, - instructions: [ - await program.account.eventQ.createInstruction(eventQ, size), - ], - signers: [eventQ], - }); - const account = await program.account.eventQ.fetch(eventQ.publicKey); - assert.strictEqual(account.events.length, 25000); - account.events.forEach((event) => { - assert.isTrue(event.from.equals(PublicKey.default)); - assert.strictEqual(event.data.toNumber(), 0); - }); - }); - - it("Updates a large event queue", async () => { - // Set index 0. - await program.rpc.updateLargeAccount(0, new BN(48), { - accounts: { - eventQ: eventQ.publicKey, - from: program.provider.wallet.publicKey, - }, - }); - // Verify update. - let account = await program.account.eventQ.fetch(eventQ.publicKey); - assert.strictEqual(account.events.length, 25000); - account.events.forEach((event, idx) => { - if (idx === 0) { - assert.isTrue(event.from.equals(program.provider.wallet.publicKey)); - assert.strictEqual(event.data.toNumber(), 48); - } else { - assert.isTrue(event.from.equals(PublicKey.default)); - assert.strictEqual(event.data.toNumber(), 0); - } - }); - - // Set index 11111. - await program.rpc.updateLargeAccount(11111, new BN(1234), { - accounts: { - eventQ: eventQ.publicKey, - from: program.provider.wallet.publicKey, - }, - }); - // Verify update. - account = await program.account.eventQ.fetch(eventQ.publicKey); - assert.strictEqual(account.events.length, 25000); - account.events.forEach((event, idx) => { - if (idx === 0) { - assert.isTrue(event.from.equals(program.provider.wallet.publicKey)); - assert.strictEqual(event.data.toNumber(), 48); - } else if (idx === 11111) { - assert.isTrue(event.from.equals(program.provider.wallet.publicKey)); - assert.strictEqual(event.data.toNumber(), 1234); - } else { - assert.isTrue(event.from.equals(PublicKey.default)); - assert.strictEqual(event.data.toNumber(), 0); - } - }); - - // Set last index. - await program.rpc.updateLargeAccount(24999, new BN(99), { - accounts: { - eventQ: eventQ.publicKey, - from: program.provider.wallet.publicKey, - }, - }); - // Verify update. - account = await program.account.eventQ.fetch(eventQ.publicKey); - assert.strictEqual(account.events.length, 25000); - account.events.forEach((event, idx) => { - if (idx === 0) { - assert.isTrue(event.from.equals(program.provider.wallet.publicKey)); - assert.strictEqual(event.data.toNumber(), 48); - } else if (idx === 11111) { - assert.isTrue(event.from.equals(program.provider.wallet.publicKey)); - assert.strictEqual(event.data.toNumber(), 1234); - } else if (idx === 24999) { - assert.isTrue(event.from.equals(program.provider.wallet.publicKey)); - assert.strictEqual(event.data.toNumber(), 99); - } else { - assert.isTrue(event.from.equals(PublicKey.default)); - assert.strictEqual(event.data.toNumber(), 0); - } - }); - }); - - it("Errors when setting an out of bounds index", async () => { - // Fail to set non existing index. - await nativeAssert.rejects( - async () => { - await program.rpc.updateLargeAccount(25000, new BN(1), { - accounts: { - eventQ: eventQ.publicKey, - from: program.provider.wallet.publicKey, - }, - }); - }, - (err) => { - console.log("err", err); - return true; - } - ); - }); -}); diff --git a/ts/README.md b/ts/README.md deleted file mode 100644 index c045ad74..00000000 --- a/ts/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# @project-serum/anchor - -[![npm](https://img.shields.io/npm/v/@project-serum/anchor.svg?color=blue)](https://www.npmjs.com/package/@project-serum/anchor) -[![Docs](https://img.shields.io/badge/docs-typedoc-blue)](https://project-serum.github.io/anchor/ts/index.html) - -TypeScript client for Anchor programs. - -## Note - -* `@project-serum/anchor` depends on node.js native modules. Therefore, webpack 5 will not work with current version. You will either need to rollback to webpack 4, or use a polyfill for each missing dependency. \ No newline at end of file diff --git a/ts/tsconfig.base.json b/tsconfig.base.json similarity index 100% rename from ts/tsconfig.base.json rename to tsconfig.base.json diff --git a/ts/tsconfig.cjs.json b/tsconfig.cjs.json similarity index 100% rename from ts/tsconfig.cjs.json rename to tsconfig.cjs.json diff --git a/ts/tsconfig.json b/tsconfig.json similarity index 100% rename from ts/tsconfig.json rename to tsconfig.json diff --git a/ts/types/buffer-layout/index.d.ts b/types/buffer-layout/index.d.ts similarity index 100% rename from ts/types/buffer-layout/index.d.ts rename to types/buffer-layout/index.d.ts diff --git a/version-bump.sh b/version-bump.sh deleted file mode 100755 index d5592ef9..00000000 --- a/version-bump.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash - -set -e - -if [ $# -eq 0 ]; then - echo "Usage $0 VERSION" - exit 1 -fi - -echo "Bumping versions to $1" - -# GNU/BSD compat -sedi=(-i) -case "$(uname)" in - # For macOS, use two parameters - Darwin*) sedi=(-i "") -esac - -git grep -l $(cat VERSION) -- ':!**/yarn.lock' ':!CHANGELOG.md' ':!Cargo.lock' ':!package.json' | \ - xargs sed "${sedi[@]}" \ - -e "s/$(cat VERSION)/$1/g" - -# Potential for collisions in package.json files, handle those separately -# Replace only matching "version": "x.xx.x" and "@project-serum/anchor": "x.xx.x" -git grep -l $(cat VERSION) -- '**/package.json' | \ - xargs sed "${sedi[@]}" \ - -e "s/@project-serum\/anchor\": \"$(cat VERSION)\"/@project-serum\/anchor\": \"$1\"/g" \ - -e "s/\"version\": \"$(cat VERSION)\"/\"version\": \"$1\"/g" - -# Potential for collisions in Cargo.lock, use cargo update to update it -cargo update --workspace - -# Insert version number into CHANGELOG.md -sed "${sedi[@]}" -e "s/## \[Unreleased\]/## [Unreleased]\n\n## [$1] - $(date '+%Y-%m-%d')/g" CHANGELOG.md - -pushd ts && yarn && popd -pushd tests && yarn && popd -pushd examples && yarn && pushd tutorial && yarn && popd && popd - -echo $1 > VERSION - -echo "$(git diff --stat | tail -n1) files modified" - -echo " $(cat VERSION) changeset generated, commit and tag" diff --git a/ts/yarn.lock b/yarn.lock similarity index 100% rename from ts/yarn.lock rename to yarn.lock