Compare commits

...

111 Commits

Author SHA1 Message Date
Christian Kamm a97d04ab06
Cli: Don't regenerate idl in read_all_programs() (#2332) 2022-12-23 13:28:35 +00:00
Henry-E e910d44b67
update cli version and add retry cli download on error (#2335) 2022-12-23 10:53:05 +00:00
Jean Marchand (Exotic Markets) 38bbb21c33
lang: remove the state and interface attributes (#2285) 2022-12-22 16:33:44 +00:00
Henry-E 7236c8bb69
Add headings to changelog (#2333) 2022-12-21 11:24:33 +00:00
riordanp 8856aee7df
Allow passing env vars to verifiable build container (#2325) 2022-12-21 11:22:34 +00:00
Henry-E 4bef8c40ef
Switch to using @coral-xyz/anchor packages (#2318) 2022-12-16 13:21:50 +00:00
acheron ba5b55d7f0
Fix 0.26.0 CHANGELOG PR links (#2316) 2022-12-15 18:59:33 +00:00
Henry-E 9f3d66ff8c
post release clean up (#2317)
Co-authored-by: henrye <henry@notanemail>
2022-12-15 18:44:12 +00:00
Henry-E 347c225a27
v0.26.0 (#2315)
* v0.26.0

* update packages

* typedocs

Co-authored-by: henrye <henry@notanemail>
2022-12-15 16:12:22 +00:00
Henry-E b00fe70d1b
add optional accounts to changelog (#2314)
* add optional accounts to changelog

* Update CHANGELOG.md
2022-12-14 20:15:57 +00:00
Henry-E f79f9dab74
Use less heap intensive error mapping (#2313)
* less heap intensive error mapping

* add changelog

* try returning the error

* dumb match statements instead

* missing commas

Co-authored-by: henrye <henry@notanemail>
2022-12-14 17:03:57 +00:00
Jean Marchand (Exotic Markets) fad05805d8
spl: change serum-dex to openbook-dex (#2308)
* Change serum-dex by openbook-dex

* Change serum-dex by openbook-dex in tests

* Fix swap

* Fix cfo

* Downgrade openbook

* Disable cfo and update changelog

* Revert lock file

* minor grammar

* update the pubkey to open dex

* Remove @solana/web3.js from ts package.json

Co-authored-by: Henry-E <henry.elder@adaptcentre.ie>
Co-authored-by: henrye <henry@notanemail>
2022-12-12 16:12:33 +00:00
Adithya Narayan b662ff1460
cli: `anchor account` subcommand to read program account (#1923)
* Setup account subcommand skeleton

* Move IDL deserialization code to syn module

* Change HashMap to serde_json

* Add enum deserialization

* Add account subcommand to docs

* Fix lint

* Add validation for account type

* Fix solana-sdk dependency version

* Fix clippy warnings

* Move IDL deserialization code to cli module

* Remove debug print

* Add integration tests

* Update documentation with example

* Fix clippy warnings

* Fix leftover merge conflict

* run prettier

Co-authored-by: Henry-E <henry.elder@adaptcentre.ie>
Co-authored-by: henrye <henry@notanemail>
2022-12-12 16:11:26 +00:00
Henry-E 03eff348db
only add public enums to the IDL (#2310)
Co-authored-by: henrye <henry@notanemail>
2022-12-12 16:10:47 +00:00
Petr Kozorezov 7527033c7b
Add 'ticks-per-slot' option passing to test validator (#1875)
* add 'ticks-per-slot' option passing to test validator

* add 'ticks-per-slot' option passing to test validator

* add 'ticks-per-slot' option passing to test validator

* cargo fmt and changelog

Co-authored-by: henrye <henry@notanemail>
2022-12-12 16:10:14 +00:00
Sammy Harris 484628070c
Feat: Optional Positional Accounts (#2101)
* optional accounts initial implementation

* cargo fmt

* panic if Account related traits are run on none

* Allow empty accounts to deserialize to None for optional accounts

* implement constraints for optional accounts

* optional accounts to idl gen

* accountstruct helper method

* implemented to_account_metas and infos

* add test program

* Rename optional to is_optional

* added more traits

* added TryKey error

* fix has_one

* update prelude

* is_optional

* add is_optional helper method

* Add TryAccountInfos trait

* improve constraint parser

* initial work on TryToAccountInfo

* Rename to TryToAccountInfo

* finished implementing tryToAccountInfo

* Using program method

* Formatting

* Fix program function call

* Remove function return borrow

* Fix access to program field

* finished implementing tryToAccountInfo

* add exit try_to_account_infos

* descriptive ID path

* try_to_account_info

* fix close constraint

* update test files

* completed typescript optional accounts implementation

* fix try accounts for init

* update tests

* fix to_account_metas

* update tests

* fix linting

* remove types/node

* update yarn.lock maybe?

* update optional test

* update optional test

* update optional rust cli test

* fix linting and tests

* fix tests

* update try_accounts to pass in accs during constraint gen

* Add default impl for TryToAccountInfos

* Removed TryToAccountInfos trait

* Formatting

* remove unneccesary traits and improve constraint gen drastically

* fix exit generation

* clippy

* improve cross check error message

* improve comments

* more comments

* update constraints hopefully good now?

* add new errors to ts client

* add new errors to ts client

* update optional test

* update anchor ts client

* update misc crate

* linting

* temporarily comment out optional rs tests

* update ts

* remove local test files

* linting

* optional client tests

* fix other lints to make the test pass

* remove comments

* remove misc-optional for now

* update optional program

* update optional program and client tests again

* update optional program and client tests again again

* added initialize tests that should pass

* undo unrelated anchor.toml change

* update close on optional program and improve tests

* update optional program again.

* update optional program and optional tests

* fix has one error message

* fix client example tests

* update lockfile

* update lockfile

* regenerate lockfile

* reset lockfile

* reset ts yarn lockfile

* update no caching tests

* update exit codegen to use generate_optional_check

* remove `try_to_account_infos`

* update parser to ignore method calls in constraints

* refactor and improve optional checks in constraints

* add misc-optional program and tests

* enable cpi for optional tests

* Revert "enable cpi for optional tests"

This reverts commit c864cd5d4f019e6bd5f93641e01bd82fc74041d4.

* simplify misc tests

* update version

* fix rust version and resolve merge conflicts

* prevent Option on composite accounts

* hopefully fixed ts stuff?

* hopefully fixed ts stuff?

* testing

* hopefully done?

* update misc test

* fix optional tests

* fix ts

* fix ts again!

* linting urg

* allow-missing-optionals feature

* fix client tests

* add bnjs types to tests

Co-authored-by: febo <febo@kent.ac.uk>
Co-authored-by: Henry-E <henry.elder@adaptcentre.ie>
2022-12-12 15:32:59 +00:00
Henry-E d88a09dbb7
missing tests for cli subsuites (#2307)
Co-authored-by: henrye <henry@notanemail>
2022-12-08 11:08:51 +00:00
Henry-E 09b829d1a3
Identity com bugfix/robust fetch nullable (#2301)
* ts: Fixed `.fetchNullable()` to be robust towards accounts only holding a balance

* update changelog to new PR id

* prettier

Co-authored-by: Martin Riedel <web@riedel-it.de>
Co-authored-by: henrye <henry@notanemail>
2022-12-07 10:54:45 +00:00
Henry-E 7d7747cc90
Build borsh package first, then build anchor package (#2306)
* Add build borsh package to yarn build anchor package

* update changelog with rsh stuff; clean up full stops

* make it even clearer in the changelog

* missing full stop

Co-authored-by: henrye <henry@notanemail>
2022-12-07 10:46:29 +00:00
skrrb 6814ce646e
cli: run a subset of the test suites (#1864)
* cli: run subset of tests with --run

* changelog

* test

* fix run test path

* test: added missing package.json

* cli: run subset of tests with --run

* changelog

* test

* fix run test path

* test: added missing package.json

Co-authored-by: Armani Ferrante <armaniferrante@gmail.com>
Co-authored-by: henrye <henry@notanemail>
2022-12-06 21:42:10 +00:00
Tommy Johnson c798821947
add geyser_plugin_config support for anchor localnet (#2016)
* add geyser_plugin_config support for anchor localnet

* run cargo fmt

* remove duplicate code that came from #2150

Co-authored-by: Tommy Johnson <tommy@psyoptions.io>
2022-12-06 21:15:48 +00:00
metta0714 04b988299a
cli: fix windows yarn error (#1965)
* fix windows yarn error

* add install_node_modules function
2022-12-06 12:20:19 +00:00
Nicolas Bayle 57434279fd
Don't use wallet object for account.createInstruction function 2112 (#2116)
* Don't use wallet object for account.createInstruction function 2112

* added publicKey type check

* yarn test pass

* Don't use wallet object for account.createInstruction function 2112

* added publicKey type check

* yarn test pass

Co-authored-by: henrye <henry@notanemail>
2022-12-06 12:14:01 +00:00
Henry-E b6ad53f628
provider.sendAll fetches transaction error logs (#2302)
Just like provider.sendAndConfirm

Co-authored-by: Tristyn <tristynstimpson@gmail.com>
Co-authored-by: henrye <henry@notanemail>
2022-12-06 12:05:38 +00:00
Henry-E 1bb1969d60
fix breaking change on null or undefined wallet (#2303)
Co-authored-by: henrye <henry@notanemail>
2022-12-06 12:05:12 +00:00
Italo Casas 11af9a6624
Adding support for idl.json (#2114) 2022-12-05 17:41:02 +00:00
Pierre 98f26ce8cf
feat: ata create idempotent and drop rent (#2153)
* feat: ata create idempotent and drop rent

* add the token program id

* cargo fmt

Co-authored-by: Arrowana <8245419+Arrowana@users.noreply.github.com>
Co-authored-by: henrye <henry@notanemail>
2022-12-05 17:11:42 +00:00
Nicholas Clarke 66e45327b9
Add optional flag to parseLogs to throw an error on decoding failure (#2043)
* Add optional flag to parseLogs to throw an error on decoding failure

* update changelog

Co-authored-by: henrye <henry@notanemail>
2022-12-05 15:50:46 +00:00
skrrb fb714b9343
fix anchor keys list command (#2063)
* fix anchor keys list command

* changelog

Co-authored-by: henrye <henry@notanemail>
2022-12-05 15:50:31 +00:00
Lucas Steuernagel 0ba2195ec7
Add support for 256-bit integers in the IDL (#2260)
* Add support for 256-bit integers in the IDL

Signed-off-by: Lucas Steuernagel <lucas.tnagel@gmail.com>

* Fix lint warnings

Signed-off-by: Lucas Steuernagel <lucas.tnagel@gmail.com>
2022-12-05 15:45:07 +00:00
Kyle Gilliam 23c9717af1
Add option for jest test scaffolding (#2200)
* Add in option for jest scaffolding

* Add tests for Jest in both JS/TS

* Clone moved value; use new function name

* Remove invalid tests
2022-12-05 15:11:56 +00:00
surfertas 50724df110
allow ability to pass in feePayer to Provider methods [WIP] (#2186)
* allow ability to pass in feePayer to Provider methods

* align comments

* prettier

* check if feePayer set, handle if not

* remove unnecessary spaces in comments

* update changelog

* strict equality check

* use null check

* use logical or

* use logical or

Co-authored-by: Henry-E <henry.elder@adaptcentre.ie>
2022-12-05 12:45:27 +00:00
acheron bbeffd58f7
Use @coral-xyz/borsh package (#2299) 2022-12-05 10:47:36 +00:00
Henry-E 66e4295fe0
drop rust version to allow earlier CLIs to compile (#2289)
* drop back rust version to allow earlier cli to compile

* update solana program to 1.13.5 latest mainnet version

Co-authored-by: henrye <henry@notanemail>
2022-11-29 11:36:48 +00:00
Matthew Callens 2768112cef
update borsh package scripts (#2292) 2022-11-28 13:00:54 -05:00
Matthew Callens fa2f18a2c0
update borsh package builds and config (#2291) 2022-11-28 12:54:49 -05:00
Henry-E 35f4e26ef8
add borsh + u256 changes + update to v0.2.6 (#2290)
Co-authored-by: henrye <henry@notanemail>
2022-11-28 12:20:13 -05:00
Darren 704fdce0cd
Fixed compile time issue on p1 wallet (#2066) (#2067) 2022-11-28 16:50:42 +00:00
Jean Marchand (Exotic Markets) ca7c3f0aca
Bump actions version (#2283) 2022-11-25 15:14:53 +00:00
Henry-E 19e00e5d8f
Don't allow the payer to be initialized as a program account (#2284)
* don't allow people to initialize the payer as a program account

* update changelog

* accidentally commented out part of another test

Co-authored-by: henrye <henry@notanemail>
2022-11-25 15:12:58 +00:00
cyphersnake a73bd728fb
lang: Add `Owner` & `Discriminator` implementation for ix structures (#2085)
lang: Add into `Discriminator` trait constant `DISCRIMINATOR`

So that during match instructions or other entities there is no explicit instruction call of `discriminator()`

lang: Add `Owner` impl to instructions

Co-authored-by: Mikhail Gorbachev <m.gorbachev@joinsprouttherapy.com>
2022-11-24 16:19:39 +00:00
Henry-E 686c97e45b
Revert "client: allow execution stack to have multiple program ids (#1954)" (#2282)
This reverts commit 52b32d2459.
2022-11-24 15:16:37 +00:00
Jean Marchand (Exotic Markets) ca52121a6b
clippy test if it is just a cache issue (#2280)
* test if it is just a cache issue

* fix lint
2022-11-24 14:14:19 +00:00
Arjun 52b32d2459
client: allow execution stack to have multiple program ids (#1954)
* allow execution stack to have multiple program ids

* implement clippy recommendations
2022-11-23 11:30:02 +00:00
Jean Marchand (Exotic Markets) c0d3ff2d77
tests: Fix multiple suites example (#2279) 2022-11-23 10:48:46 +00:00
Matthew Callens be67192f2b
add vercel script for determining deployment skipping (#2277) 2022-11-22 11:49:12 -05:00
Jean Marchand (Exotic Markets) b8cda8a717
cli: Allow custom cluster config (#2271)
* cli: Allow custom cluster config

* Update changelog

* Downgrade the code

* Fix rust fmt

Co-authored-by: Henry-E <henry.elder@adaptcentre.ie>
2022-11-22 16:33:44 +00:00
Henry-E 5291a7b6ff
Update rust to v1.62 (#2272)
* update rust from v1.56 to v1.65

* remove unnecessary borrows to appease clippy

* stop clippy complaining for now

* use 1.64 instead because of CI stuff

* update CHANGLEOG

* just seeing if the cli version is affecting the available rust version

* ok, back to 1.62

Co-authored-by: henrye <henry@notanemail>
2022-11-22 16:13:45 +00:00
Henry-E afbbbb0330
Trying to stop `zero-copy` test failing (#2274)
* trying out different solutions to zero copy issue (none work yet)

* using a newer version of the CLI should fix the issue

* re update solana client version

* add cargo.lock for some reason

* comment out broken test, get help later

* clippy doesn't like the new option

Co-authored-by: henrye <henry@notanemail>
2022-11-22 15:40:07 +00:00
Callum McIntyre 982799b7e6
Fix panic parsing seeds when account has qualified path (#2268)
* Fix panic parsing seeds when account has qualified path

* Add a test for seed derivation with account using type with path
2022-11-22 11:10:05 +00:00
Jac0xb 1ec2b7dfcd
Implement functions to return associated context when fetching accounts. (#2237) 2022-11-22 10:54:37 +00:00
Proph3t d441a3e6de
fix: allow ts to work with account names that have multiple uppercase chars in a row (#2253) 2022-11-22 10:51:07 +00:00
Jean Marchand (Exotic Markets) 89b047b21e
chore: Bump non-solana dependencies (#2270)
* chore: Bump non-solana dependencies

* Fix project generation and client example

* Fix snake case project name

* Update lock file
2022-11-22 10:42:43 +00:00
Luis Perrone fb6508addf
Remove terser for react-native support (#2263)
Co-authored-by: Luis Perrone <perronef5@users.noreply.github.com>
2022-11-21 10:43:01 +00:00
Jean Marchand (Exotic Markets) 8ce18c36db
spl: Update instructions and remove rent from constraints (#2265)
* Update spl instructions and remove the rent sys_var from constraints

* Fix initialize mint

* Update changelog and fix examples

* Remove oversights
2022-11-19 12:59:14 +00:00
Matthew Callens 5e3ebcfde3
upgrade mpl token metadata version (#2264) 2022-11-17 22:41:22 -05:00
Tracy Adams ee026aa8f5
Add geyser_plugin_config test-validator param (#2150) 2022-11-17 14:21:16 +00:00
acheron af115999c6
ts: Remove SPL coders from Anchor package (#2155)
* ts: Remove SPL coders from Anchor package

* Replace old SPL custom coder tests

* Build and link new SPL packages before tests and decode token account for AccountStore

* Fix Github actions ts setup

* Fix u64 buffer length

* Update submodules

* Update CHANGELOG

* Checkout correct submodule commits

* Add filler program to generate idls in order for anchor test to pass
2022-11-17 14:17:22 +00:00
filipzeta 5c474c6dfb
Bugfix multiple event listeners with the same name (#2165)
* Bugfix multiple event listeners with the same name

* Changelog

* changelog
2022-11-16 16:33:01 +00:00
Jean Marchand b107cbe94f
lang: Fix parsing for bytes literals in the IDL (#2261)
* lang: Fix parsing of some constants

* Fix lint + changelog
2022-11-16 15:59:09 +00:00
Deep Mehta e8618a588f
replace getRecentBlockhash to getLatestBlockhash (#2205) 2022-11-14 16:59:36 +00:00
omahs 43ab991852
Fix: typos (#2236)
Fix: typos
2022-11-14 16:57:41 +00:00
Noah Prince feff131ab0
fix: Nested pda types and account resolution for pdas (#2259) 2022-11-13 21:45:41 -05:00
Noah Prince 91a2b7ec96
fix: 0 is falsey for arg filling (#2258) 2022-11-13 08:33:04 -05:00
Henry-E 7a0a37e0f8
Update solana to version 1.13.3 (#2239)
* upgrade solana version to 1.13.3

* get everything to compile correctly

* update to using struct constructor to avoid deprecated field usage

* update deprecated functions

* more fixes to pass tests

* update auction-house submodule commit

* update the swap submodule

Co-authored-by: henrye <henry@notanemail>
2022-10-29 15:23:13 +01:00
Proph3t 5532a0f087
Update the test methods in the examples directory (#2238)
* updated, outdated methods of basic-0,1,2,3

* chore: run prettier on changes

Co-authored-by: vpin09 <sainivipin1998@gmail.com>
2022-10-28 12:12:38 +01:00
Sammy Harris fa1249836e
Feat: Update AccountsClose to be safe to call manually (#2209)
* fix other lints to make the test pass

(cherry picked from commit d6e43c1ed6a176caa60dfdabe406ec125c3d34cf)

* update close to make it safe to call manually

* fix test script

* re-add safety warnings for deprecated account types

* update close checking logic

* readd logic for deprecated methods

* add additional checks to account_loader in exit
2022-10-21 17:06:54 +01:00
Henry-E 46f0b67bff
Revert "updated, outdated methods of basic-0,1,2,3 (#2144)" (#2235)
This reverts commit 6a46bedaf1.
2022-10-21 10:32:50 +01:00
Vipin Saini 6a46bedaf1
updated, outdated methods of basic-0,1,2,3 (#2144)
Tests aren't running properly on this so going to merge and then revert if tests fail
2022-10-21 10:19:18 +01:00
Sammy Harris d653226f40
chore: Speed up tests (#2233) 2022-10-18 14:26:09 -04:00
Sammy Harris e866d45af9
update github runners (#2232) 2022-10-18 12:13:55 -04:00
Dana 8719facf60
Add libssl-dev to additional deps (#2206) 2022-10-18 10:14:25 -04:00
Totoro 8e66d5bb5f
Add support for idl enum tuples, enchance types (#2185) 2022-10-18 10:13:23 -04:00
Ryan Mehta 0f10a99995
Add missing closing parenthesis (#2230) 2022-10-17 22:23:38 -04:00
eydelrivero b9b2737c18
Improved error message when verify command fails on missing program or IDL (#2225) 2022-10-14 00:42:29 -04:00
Sammy Harris 3da28dbc5f
fix: ignore non Accounts in safety checks (#2201) 2022-10-13 23:08:02 -04:00
Sammy Harris 5647510615
chore: bump @solana/web3.js in anchor-ts (#2227) 2022-10-13 23:06:51 -04:00
Henry-E d83efcdc3a
update solana web3 package to 1.64.0 (#2223)
* update solana web3 package to 1.64.0

* fix some missing packages; update changelog

* fix weird prettier thing messing with changelog

* also upgrade the yarn.lock in tests
2022-10-13 11:20:32 +01:00
Noah Prince 52a7e1e278
bug: use program id from account, not idl, when resolving relations (#2221) 2022-10-11 12:44:41 -04:00
Noah Prince fd467df932
feat: Derive has_one's from other programs, recursively search custom resolvers (#2208) 2022-10-08 00:33:18 -04:00
Henry-E 862575a649
new clippy lints for rust 1.63 (#2207) 2022-10-06 14:26:22 -04:00
Han Yang 3970847e75
stake (#2195) 2022-09-24 14:11:12 -04:00
stateofnonreturn 7f243ce66c
the-accounts-struct.md improvement (#2156) 2022-09-24 14:10:23 -04:00
Noah Prince 6f3877f36c
feat: Update seeds inference to allow nested user defined structs as part of the seeds (#2198) 2022-09-23 22:14:57 -04:00
Noah Prince d929589013
feat: Recursively derive const pubkeys and add custom resolver export (#2196) 2022-09-22 12:26:13 -04:00
Noah Prince 436791b039
feat: Recursively derive seeds and add custom account resolver (#2194) 2022-09-21 16:32:24 -04:00
Gajesh Naik 2a07d841c6
implemented more token_metadata wrappers (#2179) 2022-09-13 22:27:14 -04:00
0xWoo e019ba2199
metadata: verify_collection & refactor results (#2151) 2022-09-13 18:05:24 -04:00
Totoro 5ae52cae13
fix IDL types parsing (#2176) 2022-09-12 08:04:11 -04:00
Matthew Callens f155996c3c
add wrappers for mpl `sign_metadata` and `remove_creator_verification` (#2175)
* add wrappers for mpl creator verification ix

* update changelog
2022-09-10 16:32:29 -04:00
Matthew Callens 4c24c5d7b1
add `update_primary_sale_happened_via_token` ix wrapper for `metadata` (#2173)
* add update_primary_sale_happened_via_token ix wrapper

* update changelog
2022-09-10 15:57:27 -04:00
Noah Prince e69e50daaf
feat: Add has_one relations inference so you don't need to pass accounts that are referenced by a has_one (#2160) 2022-09-10 12:52:59 -04:00
surfertas 0c70d183ef
add freeze and thaw delegated account instructions (#2164) 2022-09-06 10:30:50 -04:00
Pierre 8ee4600785
feat: Use new canonical close account pattern (#2169) 2022-09-06 09:33:17 -04:00
Sammy Harris c1c4f6c335
fix new clippy lints (#2148) 2022-08-23 18:57:32 -04:00
Matthew Callens 586094156b
ci: disable breaking nonRentExempt test (#2147) 2022-08-23 16:38:31 -04:00
acheron 5a025b949e
ts: Add SPL Anchor packages (#2143) 2022-08-22 21:57:56 -04:00
dependabot[bot] a59aa5f6f3
build(deps): bump terser from 5.12.1 to 5.14.2 in /ts (#2136) 2022-08-16 19:16:39 -04:00
dependabot[bot] 7bdbb03cbd
build(deps): bump minimist from 1.2.5 to 1.2.6 in /ts (#2135) 2022-08-16 19:16:24 -04:00
Sammy Harris 290b2aa43e
feat: constants declared in impl blocks in seeds (#2128) 2022-08-14 12:42:16 -04:00
tomland123 fc9c680d6a
Update installation.md (#2127) 2022-08-14 01:00:44 -04:00
Sammy Harris 3a0deba901
fix: IDL gen byte string lit parsing (#2125) 2022-08-13 22:57:59 -04:00
chinepun bf11f811d7
Adds purpose of seeds = true does in an Anchor.toml file (#2124) 2022-08-13 20:38:48 -04:00
Matthew Callens 7101ece803
spl: import TokenMetadataAccount (#2123) 2022-08-13 15:27:10 -04:00
Armani Ferrante e134cce3f3
spl: import deref (#2122) 2022-08-13 14:59:49 -04:00
Kevin Rodríguez d3ee9b529a
spl: adding metadata account type (#2014) 2022-08-13 14:44:38 -04:00
acheron 9457180a0a
ts: fix fetching all accounts when using a custom coder (#2107) 2022-08-12 14:06:14 -04:00
Matthew Callens 000e74ed48
spl: add create metadata v3 and set collection size ix (#2119)
* add create metadata v3 and set collection size ix

* update changelog
2022-08-12 12:11:43 -04:00
Luis Cossío ebe88187e3
Add workspace section to `Anchor.toml` reference (#2105) 2022-08-11 14:29:22 -04:00
Michal Rostecki 93332766f1
docs: Update links to the repository (#2103) 2022-08-04 13:05:15 -04:00
Sammy Harris 68362aca58
feat: Adds transaction function to RequestBuilder (#1985) 2022-07-30 14:53:10 -04:00
633 changed files with 54333 additions and 10817 deletions

1
.clippy.toml Normal file
View File

@ -0,0 +1 @@
large-error-threshold = 1_000_000

View File

@ -3,19 +3,27 @@ description: "Setup Solana"
runs:
using: "composite"
steps:
- uses: actions/cache@v2
name: Cache Solana Tool Suite
id: cache-solana
with:
path: |
~/.cache/solana/
~/.local/share/solana/
key: solana-${{ runner.os }}-v0000-${{ env.SOLANA_CLI_VERSION }}
- run: sh -c "$(curl -sSfL https://release.solana.com/v${{ env.SOLANA_CLI_VERSION }}/install)"
shell: bash
- run: echo "/home/runner/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH
shell: bash
- run: solana-keygen new --no-bip39-passphrase
shell: bash
- run: solana config set --url localhost
- uses: actions/cache@v3
name: Cache Solana Tool Suite
id: cache-solana
with:
path: |
~/.cache/solana/
~/.local/share/solana/
key: solana-${{ runner.os }}-v0000-${{ env.SOLANA_CLI_VERSION }}
- uses: nick-fields/retry@v2
with:
retry_wait_seconds: 300
timeout_minutes: 2
max_attempts: 10
retry_on: error
shell: bash
command: sh -c "$(curl -sSfL https://release.solana.com/v${{ env.SOLANA_CLI_VERSION }}/install)"
- run: sh -c "$(curl -sSfL https://release.solana.com/v${{ env.SOLANA_CLI_VERSION }}/install)"
shell: bash
- run: echo "/home/runner/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH
shell: bash
- run: solana-keygen new --no-bip39-passphrase
shell: bash
- run: solana config set --url localhost
shell: bash

View File

@ -3,27 +3,32 @@ description: "Setup ts"
runs:
using: "composite"
steps:
- uses: actions/setup-node@v2
with:
node-version: ${{ env.NODE_VERSION }}
- uses: actions/cache@v2
name: Cache Typescript node_modules
id: cache-typescript-node-modules
with:
path: |
./ts/node_modules/
key: solana-${{ runner.os }}-v0000-${{ env.NODE_VERSION }}-${{ hashFiles('./ts/**/yarn.lock') }}
- uses: actions/cache@v2
name: Cache Typescript Dist
id: cache-typescript-dist
with:
path: |
./ts/dist/
key: solana-${{ runner.os }}-v0000-${{ env.NODE_VERSION }}-${{ hashFiles('./ts/**/*.ts') }}
- run: cd ts && yarn --frozen-lockfile && yarn build:node && yarn link && cd ../
shell: bash
- run: cd examples/tutorial && yarn link @project-serum/anchor && yarn --frozen-lockfile && cd ../../
shell: bash
- run: cd tests && yarn link @project-serum/anchor && yarn --frozen-lockfile && cd ..
shell: bash
- uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
- uses: actions/cache@v3
name: Cache Typescript node_modules
id: cache-typescript-node-modules
with:
path: |
./ts/node_modules/
key: solana-${{ runner.os }}-v0000-${{ env.NODE_VERSION }}-${{ hashFiles('./ts/**/yarn.lock') }}
- uses: actions/cache@v3
name: Cache Typescript Dist
id: cache-typescript-dist
with:
path: |
./ts/dist/
key: solana-${{ runner.os }}-v0000-${{ env.NODE_VERSION }}-${{ hashFiles('./ts/**/*.ts') }}
- run: cd ts/packages/borsh && yarn --frozen-lockfile && yarn build && cd ../../../
shell: bash
- run: cd ts/packages/anchor && yarn --frozen-lockfile && yarn build:node && yarn link && cd ../../../
shell: bash
- run: cd ts/packages/spl-associated-token-account && yarn --frozen-lockfile && yarn build:node && yarn link && cd ../../../
shell: bash
- run: cd ts/packages/spl-token && yarn --frozen-lockfile && yarn build:node && yarn link && cd ../../../
shell: bash
- run: cd examples/tutorial && yarn link @coral-xyz/anchor && yarn --frozen-lockfile && cd ../../
shell: bash
- run: cd tests && yarn link @coral-xyz/anchor && yarn link @coral-xyz/spl-associated-token-account && yarn link @coral-xyz/spl-token && yarn --frozen-lockfile && cd ..
shell: bash

View File

@ -5,18 +5,18 @@ on:
branches:
- master
env:
SOLANA_CLI_VERSION: 1.10.29
SOLANA_CLI_VERSION: 1.14.11
NODE_VERSION: 17.0.1
jobs:
test-core:
name: Core Tests
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/actions/setup/
- uses: actions/setup-node@v2
- uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
- run: cargo build
@ -25,9 +25,11 @@ jobs:
- run: cargo test
# using singlethreaded testing for avm so that tests that change files do not conflict with each other
- run: cd avm && cargo fmt -- --check && cargo clippy --all-targets -- -D warnings && cargo test -- --test-threads=1
- run: cd ts && yarn --frozen-lockfile
- run: cd ts && yarn test
- run: cd ts && yarn lint
# Init local borsh package
- run: cd ts/packages/borsh && yarn --frozen-lockfile && yarn build
- run: cd ts/packages/anchor && yarn --frozen-lockfile
- run: cd ts/packages/anchor && yarn test
- run: cd ts/packages/anchor && yarn lint
- run: cd examples/tutorial && yarn --frozen-lockfile
- run: cd examples/tutorial && yarn lint
- run: cd tests && yarn --frozen-lockfile
@ -36,15 +38,15 @@ jobs:
setup-anchor-cli:
name: Setup Anchor cli
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/actions/setup/
- run: cargo install --path cli anchor-cli --locked --force
- run: chmod +x ~/.cargo/bin/anchor
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v3
with:
name: anchor-binary-no-caching
path: ~/.cargo/bin/anchor
@ -54,11 +56,11 @@ jobs:
test-examples:
needs: setup-anchor-cli
name: Examples Test
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: actions/download-artifact@v2
- uses: actions/checkout@v3
- uses: actions/download-artifact@v3
with:
name: anchor-binary-no-caching
path: ~/.cargo/bin/
@ -73,12 +75,14 @@ jobs:
setup-client-example:
needs: setup-anchor-cli
name: Setup Client Example Test
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
node:
- path: tests/optional/
name: optional.so
- path: tests/events/
name: events.so
- path: examples/tutorial/basic-4/
@ -88,18 +92,18 @@ jobs:
- path: tests/composite/
name: composite.so
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/actions/setup/
- uses: ./.github/actions/setup-solana/
- uses: actions/download-artifact@v2
- uses: actions/download-artifact@v3
with:
name: anchor-binary-no-caching
path: ~/.cargo/bin/
- run: chmod +rwx ~/.cargo/bin/anchor
- run: cd ${{ matrix.node.path }} && anchor build --skip-lint
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v3
with:
name: ${{ matrix.node.name }}
path: ${{ matrix.node.path }}target/deploy/${{ matrix.node.name }}
@ -108,32 +112,36 @@ jobs:
test-client-example:
needs: setup-client-example
name: Client Example Test
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/actions/setup/
- uses: ./.github/actions/setup-ts/
- uses: actions/download-artifact@v2
- uses: actions/download-artifact@v3
with:
name: anchor-binary-no-caching
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/anchor
- uses: actions/download-artifact@v3
with:
name: optional.so
path: tests/optional/target/deploy/
- uses: actions/download-artifact@v2
with:
name: events.so
path: tests/events/target/deploy/
- uses: actions/download-artifact@v2
- uses: actions/download-artifact@v3
with:
name: basic_4.so
path: examples/tutorial/basic-4/target/deploy/
- uses: actions/download-artifact@v2
- uses: actions/download-artifact@v3
with:
name: basic_2.so
path: examples/tutorial/basic-2/target/deploy/
- uses: actions/download-artifact@v2
- uses: actions/download-artifact@v3
with:
name: composite.so
path: tests/composite/target/deploy/
@ -144,15 +152,15 @@ jobs:
test-bpf-upgradeable-state:
needs: setup-anchor-cli
name: Test tests/bpf-upgradeable-state
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/actions/setup/
- uses: ./.github/actions/setup-ts/
- uses: ./.github/actions/setup-solana/
- uses: actions/download-artifact@v2
- uses: actions/download-artifact@v3
with:
name: anchor-binary-no-caching
path: ~/.cargo/bin/
@ -168,66 +176,66 @@ jobs:
- run: cd tests/bpf-upgradeable-state && npx tsc --noEmit
- uses: ./.github/actions/git-diff/
# this test exists to make sure that anchor
# checks rent correctly for legacy accounts
# that don't have to be rent-exempt
test-misc-non-rent-exempt:
# the anchor cli is built with a different solana version
# but that's fine since it's just the cli
needs: setup-anchor-cli
name: Test tests/misc/nonRentExempt
runs-on: ubuntu-18.04
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/setup/
- uses: ./.github/actions/setup-ts/
# using an outdated validator but that
# is ok as long as the test doesn't
# include newer incompatible features
- run: sh -c "$(curl -sSfL https://release.solana.com/v1.8.14/install)"
shell: bash
- run: echo "/home/runner/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH
shell: bash
- run: solana-keygen new --no-bip39-passphrase
shell: bash
- run: solana config set --url localhost
shell: bash
- uses: actions/download-artifact@v2
with:
name: anchor-binary-no-caching
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/anchor
# # this test exists to make sure that anchor
# # checks rent correctly for legacy accounts
# # that don't have to be rent-exempt
# test-misc-non-rent-exempt:
# # the anchor cli is built with a different solana version
# # but that's fine since it's just the cli
# needs: setup-anchor-cli
# name: Test tests/misc/nonRentExempt
# runs-on: ubuntu-latest
# timeout-minutes: 30
# steps:
# - uses: actions/checkout@v3
# - uses: ./.github/actions/setup/
# - uses: ./.github/actions/setup-ts/
# # using an outdated validator but that
# # is ok as long as the test doesn't
# # include newer incompatible features
# - run: sh -c "$(curl -sSfL https://release.solana.com/v1.8.14/install)"
# shell: bash
# - run: echo "/home/runner/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH
# shell: bash
# - run: solana-keygen new --no-bip39-passphrase
# shell: bash
# - run: solana config set --url localhost
# shell: bash
# - uses: actions/download-artifact@v3
# with:
# name: anchor-binary-no-caching
# path: ~/.cargo/bin/
# - run: chmod +x ~/.cargo/bin/anchor
- run: cd tests/misc && yarn --frozen-lockfile
- run: cd tests/misc
- run: cd tests/misc && chmod +x ci.sh && ./ci.sh
- run: cd tests/misc && anchor test --skip-lint
# - run: cd tests/misc && yarn --frozen-lockfile
# - run: cd tests/misc
# - run: cd tests/misc && chmod +x ci.sh && ./ci.sh
# - run: cd tests/misc && anchor test --skip-lint
test-anchor-init:
needs: setup-anchor-cli
name: Test Anchor Init
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/actions/setup/
- uses: ./.github/actions/setup-ts/
- uses: ./.github/actions/setup-solana/
- uses: actions/download-artifact@v2
- uses: actions/download-artifact@v3
with:
name: anchor-binary-no-caching
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/anchor
- run: cd "$(mktemp -d)" && anchor init hello-anchor && cd hello-anchor && yarn link @project-serum/anchor && yarn && anchor test && yarn lint:fix
- run: cd "$(mktemp -d)" && anchor init hello-anchor && cd hello-anchor && yarn link @coral-xyz/anchor && yarn && anchor test && yarn lint:fix
- uses: ./.github/actions/git-diff/
test-programs:
needs: setup-anchor-cli
name: Test ${{ matrix.node.path }}
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
@ -243,11 +251,9 @@ jobs:
path: spl/token-proxy
- cmd: cd tests/multisig && anchor test --skip-lint
path: tests/multisig
- cmd: cd tests/interface && anchor test --skip-lint
path: tests/interface
- cmd: cd tests/lockup && anchor test --skip-lint
path: tests/lockup
- cmd: cd tests/swap/deps/serum-dex/dex && cargo build-bpf -- --locked && cd ../../../ && anchor test --skip-lint
# - cmd: cd tests/lockup && anchor test --skip-lint
# path: tests/lockup
- cmd: cd tests/swap/deps/openbook-dex/dex && cargo build-bpf -- --locked && cd ../../../ && anchor test --skip-lint
path: tests/swap
- cmd: cd tests/escrow && anchor test --skip-lint && npx tsc --noEmit
path: tests/escrow
@ -271,8 +277,8 @@ jobs:
path: tests/chat
- cmd: cd tests/ido-pool && anchor test --skip-lint
path: tests/ido-pool
- cmd: cd tests/cfo && anchor run test-with-build && cd deps/stake && git checkout Cargo.lock && cd ../swap && git checkout Cargo.lock
path: tests/cfo
# - cmd: cd tests/cfo && anchor run test-with-build && cd deps/stake && git checkout Cargo.lock && cd ../swap && git checkout Cargo.lock
# path: tests/cfo
- cmd: cd tests/auction-house && yarn --frozen-lockfile && anchor test --skip-lint && git checkout Cargo.lock
path: tests/auction-house
- cmd: cd tests/floats && yarn --frozen-lockfile && anchor test --skip-lint && npx tsc --noEmit
@ -287,17 +293,25 @@ jobs:
path: tests/cpi-returns
- cmd: cd tests/multiple-suites && anchor test --skip-lint && npx tsc --noEmit
path: tests/multiple-suites
- cmd: cd tests/optional && anchor test --skip-lint && npx tsc --noEmit
path: tests/optional
- cmd: cd tests/multiple-suites-run-single && anchor test --skip-lint --run tests/should-run && npx tsc --noEmit
path: tests/multiple-suites-run-single
- cmd: cd tests/pda-derivation && anchor test --skip-lint && npx tsc --noEmit
path: tests/pda-derivation
- cmd: cd tests/relations-derivation && anchor test --skip-lint && npx tsc --noEmit
path: tests/relations-derivation
- cmd: cd tests/anchor-cli-idl && ./test.sh
path: tests/anchor-cli-idl
- cmd: cd tests/anchor-cli-account && anchor test --skip-lint
path: tests/anchor-cli-account
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/actions/setup/
- uses: ./.github/actions/setup-ts/
- uses: ./.github/actions/setup-solana/
- uses: actions/download-artifact@v2
- uses: actions/download-artifact@v3
with:
name: anchor-binary-no-caching
path: ~/.cargo/bin/

View File

@ -8,21 +8,22 @@ on:
branches:
- master
env:
SOLANA_CLI_VERSION: 1.10.29
SOLANA_CLI_VERSION: 1.14.11
NODE_VERSION: 17.0.1
CARGO_PROFILE: debug
jobs:
test-core:
name: Core Tests
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/actions/setup/
- uses: actions/setup-node@v2
- uses: actions/setup-node@v3
with:
node-version: ${{ env.NODE_VERSION }}
- uses: actions/cache@v2
- uses: actions/cache@v3
name: Cache Cargo registry + index
id: cache-cargo-build
with:
@ -39,9 +40,10 @@ jobs:
- run: cargo test
# using singlethreaded testing for avm so that tests that change files do not conflict with each other
- run: cd avm && cargo fmt -- --check && cargo clippy --all-targets -- -D warnings && cargo test -- --test-threads=1
- run: cd ts && yarn --frozen-lockfile
- run: cd ts && yarn test
- run: cd ts && yarn lint
- run: cd ts/packages/borsh && yarn --frozen-lockfile && yarn build
- run: cd ts/packages/anchor && yarn --frozen-lockfile
- run: cd ts/packages/anchor && yarn test
- run: cd ts/packages/anchor && yarn lint
- run: cd examples/tutorial && yarn --frozen-lockfile
- run: cd examples/tutorial && yarn lint
- run: cd tests && yarn --frozen-lockfile
@ -50,13 +52,13 @@ jobs:
setup-anchor-cli:
name: Setup Anchor cli
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/actions/setup/
- uses: actions/cache@v2
- uses: actions/cache@v3
name: Cache Cargo registry + index
id: cache-anchor
with:
@ -66,10 +68,13 @@ jobs:
~/.cargo/registry/cache/
~/.cargo/git/db/
./target/
key: cargo-${{ runner.os }}-anchor-${{ hashFiles('**/Cargo.lock') }}
key: cargo-${{ runner.os }}-${{ env.CARGO_PROFILE }}-anchor-${{ hashFiles('**/Cargo.lock') }}
- run: cargo install --path cli anchor-cli --locked --force --debug
if: env.CARGO_PROFILE == 'debug'
- run: cargo install --path cli anchor-cli --locked --force
if: env.CARGO_PROFILE != 'debug'
- run: chmod +x ~/.cargo/bin/anchor
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v3
with:
name: anchor-binary
path: ~/.cargo/bin/anchor
@ -79,11 +84,11 @@ jobs:
test-examples:
needs: setup-anchor-cli
name: Examples Test
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: actions/download-artifact@v2
- uses: actions/checkout@v3
- uses: actions/download-artifact@v3
with:
name: anchor-binary
path: ~/.cargo/bin/
@ -92,31 +97,31 @@ jobs:
- uses: ./.github/actions/setup/
- uses: ./.github/actions/setup-solana/
- uses: ./.github/actions/setup-ts/
- uses: actions/cache@v2
- uses: actions/cache@v3
name: basic-0 cache
id: cache-basic-0
with:
path: ./examples/tutorial/basic-0/target
key: cargo-${{ runner.os }}-${{ hashFiles('./examples/tutorial/basic-0/**/Cargo.toml') }}-${{ env.SOLANA_CLI_VERSION }}-${{ hashFiles('**/Cargo.lock') }}
- uses: actions/cache@v2
- uses: actions/cache@v3
name: basic-1 cache
id: cache-basic-1
with:
path: ./examples/tutorial/basic-1/target
key: cargo-${{ runner.os }}-${{ hashFiles('./examples/tutorial/basic-1/**/Cargo.toml') }}-${{ env.SOLANA_CLI_VERSION }}-${{ hashFiles('**/Cargo.lock') }}
- uses: actions/cache@v2
- uses: actions/cache@v3
name: basic-2 cache
id: cache-basic-2
with:
path: ./examples/tutorial/basic-2/target
key: cargo-${{ runner.os }}-${{ hashFiles('./examples/tutorial/basic-2/**/Cargo.toml') }}-${{ env.SOLANA_CLI_VERSION }}-${{ hashFiles('**/Cargo.lock') }}
- uses: actions/cache@v2
- uses: actions/cache@v3
name: basic-3 cache
id: cache-basic-3
with:
path: ./examples/tutorial/basic-3/target
key: cargo-${{ runner.os }}-${{ hashFiles('./examples/tutorial/basic-3/**/Cargo.toml') }}-${{ env.SOLANA_CLI_VERSION }}-${{ hashFiles('**/Cargo.lock') }}
- uses: actions/cache@v2
- uses: actions/cache@v3
name: basic-4 cache
id: cache-basic-4
with:
@ -128,12 +133,14 @@ jobs:
setup-client-example:
needs: setup-anchor-cli
name: Setup Client Example Test
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
node:
- path: tests/optional/
name: optional.so
- path: tests/events/
name: events.so
- path: examples/tutorial/basic-4/
@ -143,18 +150,18 @@ jobs:
- path: tests/composite/
name: composite.so
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/actions/setup/
- uses: ./.github/actions/setup-solana/
- uses: actions/download-artifact@v2
- uses: actions/download-artifact@v3
with:
name: anchor-binary
path: ~/.cargo/bin/
- run: chmod +rwx ~/.cargo/bin/anchor
- run: cd ${{ matrix.node.path }} && anchor build --skip-lint
- uses: actions/upload-artifact@v2
- uses: actions/upload-artifact@v3
with:
name: ${{ matrix.node.name }}
path: ${{ matrix.node.path }}target/deploy/${{ matrix.node.name }}
@ -163,36 +170,40 @@ jobs:
test-client-example:
needs: setup-client-example
name: Client Example Test
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/actions/setup/
- uses: ./.github/actions/setup-ts/
- uses: actions/download-artifact@v2
- uses: actions/download-artifact@v3
with:
name: anchor-binary
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/anchor
- uses: actions/download-artifact@v3
with:
name: optional.so
path: tests/optional/target/deploy/
- uses: actions/download-artifact@v2
with:
name: events.so
path: tests/events/target/deploy/
- uses: actions/download-artifact@v2
- uses: actions/download-artifact@v3
with:
name: basic_4.so
path: examples/tutorial/basic-4/target/deploy/
- uses: actions/download-artifact@v2
- uses: actions/download-artifact@v3
with:
name: basic_2.so
path: examples/tutorial/basic-2/target/deploy/
- uses: actions/download-artifact@v2
- uses: actions/download-artifact@v3
with:
name: composite.so
path: tests/composite/target/deploy/
- uses: actions/cache@v2
- uses: actions/cache@v3
name: Cache client/example target
id: cache-test-target
with:
@ -205,15 +216,15 @@ jobs:
test-bpf-upgradeable-state:
needs: setup-anchor-cli
name: Test tests/bpf-upgradeable-state
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/actions/setup/
- uses: ./.github/actions/setup-ts/
- uses: ./.github/actions/setup-solana/
- uses: actions/cache@v2
- uses: actions/cache@v3
name: Cache Cargo registry + index
id: cache-anchor
with:
@ -223,15 +234,15 @@ jobs:
~/.cargo/registry/cache/
~/.cargo/git/db/
./target/
key: cargo-${{ runner.os }}-anchor-${{ hashFiles('**/Cargo.lock') }}
key: cargo-${{ runner.os }}-${{ env.CARGO_PROFILE }}-anchor-${{ hashFiles('**/Cargo.lock') }}
- uses: actions/download-artifact@v2
- uses: actions/download-artifact@v3
with:
name: anchor-binary
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/anchor
- uses: actions/cache@v2
- uses: actions/cache@v3
name: Cache tests/bpf-upgradeable-state target
id: cache-test-target
with:
@ -248,69 +259,69 @@ jobs:
- run: cd tests/bpf-upgradeable-state && npx tsc --noEmit
- uses: ./.github/actions/git-diff/
# this test exists to make sure that anchor
# checks rent correctly for legacy accounts
# that don't have to be rent-exempt
test-misc-non-rent-exempt:
# the anchor cli is built with a different solana version
# but that's fine since it's just the cli
needs: setup-anchor-cli
name: Test tests/misc/nonRentExempt
runs-on: ubuntu-18.04
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: ./.github/actions/setup/
- uses: ./.github/actions/setup-ts/
- uses: actions/cache@v2
name: Cache Solana Tool Suite
id: cache-solana
with:
path: |
~/.cache/solana/
~/.local/share/solana/
key: solana-${{ runner.os }}-v0000-1.8.14
# using an outdated validator but that
# is ok as long as the test doesn't
# include newer incompatible features
- run: sh -c "$(curl -sSfL https://release.solana.com/v1.8.14/install)"
shell: bash
- run: echo "/home/runner/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH
shell: bash
- run: solana-keygen new --no-bip39-passphrase
shell: bash
- run: solana config set --url localhost
shell: bash
- uses: actions/download-artifact@v2
with:
name: anchor-binary
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/anchor
# # this test exists to make sure that anchor
# # checks rent correctly for legacy accounts
# # that don't have to be rent-exempt
# test-misc-non-rent-exempt:
# # the anchor cli is built with a different solana version
# # but that's fine since it's just the cli
# needs: setup-anchor-cli
# name: Test tests/misc/nonRentExempt
# runs-on: ubuntu-latest
# timeout-minutes: 30
# steps:
# - uses: actions/checkout@v3
# - uses: ./.github/actions/setup/
# - uses: ./.github/actions/setup-ts/
# - uses: actions/cache@v3
# name: Cache Solana Tool Suite
# id: cache-solana
# with:
# path: |
# ~/.cache/solana/
# ~/.local/share/solana/
# key: solana-${{ runner.os }}-v0000-1.8.14
# # using an outdated validator but that
# # is ok as long as the test doesn't
# # include newer incompatible features
# - run: sh -c "$(curl -sSfL https://release.solana.com/v1.8.14/install)"
# shell: bash
# - run: echo "/home/runner/.local/share/solana/install/active_release/bin" >> $GITHUB_PATH
# shell: bash
# - run: solana-keygen new --no-bip39-passphrase
# shell: bash
# - run: solana config set --url localhost
# shell: bash
# - uses: actions/download-artifact@v3
# with:
# name: anchor-binary
# path: ~/.cargo/bin/
# - run: chmod +x ~/.cargo/bin/anchor
- uses: actions/cache@v2
name: Cache tests/misc target
id: cache-test-target
with:
path: tests/misc/target
key: cargo-${{ runner.os }}-tests/misc-${{ env.ANCHOR_VERSION }}-1.8.14-${{ hashFiles('**/Cargo.lock') }}
# - uses: actions/cache@v3
# name: Cache tests/misc target
# id: cache-test-target
# with:
# path: tests/misc/target
# key: cargo-${{ runner.os }}-tests/misc-${{ env.ANCHOR_VERSION }}-1.8.14-${{ hashFiles('**/Cargo.lock') }}
- run: cd tests/misc && yarn --frozen-lockfile
- run: cd tests/misc
- run: cd tests/misc && chmod +x ci.sh && ./ci.sh
- run: cd tests/misc && anchor test --skip-lint
# - run: cd tests/misc && yarn --frozen-lockfile
# - run: cd tests/misc
# - run: cd tests/misc && chmod +x ci.sh && ./ci.sh
# - run: cd tests/misc && anchor test --skip-lint
test-anchor-init:
needs: setup-anchor-cli
name: Test Anchor Init
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/actions/setup/
- uses: ./.github/actions/setup-ts/
- uses: ./.github/actions/setup-solana/
- uses: actions/cache@v2
- uses: actions/cache@v3
name: Cache Cargo registry + index
id: cache-anchor
with:
@ -320,21 +331,21 @@ jobs:
~/.cargo/registry/cache/
~/.cargo/git/db/
./target/
key: cargo-${{ runner.os }}-anchor-${{ hashFiles('**/Cargo.lock') }}
key: cargo-${{ runner.os }}-${{ env.CARGO_PROFILE }}-anchor-${{ hashFiles('**/Cargo.lock') }}
- uses: actions/download-artifact@v2
- uses: actions/download-artifact@v3
with:
name: anchor-binary
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/anchor
- run: cd "$(mktemp -d)" && anchor init hello-anchor && cd hello-anchor && yarn link @project-serum/anchor && yarn && anchor test && yarn lint:fix
- run: cd "$(mktemp -d)" && anchor init hello-anchor && cd hello-anchor && yarn link @coral-xyz/anchor && yarn && anchor test && yarn lint:fix
- uses: ./.github/actions/git-diff/
test-programs:
needs: setup-anchor-cli
name: Test ${{ matrix.node.path }}
runs-on: ubuntu-18.04
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
fail-fast: false
@ -350,11 +361,9 @@ jobs:
path: spl/token-proxy
- cmd: cd tests/multisig && anchor test --skip-lint
path: tests/multisig
- cmd: cd tests/interface && anchor test --skip-lint
path: tests/interface
- cmd: cd tests/lockup && anchor test --skip-lint
path: tests/lockup
- cmd: cd tests/swap/deps/serum-dex/dex && cargo build-bpf -- --locked && cd ../../../ && anchor test --skip-lint
# - cmd: cd tests/lockup && anchor test --skip-lint
# path: tests/lockup
- cmd: cd tests/swap/deps/openbook-dex/dex && cargo build-bpf -- --locked && cd ../../../ && anchor test --skip-lint
path: tests/swap
- cmd: cd tests/escrow && anchor test --skip-lint && npx tsc --noEmit
path: tests/escrow
@ -380,8 +389,8 @@ jobs:
path: tests/chat
- cmd: cd tests/ido-pool && anchor test --skip-lint
path: tests/ido-pool
- cmd: cd tests/cfo && anchor run test-with-build && cd deps/stake && git checkout Cargo.lock && cd ../swap && git checkout Cargo.lock
path: tests/cfo
# - cmd: cd tests/cfo && anchor run test-with-build && cd deps/stake && git checkout Cargo.lock && cd ../swap && git checkout Cargo.lock
# path: tests/cfo
- cmd: cd tests/auction-house && yarn --frozen-lockfile && anchor test --skip-lint && git checkout Cargo.lock
path: tests/auction-house
- cmd: cd tests/floats && yarn --frozen-lockfile && anchor test --skip-lint && npx tsc --noEmit
@ -396,17 +405,25 @@ jobs:
path: tests/cpi-returns
- cmd: cd tests/multiple-suites && anchor test --skip-lint && npx tsc --noEmit
path: tests/multiple-suites
- cmd: cd tests/multiple-suites-run-single && anchor test --skip-lint --run tests/should-run && npx tsc --noEmit
path: tests/multiple-suites-run-single
- cmd: cd tests/optional && anchor test --skip-lint && npx tsc --noEmit
path: tests/optional
- cmd: cd tests/pda-derivation && anchor test --skip-lint && npx tsc --noEmit
path: tests/pda-derivation
- cmd: cd tests/relations-derivation && anchor test --skip-lint && npx tsc --noEmit
path: tests/relations-derivation
- cmd: cd tests/anchor-cli-idl && ./test.sh
path: tests/anchor-cli-idl
- cmd: cd tests/anchor-cli-account && anchor test --skip-lint
path: tests/anchor-cli-account
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- uses: ./.github/actions/setup/
- uses: ./.github/actions/setup-ts/
- uses: ./.github/actions/setup-solana/
- uses: actions/cache@v2
- uses: actions/cache@v3
name: Cache Cargo registry + index
id: cache-anchor
with:
@ -416,15 +433,15 @@ jobs:
~/.cargo/registry/cache/
~/.cargo/git/db/
./target/
key: cargo-${{ runner.os }}-anchor-${{ hashFiles('**/Cargo.lock') }}
key: cargo-${{ runner.os }}-${{ env.CARGO_PROFILE }}-anchor-${{ hashFiles('**/Cargo.lock') }}
- uses: actions/download-artifact@v2
- uses: actions/download-artifact@v3
with:
name: anchor-binary
path: ~/.cargo/bin/
- run: chmod +x ~/.cargo/bin/anchor
- uses: actions/cache@v2
- uses: actions/cache@v3
name: Cache ${{ matrix.node.path }} target
id: cache-test-target
with:

2
.gitignore vendored
View File

@ -21,3 +21,5 @@ cli/npm-package/anchor
cli/npm-package/*.tgz
docker-target
.rollup.cache/
test-keypair.json
tsconfig.tsbuildinfo

15
.gitmodules vendored
View File

@ -1,9 +1,9 @@
[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/swap/deps/openbook-dex"]
path = tests/swap/deps/openbook-dex
url = https://github.com/openbook-dex/program
[submodule "examples/cfo/deps/openbook-dex"]
path = tests/cfo/deps/openbook-dex
url = https://github.com/openbook-dex/program
[submodule "examples/cfo/deps/swap"]
path = tests/cfo/deps/swap
url = https://github.com/project-serum/swap.git
@ -12,9 +12,6 @@
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

File diff suppressed because it is too large Load Diff

2037
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,6 @@ members = [
"spl",
]
exclude = [
"tests/swap/deps/serum-dex",
"tests/cfo/deps/serum-dex",
"tests/permissioned-markets/deps/serum-dex",
"tests/swap/deps/openbook-dex",
"tests/cfo/deps/openbook-dex",
]

View File

@ -23,12 +23,8 @@ publish:
sleep 25
cd lang/attribute/error/ && cargo publish && cd ../../../
sleep 25
cd lang/attribute/interface/ && cargo publish && cd ../../../
sleep 25
cd lang/attribute/program/ && cargo publish && cd ../../..
sleep 25
cd lang/attribute/state/ && cargo publish && cd ../../../
sleep 25
cd lang/attribute/event/ && cargo publish && cd ../../../
sleep 25
cd lang/ && cargo publish && cd ../

View File

@ -31,18 +31,18 @@ To jump straight to examples, go [here](https://github.com/coral-xyz/anchor/tree
## Packages
| Package | Description | Version | Docs |
| :-------------------------- | :------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------- |
| `anchor-lang` | Rust primitives for writing programs on Solana | [![Crates.io](https://img.shields.io/crates/v/anchor-lang?color=blue)](https://crates.io/crates/anchor-lang) | [![Docs.rs](https://docs.rs/anchor-lang/badge.svg)](https://docs.rs/anchor-lang) |
| `anchor-spl` | CPI clients for SPL programs on Solana | ![crates](https://img.shields.io/crates/v/anchor-spl?color=blue) | [![Docs.rs](https://docs.rs/anchor-spl/badge.svg)](https://docs.rs/anchor-spl) |
| `anchor-client` | Rust client for Anchor programs | ![crates](https://img.shields.io/crates/v/anchor-client?color=blue) | [![Docs.rs](https://docs.rs/anchor-client/badge.svg)](https://docs.rs/anchor-client) |
| `@project-serum/anchor` | TypeScript client for Anchor programs | [![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://coral-xyz.github.io/anchor/ts/index.html) |
| `@project-serum/anchor-cli` | CLI to support building and managing an Anchor workspace | [![npm](https://img.shields.io/npm/v/@project-serum/anchor-cli.svg?color=blue)](https://www.npmjs.com/package/@project-serum/anchor-cli) | [![Docs](https://img.shields.io/badge/docs-typedoc-blue)](https://coral-xyz.github.io/anchor/cli/commands.html) |
| Package | Description | Version | Docs |
| :---------------------- | :------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------- |
| `anchor-lang` | Rust primitives for writing programs on Solana | [![Crates.io](https://img.shields.io/crates/v/anchor-lang?color=blue)](https://crates.io/crates/anchor-lang) | [![Docs.rs](https://docs.rs/anchor-lang/badge.svg)](https://docs.rs/anchor-lang) |
| `anchor-spl` | CPI clients for SPL programs on Solana | ![crates](https://img.shields.io/crates/v/anchor-spl?color=blue) | [![Docs.rs](https://docs.rs/anchor-spl/badge.svg)](https://docs.rs/anchor-spl) |
| `anchor-client` | Rust client for Anchor programs | ![crates](https://img.shields.io/crates/v/anchor-client?color=blue) | [![Docs.rs](https://docs.rs/anchor-client/badge.svg)](https://docs.rs/anchor-client) |
| `@coral-xyz/anchor` | TypeScript client for Anchor programs | [![npm](https://img.shields.io/npm/v/@coral-xyz/anchor.svg?color=blue)](https://www.npmjs.com/package/@coral-xyz/anchor) | [![Docs](https://img.shields.io/badge/docs-typedoc-blue)](https://coral-xyz.github.io/anchor/ts/index.html) |
| `@coral-xyz/anchor-cli` | CLI to support building and managing an Anchor workspace | [![npm](https://img.shields.io/npm/v/@coral-xyz/anchor-cli.svg?color=blue)](https://www.npmjs.com/package/@coral-xyz/anchor-cli) | [![Docs](https://img.shields.io/badge/docs-typedoc-blue)](https://coral-xyz.github.io/anchor/cli/commands.html) |
## Note
* **Anchor is in active development, so all APIs are subject to change.**
* **This code is unaudited. Use at your own risk.**
- **Anchor is in active development, so all APIs are subject to change.**
- **This code is unaudited. Use at your own risk.**
## Examples

View File

@ -1 +1 @@
0.25.0
0.26.0

573
avm/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
[package]
name = "avm"
version = "0.25.0"
rust-version = "1.56"
version = "0.26.0"
rust-version = "1.59"
edition = "2021"
[[bin]]
@ -13,10 +13,10 @@ name = "anchor"
path = "src/anchor/main.rs"
[dependencies]
clap = { version = "3.0.13", features = [ "derive" ]}
clap = { version = "4.0.26", features = [ "derive" ]}
cfg-if = "1.0.0"
anyhow = "1.0.32"
dirs = "1.0.5"
dirs = "4.0.0"
semver = "1.0.4"
serde = { version = "1.0.136", features = [ "derive" ]}
serde_json = "1.0.78"

View File

@ -88,7 +88,7 @@ pub fn install_version(version: &Version, force: bool) -> Result<()> {
}
let exit = std::process::Command::new("cargo")
.args(&[
.args([
"install",
"--git",
"https://github.com/coral-xyz/anchor",

View File

@ -15,12 +15,12 @@ pub struct Cli {
pub enum Commands {
#[clap(about = "Use a specific version of Anchor")]
Use {
#[clap(parse(try_from_str = parse_version))]
#[clap(value_parser = parse_version)]
version: Version,
},
#[clap(about = "Install a version of Anchor")]
Install {
#[clap(parse(try_from_str = parse_version))]
#[clap(value_parser = parse_version)]
version: Version,
#[clap(long)]
/// Flag to force installation even if the version
@ -29,7 +29,7 @@ pub enum Commands {
},
#[clap(about = "Uninstall a version of Anchor")]
Uninstall {
#[clap(parse(try_from_str = parse_version))]
#[clap(value_parser = parse_version)]
version: Version,
},
#[clap(about = "List available versions of Anchor")]

View File

@ -1,8 +1,8 @@
[package]
name = "anchor-cli"
version = "0.25.0"
version = "0.26.0"
authors = ["armaniferrante <armaniferrante@gmail.com>"]
rust-version = "1.56"
rust-version = "1.59"
edition = "2021"
repository = "https://github.com/coral-xyz/anchor"
description = "Anchor CLI"
@ -17,31 +17,30 @@ dev = []
default = []
[dependencies]
clap = { version = "3.0.13", features = ["derive"] }
clap = { version = "4.0.26", features = ["derive"] }
anyhow = "1.0.32"
syn = { version = "1.0.60", features = ["full", "extra-traits"] }
anchor-lang = { path = "../lang", version = "0.25.0" }
anchor-client = { path = "../client", version = "0.25.0" }
anchor-syn = { path = "../lang/syn", features = ["idl", "init-if-needed"], version = "0.25.0" }
anchor-lang = { path = "../lang", version = "0.26.0" }
anchor-client = { path = "../client", version = "0.26.0" }
anchor-syn = { path = "../lang/syn", features = ["idl", "init-if-needed"], version = "0.26.0" }
serde_json = "1.0"
shellexpand = "2.1.0"
toml = "0.5.8"
semver = "1.0.4"
serde = { version = "1.0.122", features = ["derive"] }
solana-sdk = "~1.10.29"
solana-program = "~1.10.29"
solana-client = "~1.10.29"
solana-cli-config = "~1.10.29"
solana-faucet = "~1.10.29"
dirs = "3.0"
heck = "0.3.1"
solana-sdk = "1.13.5"
solana-program = "1.13.5"
solana-client = "1.13.5"
solana-cli-config = "1.13.5"
solana-faucet = "1.13.5"
dirs = "4.0"
heck = "0.4.0"
flate2 = "1.0.19"
rand = "0.7.3"
tar = "0.4.35"
reqwest = { version = "0.11.4", features = ["multipart", "blocking"] }
tokio = "1.0"
pathdiff = "0.2.0"
cargo_toml = "0.9.2"
cargo_toml = "0.13.0"
walkdir = "2.3.2"
chrono = "0.4.19"
portpicker = "0.1.1"

View File

@ -1,6 +1,6 @@
{
"name": "@project-serum/anchor-cli",
"version": "0.25.0",
"name": "@coral-xyz/anchor-cli",
"version": "0.26.0",
"description": "Anchor CLI tool",
"homepage": "https://github.com/coral-xyz/anchor#readme",
"bugs": {

View File

@ -2,21 +2,24 @@ use crate::is_hidden;
use anchor_client::Cluster;
use anchor_syn::idl::Idl;
use anyhow::{anyhow, Context, Error, Result};
use clap::{ArgEnum, Parser};
use heck::SnakeCase;
use serde::{Deserialize, Serialize};
use clap::{Parser, ValueEnum};
use heck::ToSnakeCase;
use reqwest::Url;
use serde::de::{self, MapAccess, Visitor};
use serde::{Deserialize, Deserializer, Serialize};
use solana_cli_config::{Config as SolanaConfig, CONFIG_FILE};
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{Keypair, Signer};
use std::collections::{BTreeMap, HashMap};
use std::convert::TryFrom;
use std::fs::{self, File};
use std::io;
use std::io::prelude::*;
use std::marker::PhantomData;
use std::ops::Deref;
use std::path::Path;
use std::path::PathBuf;
use std::str::FromStr;
use std::{fmt, io};
use walkdir::WalkDir;
pub trait Merge: Sized {
@ -94,7 +97,7 @@ impl Manifest {
pub fn version(&self) -> String {
match &self.package {
Some(package) => package.version.to_string(),
Some(package) => package.version().to_string(),
_ => "0.0.0".to_string(),
}
}
@ -171,20 +174,18 @@ impl WithPath<Config> {
.collect())
}
// TODO: this should read idl dir instead of parsing source.
pub fn read_all_programs(&self) -> Result<Vec<Program>> {
let mut r = vec![];
for path in self.get_program_list()? {
let cargo = Manifest::from_path(&path.join("Cargo.toml"))?;
let lib_name = cargo.lib_name()?;
let version = cargo.version();
let idl = anchor_syn::idl::file::parse(
path.join("src/lib.rs"),
version,
self.features.seeds,
false,
false,
)?;
let idl_filepath = format!("target/idl/{}.json", lib_name);
let idl = fs::read(idl_filepath)
.ok()
.map(|bytes| serde_json::from_reader(&*bytes))
.transpose()?;
r.push(Program {
lib_name,
path,
@ -323,7 +324,7 @@ pub struct WorkspaceConfig {
pub types: String,
}
#[derive(ArgEnum, Parser, Clone, PartialEq, Debug)]
#[derive(ValueEnum, Parser, Clone, PartialEq, Eq, Debug)]
pub enum BootstrapMode {
None,
Debian,
@ -338,8 +339,12 @@ pub struct BuildConfig {
}
impl Config {
pub fn add_test_config(&mut self, root: impl AsRef<Path>) -> Result<()> {
self.test_config = TestConfig::discover(root)?;
pub fn add_test_config(
&mut self,
root: impl AsRef<Path>,
test_paths: Vec<PathBuf>,
) -> Result<()> {
self.test_config = TestConfig::discover(root, test_paths)?;
Ok(())
}
@ -420,10 +425,58 @@ struct _Config {
#[derive(Debug, Serialize, Deserialize)]
struct Provider {
cluster: String,
#[serde(deserialize_with = "des_cluster")]
cluster: Cluster,
wallet: String,
}
fn des_cluster<'de, D>(deserializer: D) -> Result<Cluster, D::Error>
where
D: Deserializer<'de>,
{
struct StringOrCustomCluster(PhantomData<fn() -> Cluster>);
impl<'de> Visitor<'de> for StringOrCustomCluster {
type Value = Cluster;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("string or map")
}
fn visit_str<E>(self, value: &str) -> Result<Cluster, E>
where
E: de::Error,
{
value.parse().map_err(de::Error::custom)
}
fn visit_map<M>(self, mut map: M) -> Result<Cluster, M::Error>
where
M: MapAccess<'de>,
{
// Gets keys
if let (Some((http_key, http_value)), Some((ws_key, ws_value))) = (
map.next_entry::<String, String>()?,
map.next_entry::<String, String>()?,
) {
// Checks keys
if http_key != "http" || ws_key != "ws" {
return Err(de::Error::custom("Invalid key"));
}
// Checks urls
Url::parse(&http_value).map_err(de::Error::custom)?;
Url::parse(&ws_value).map_err(de::Error::custom)?;
Ok(Cluster::Custom(http_value, ws_value))
} else {
Err(de::Error::custom("Invalid entry"))
}
}
}
deserializer.deserialize_any(StringOrCustomCluster(PhantomData))
}
impl ToString for Config {
fn to_string(&self) -> String {
let programs = {
@ -440,7 +493,7 @@ impl ToString for Config {
features: Some(self.features.clone()),
registry: Some(self.registry.clone()),
provider: Provider {
cluster: format!("{}", self.provider.cluster),
cluster: self.provider.cluster.clone(),
wallet: self.provider.wallet.to_string(),
},
test: self.test_validator.clone().map(Into::into),
@ -469,7 +522,7 @@ impl FromStr for Config {
features: cfg.features.unwrap_or_default(),
registry: cfg.registry.unwrap_or_default(),
provider: ProviderConfig {
cluster: cfg.provider.cluster.parse()?,
cluster: cfg.provider.cluster,
wallet: shellexpand::tilde(&cfg.provider.wallet).parse()?,
},
scripts: cfg.scripts.unwrap_or_default(),
@ -537,6 +590,7 @@ fn deser_programs(
path: None,
idl: None,
},
serde_json::Value::Object(_) => {
serde_json::from_value(program_id.clone())
.map_err(|_| anyhow!("Unable to read toml"))?
@ -610,14 +664,17 @@ impl Deref for TestConfig {
}
impl TestConfig {
pub fn discover(root: impl AsRef<Path>) -> Result<Option<Self>> {
pub fn discover(root: impl AsRef<Path>, test_paths: Vec<PathBuf>) -> Result<Option<Self>> {
let walker = WalkDir::new(root).into_iter();
let mut test_suite_configs = HashMap::new();
for entry in walker.filter_entry(|e| !is_hidden(e)) {
let entry = entry?;
if entry.file_name() == "Test.toml" {
let test_toml = TestToml::from_path(entry.path())?;
test_suite_configs.insert(entry.path().into(), test_toml);
let entry_path = entry.path();
let test_toml = TestToml::from_path(entry_path)?;
if test_paths.is_empty() || test_paths.iter().any(|p| entry_path.starts_with(p)) {
test_suite_configs.insert(entry.path().into(), test_toml);
}
}
}
@ -833,6 +890,9 @@ pub struct _Validator {
// Give the faucet address this much SOL in genesis. [default: 1000000]
#[serde(skip_serializing_if = "Option::is_none")]
pub faucet_sol: Option<String>,
// Geyser plugin config location
#[serde(skip_serializing_if = "Option::is_none")]
pub geyser_plugin_config: Option<String>,
// Gossip DNS name or IP address for the validator to advertise in gossip. [default: 127.0.0.1]
#[serde(skip_serializing_if = "Option::is_none")]
pub gossip_host: Option<String>,
@ -854,6 +914,9 @@ pub struct _Validator {
// Override the number of slots in an epoch.
#[serde(skip_serializing_if = "Option::is_none")]
pub slots_per_epoch: Option<String>,
// The number of ticks in a slot
#[serde(skip_serializing_if = "Option::is_none")]
pub ticks_per_slot: Option<u16>,
// Warp the ledger to WARP_SLOT after starting the validator.
#[serde(skip_serializing_if = "Option::is_none")]
pub warp_slot: Option<String>,
@ -873,6 +936,8 @@ pub struct Validator {
#[serde(skip_serializing_if = "Option::is_none")]
pub faucet_sol: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub geyser_plugin_config: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub gossip_host: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub gossip_port: Option<u16>,
@ -885,6 +950,8 @@ pub struct Validator {
#[serde(skip_serializing_if = "Option::is_none")]
pub slots_per_epoch: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ticks_per_slot: Option<u16>,
#[serde(skip_serializing_if = "Option::is_none")]
pub warp_slot: Option<String>,
}
@ -899,6 +966,7 @@ impl From<_Validator> for Validator {
dynamic_port_range: _validator.dynamic_port_range,
faucet_port: _validator.faucet_port,
faucet_sol: _validator.faucet_sol,
geyser_plugin_config: _validator.geyser_plugin_config,
gossip_host: _validator.gossip_host,
gossip_port: _validator.gossip_port,
url: _validator.url,
@ -910,6 +978,7 @@ impl From<_Validator> for Validator {
.rpc_port
.unwrap_or(solana_sdk::rpc_port::DEFAULT_RPC_PORT),
slots_per_epoch: _validator.slots_per_epoch,
ticks_per_slot: _validator.ticks_per_slot,
warp_slot: _validator.warp_slot,
}
}
@ -924,6 +993,7 @@ impl From<Validator> for _Validator {
dynamic_port_range: validator.dynamic_port_range,
faucet_port: validator.faucet_port,
faucet_sol: validator.faucet_sol,
geyser_plugin_config: validator.geyser_plugin_config,
gossip_host: validator.gossip_host,
gossip_port: validator.gossip_port,
url: validator.url,
@ -931,6 +1001,7 @@ impl From<Validator> for _Validator {
limit_ledger_size: validator.limit_ledger_size,
rpc_port: Some(validator.rpc_port),
slots_per_epoch: validator.slots_per_epoch,
ticks_per_slot: validator.ticks_per_slot,
warp_slot: validator.warp_slot,
}
}
@ -987,6 +1058,9 @@ impl Merge for _Validator {
.or_else(|| self.dynamic_port_range.take()),
faucet_port: other.faucet_port.or_else(|| self.faucet_port.take()),
faucet_sol: other.faucet_sol.or_else(|| self.faucet_sol.take()),
geyser_plugin_config: other
.geyser_plugin_config
.or_else(|| self.geyser_plugin_config.take()),
gossip_host: other.gossip_host.or_else(|| self.gossip_host.take()),
gossip_port: other.gossip_port.or_else(|| self.gossip_port.take()),
url: other.url.or_else(|| self.url.take()),
@ -998,6 +1072,7 @@ impl Merge for _Validator {
slots_per_epoch: other
.slots_per_epoch
.or_else(|| self.slots_per_epoch.take()),
ticks_per_slot: other.ticks_per_slot.or_else(|| self.ticks_per_slot.take()),
warp_slot: other.warp_slot.or_else(|| self.warp_slot.take()),
};
}
@ -1037,7 +1112,7 @@ impl Program {
path,
));
}
let program_kp = Keypair::generate(&mut rand::rngs::OsRng);
let program_kp = Keypair::new();
let mut file = File::create(&path)
.with_context(|| format!("Error creating file with path: {}", path.display()))?;
file.write_all(format!("{:?}", &program_kp.to_bytes()).as_bytes())?;
@ -1129,6 +1204,18 @@ mod tests {
wallet = \"id.json\"
";
const CUSTOM_CONFIG: &str = "
[provider]
cluster = { http = \"http://my-url.com\", ws = \"ws://my-url.com\" }
wallet = \"id.json\"
";
#[test]
fn parse_custom_cluster() {
let config = Config::from_str(CUSTOM_CONFIG).unwrap();
assert!(!config.features.skip_lint);
}
#[test]
fn parse_skip_lint_no_section() {
let config = Config::from_str(BASE_CONFIG).unwrap();

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@ use crate::config::ProgramWorkspace;
use crate::VERSION;
use anchor_syn::idl::Idl;
use anyhow::Result;
use heck::{CamelCase, MixedCase, SnakeCase};
use heck::{ToLowerCamelCase, ToSnakeCase, ToUpperCamelCase};
use solana_sdk::pubkey::Pubkey;
use std::fmt::Write;
@ -41,7 +41,7 @@ token = "{}"
pub fn idl_ts(idl: &Idl) -> Result<String> {
let mut idl = idl.clone();
for acc in idl.accounts.iter_mut() {
acc.name = acc.name.to_mixed_case();
acc.name = acc.name.to_lower_camel_case();
}
let idl_json = serde_json::to_string_pretty(&idl)?;
Ok(format!(
@ -49,9 +49,9 @@ pub fn idl_ts(idl: &Idl) -> Result<String> {
export const IDL: {} = {};
"#,
idl.name.to_camel_case(),
idl.name.to_upper_camel_case(),
idl_json,
idl.name.to_camel_case(),
idl.name.to_upper_camel_case(),
idl_json
))
}
@ -87,7 +87,7 @@ anchor-lang = "{2}"
pub fn deploy_js_script_host(cluster_url: &str, script_path: &str) -> String {
format!(
r#"
const anchor = require('@project-serum/anchor');
const anchor = require('@coral-xyz/anchor');
// Deploy script defined by the user.
const userScript = require("{0}");
@ -114,7 +114,7 @@ main();
pub fn deploy_ts_script_host(cluster_url: &str, script_path: &str) -> String {
format!(
r#"import * as anchor from '@project-serum/anchor';
r#"import * as anchor from '@coral-xyz/anchor';
// Deploy script defined by the user.
const userScript = require("{0}");
@ -144,7 +144,7 @@ pub fn deploy_script() -> &'static str {
// 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");
const anchor = require("@coral-xyz/anchor");
module.exports = async function (provider) {
// Configure client to use the provider.
@ -160,7 +160,7 @@ pub fn ts_deploy_script() -> &'static str {
// 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");
const anchor = require("@coral-xyz/anchor");
module.exports = async function (provider) {
// Configure client to use the provider.
@ -202,7 +202,7 @@ pub struct Initialize {{}}
pub fn mocha(name: &str) -> String {
format!(
r#"const anchor = require("@project-serum/anchor");
r#"const anchor = require("@coral-xyz/anchor");
describe("{}", () => {{
// Configure the client to use the local cluster.
@ -217,19 +217,59 @@ describe("{}", () => {{
}});
"#,
name,
name.to_camel_case(),
name.to_upper_camel_case(),
)
}
pub fn package_json() -> String {
pub fn jest(name: &str) -> String {
format!(
r#"{{
r#"const anchor = require("@coral-xyz/anchor");
describe("{}", () => {{
// 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.{};
const tx = await program.methods.initialize().rpc();
console.log("Your transaction signature", tx);
}});
}});
"#,
name,
name.to_upper_camel_case(),
)
}
pub fn package_json(jest: bool) -> String {
if jest {
format!(
r#"{{
"scripts": {{
"lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
"lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
}},
"dependencies": {{
"@coral-xyz/anchor": "^{0}"
}},
"devDependencies": {{
"jest": "^29.0.3",
"prettier": "^2.6.2"
}}
}}
"#,
VERSION
)
} else {
format!(
r#"{{
"scripts": {{
"lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
"lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
}},
"dependencies": {{
"@project-serum/anchor": "^{0}"
"@coral-xyz/anchor": "^{0}"
}},
"devDependencies": {{
"chai": "^4.3.4",
@ -238,19 +278,43 @@ pub fn package_json() -> String {
}}
}}
"#,
VERSION
)
VERSION
)
}
}
pub fn ts_package_json() -> String {
format!(
r#"{{
pub fn ts_package_json(jest: bool) -> String {
if jest {
format!(
r#"{{
"scripts": {{
"lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
"lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
}},
"dependencies": {{
"@coral-xyz/anchor": "^{0}"
}},
"devDependencies": {{
"@types/bn.js": "^5.1.0",
"@types/jest": "^29.0.3",
"jest": "^29.0.3",
"prettier": "^2.6.2",
"ts-jest": "^29.0.2",
"typescript": "^4.3.5"
}}
}}
"#,
VERSION
)
} else {
format!(
r#"{{
"scripts": {{
"lint:fix": "prettier */*.js \"*/**/*{{.js,.ts}}\" -w",
"lint": "prettier */*.js \"*/**/*{{.js,.ts}}\" --check"
}},
"dependencies": {{
"@project-serum/anchor": "^{0}"
"@coral-xyz/anchor": "^{0}"
}},
"devDependencies": {{
"chai": "^4.3.4",
@ -264,14 +328,15 @@ pub fn ts_package_json() -> String {
}}
}}
"#,
VERSION
)
VERSION
)
}
}
pub fn ts_mocha(name: &str) -> String {
format!(
r#"import * as anchor from "@project-serum/anchor";
import {{ Program }} from "@project-serum/anchor";
r#"import * as anchor from "@coral-xyz/anchor";
import {{ Program }} from "@coral-xyz/anchor";
import {{ {} }} from "../target/types/{}";
describe("{}", () => {{
@ -287,26 +352,67 @@ describe("{}", () => {{
}});
}});
"#,
name.to_camel_case(),
name.to_upper_camel_case(),
name.to_snake_case(),
name,
name.to_camel_case(),
name.to_camel_case(),
name.to_upper_camel_case(),
name.to_upper_camel_case(),
)
}
pub fn ts_config() -> &'static str {
r#"{
"compilerOptions": {
"types": ["mocha", "chai"],
"typeRoots": ["./node_modules/@types"],
"lib": ["es2015"],
"module": "commonjs",
"target": "es6",
"esModuleInterop": true
}
pub fn ts_jest(name: &str) -> String {
format!(
r#"import * as anchor from "@coral-xyz/anchor";
import {{ Program }} from "@coral-xyz/anchor";
import {{ {} }} from "../target/types/{}";
describe("{}", () => {{
// Configure the client to use the local cluster.
anchor.setProvider(anchor.AnchorProvider.env());
const program = anchor.workspace.{} as Program<{}>;
it("Is initialized!", async () => {{
// Add your test here.
const tx = await program.methods.initialize().rpc();
console.log("Your transaction signature", tx);
}});
}});
"#,
name.to_upper_camel_case(),
name.to_snake_case(),
name,
name.to_upper_camel_case(),
name.to_upper_camel_case(),
)
}
"#
pub fn ts_config(jest: bool) -> &'static str {
if jest {
r#"{
"compilerOptions": {
"types": ["jest"],
"typeRoots": ["./node_modules/@types"],
"lib": ["es2015"],
"module": "commonjs",
"target": "es6",
"esModuleInterop": true
}
}
"#
} else {
r#"{
"compilerOptions": {
"types": ["mocha", "chai"],
"typeRoots": ["./node_modules/@types"],
"lib": ["es2015"],
"module": "commonjs",
"target": "es6",
"esModuleInterop": true
}
}
"#
}
}
pub fn git_ignore() -> &'static str {
@ -339,7 +445,7 @@ pub fn node_shell(
) -> Result<String> {
let mut eval_string = format!(
r#"
const anchor = require('@project-serum/anchor');
const anchor = require('@coral-xyz/anchor');
const web3 = anchor.web3;
const PublicKey = anchor.web3.PublicKey;
const Keypair = anchor.web3.Keypair;
@ -374,7 +480,7 @@ anchor.setProvider(provider);
r#"
anchor.workspace.{} = new anchor.Program({}, new PublicKey("{}"), provider);
"#,
program.name.to_camel_case(),
program.name.to_upper_camel_case(),
serde_json::to_string(&program.idl)?,
program.program_id
)?;

View File

@ -1,8 +1,8 @@
[package]
name = "anchor-client"
version = "0.25.0"
version = "0.26.0"
authors = ["Serum Foundation <foundation@projectserum.com>"]
rust-version = "1.56"
rust-version = "1.59"
edition = "2021"
license = "Apache-2.0"
description = "Rust client for Anchor programs"
@ -11,12 +11,12 @@ description = "Rust client for Anchor programs"
debug = []
[dependencies]
anchor-lang = { path = "../lang", version = "0.25.0" }
anchor-lang = { path = "../lang", version = "0.26.0" }
anyhow = "1.0.32"
regex = "1.4.5"
serde = { version = "1.0.122", features = ["derive"] }
solana-client = "~1.10.29"
solana-sdk = "~1.10.29"
solana-account-decoder = "~1.10.29"
solana-client = "1.14.7"
solana-sdk = "1.13.5"
solana-account-decoder = "1.13.5"
thiserror = "1.0.20"
url = "2.2.2"

View File

@ -2,7 +2,7 @@
name = "example"
version = "0.1.0"
authors = ["Armani Ferrante <armaniferrante@gmail.com>"]
rust-version = "1.56"
rust-version = "1.59"
edition = "2021"
[workspace]
@ -12,9 +12,9 @@ anchor-client = { path = "../", features = ["debug"] }
basic-2 = { path = "../../examples/tutorial/basic-2/programs/basic-2", features = ["no-entrypoint"] }
basic-4 = { path = "../../examples/tutorial/basic-4/programs/basic-4", features = ["no-entrypoint"] }
composite = { path = "../../tests/composite/programs/composite", features = ["no-entrypoint"] }
optional = { path = "../../tests/optional/programs/optional", features = ["no-entrypoint"] }
events = { path = "../../tests/events/programs/events", features = ["no-entrypoint"] }
shellexpand = "2.1.0"
anyhow = "1.0.32"
rand = "0.7.3"
clap = { version = "3.0.0-rc.0", features = ["derive"] }
solana-sdk = "~1.10.29"
clap = { version = "4.0.26", features = ["derive"] }
solana-sdk = "1.13.5"

View File

@ -26,6 +26,7 @@ main() {
local basic_2_pid="Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"
local basic_4_pid="CwrqeMj2U8tFr1Rhkgwc84tpAsqbt9pTt2a4taoTADPr"
local events_pid="2dhGsWUzy5YKUsjZdLHLmkNpUDAXkNa9MYWsPc4Ziqzy"
local optional_pid="FNqz6pqLAwvMSds2FYjR4nKV3moVpPNtvkfGFrqLKrgG"
#
# Bootup validator.
@ -35,13 +36,14 @@ main() {
--bpf-program $basic_2_pid ../../examples/tutorial/basic-2/target/deploy/basic_2.so \
--bpf-program $basic_4_pid ../../examples/tutorial/basic-4/target/deploy/basic_4.so \
--bpf-program $events_pid ../../tests/events/target/deploy/events.so \
--bpf-program $optional_pid ../../tests/optional/target/deploy/optional.so \
> test-validator.log &
sleep 5
#
# Run Test.
#
cargo run -- --composite-pid $composite_pid --basic-2-pid $basic_2_pid --basic-4-pid $basic_4_pid --events-pid $events_pid
cargo run -- --composite-pid $composite_pid --basic-2-pid $basic_2_pid --basic-4-pid $basic_4_pid --events-pid $events_pid --optional-pid $optional_pid
}
cleanup() {

View File

@ -12,16 +12,18 @@ use basic_2::instruction as basic_2_instruction;
use basic_2::Counter;
use events::instruction as events_instruction;
use events::MyEvent;
use optional::accounts::Initialize as OptionalInitialize;
use optional::instruction as optional_instruction;
// The `accounts` and `instructions` modules are generated by the framework.
use basic_4::accounts as basic_4_accounts;
use basic_4::basic_4::Counter as CounterState;
use basic_4::instruction as basic_4_instruction;
use basic_4::Counter as CounterAccount;
use clap::Parser;
// The `accounts` and `instructions` modules are generated by the framework.
use composite::accounts::{Bar, CompositeUpdate, Foo, Initialize};
use composite::instruction as composite_instruction;
use composite::{DummyA, DummyB};
use rand::rngs::OsRng;
use optional::account::{DataAccount, DataPda};
use std::rc::Rc;
use std::time::Duration;
@ -35,6 +37,8 @@ pub struct Opts {
basic_4_pid: Pubkey,
#[clap(long)]
events_pid: Pubkey,
#[clap(long)]
optional_pid: Pubkey,
}
// This example assumes a local validator is running with the programs
@ -59,6 +63,7 @@ fn main() -> Result<()> {
basic_2(&client, opts.basic_2_pid)?;
basic_4(&client, opts.basic_4_pid)?;
events(&client, opts.events_pid)?;
optional(&client, opts.optional_pid)?;
// Success.
Ok(())
@ -72,8 +77,8 @@ fn composite(client: &Client, pid: Pubkey) -> Result<()> {
let program = client.program(pid);
// `Initialize` parameters.
let dummy_a = Keypair::generate(&mut OsRng);
let dummy_b = Keypair::generate(&mut OsRng);
let dummy_a = Keypair::new();
let dummy_b = Keypair::new();
// Build and send a transaction.
program
@ -142,7 +147,7 @@ fn basic_2(client: &Client, pid: Pubkey) -> Result<()> {
let program = client.program(pid);
// `Create` parameters.
let counter = Keypair::generate(&mut OsRng);
let counter = Keypair::new();
let authority = program.payer();
// Build and send a transaction.
@ -202,24 +207,28 @@ fn events(client: &Client, pid: Pubkey) -> Result<()> {
pub fn basic_4(client: &Client, pid: Pubkey) -> Result<()> {
let program = client.program(pid);
let authority = program.payer();
let (counter, _) = Pubkey::find_program_address(&[b"counter"], &pid);
// Invoke the state's `new` constructor.
program
.state_request()
.accounts(basic_4_accounts::Auth { authority })
.new(basic_4_instruction::state::New)
.request()
.accounts(basic_4_accounts::Initialize {
counter,
authority,
system_program: system_program::ID,
})
.args(basic_4_instruction::Initialize {})
.send()?;
let counter_account: CounterState = program.state()?;
let counter_account: CounterAccount = program.account(counter)?;
assert_eq!(counter_account.authority, authority);
assert_eq!(counter_account.count, 0);
// Call a state method.
program
.state_request()
.accounts(basic_4_accounts::Auth { authority })
.args(basic_4_instruction::state::Increment)
.request()
.accounts(basic_4_accounts::Increment { counter, authority })
.args(basic_4_instruction::Increment {})
.send()?;
let counter_account: CounterState = program.state()?;
let counter_account: CounterAccount = program.account(counter)?;
assert_eq!(counter_account.authority, authority);
assert_eq!(counter_account.count, 1);
@ -227,3 +236,61 @@ pub fn basic_4(client: &Client, pid: Pubkey) -> Result<()> {
Ok(())
}
// Runs a client for tests/optional.
//
// Make sure to run a localnet with the program deploy to run this example.
fn optional(client: &Client, pid: Pubkey) -> Result<()> {
// Program client.
let program = client.program(pid);
// `Initialize` parameters.
let data_account_keypair = Keypair::new();
let data_account_key = data_account_keypair.pubkey();
let data_pda_seeds = &[DataPda::PREFIX.as_ref(), data_account_key.as_ref()];
let data_pda_key = Pubkey::find_program_address(data_pda_seeds, &pid).0;
let required_keypair = Keypair::new();
let value: u64 = 10;
// Build and send a transaction.
program
.request()
.instruction(system_instruction::create_account(
&program.payer(),
&required_keypair.pubkey(),
program
.rpc()
.get_minimum_balance_for_rent_exemption(DataAccount::LEN)?,
DataAccount::LEN as u64,
&program.id(),
))
.signer(&data_account_keypair)
.signer(&required_keypair)
.accounts(OptionalInitialize {
payer: Some(program.payer()),
required: required_keypair.pubkey(),
system_program: Some(system_program::id()),
optional_account: Some(data_account_keypair.pubkey()),
optional_pda: None,
})
.args(optional_instruction::Initialize { value, key: pid })
.send()
.unwrap();
// Assert the transaction worked.
let required: DataAccount = program.account(required_keypair.pubkey())?;
assert_eq!(required.data, 0);
let optional_pda = program.account::<DataPda>(data_pda_key);
assert!(optional_pda.is_err());
let optional_account: DataAccount = program.account(data_account_keypair.pubkey())?;
assert_eq!(optional_account.data, value * 2);
println!("Optional success!");
Ok(())
}

View File

@ -1,10 +1,10 @@
//! `anchor_client` provides an RPC client to send transactions and fetch
//! deserialized accounts from Solana programs written in `anchor_lang`.
use anchor_lang::solana_program::hash::Hash;
use anchor_lang::solana_program::instruction::{AccountMeta, Instruction};
use anchor_lang::solana_program::program_error::ProgramError;
use anchor_lang::solana_program::pubkey::Pubkey;
use anchor_lang::solana_program::system_program;
use anchor_lang::{AccountDeserialize, Discriminator, InstructionData, ToAccountMetas};
use regex::Regex;
use solana_account_decoder::UiAccountEncoding;
@ -15,10 +15,9 @@ use solana_client::rpc_config::{
RpcAccountInfoConfig, RpcProgramAccountsConfig, RpcSendTransactionConfig,
RpcTransactionLogsConfig, RpcTransactionLogsFilter,
};
use solana_client::rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType};
use solana_client::rpc_filter::{Memcmp, RpcFilterType};
use solana_client::rpc_response::{Response as RpcResponse, RpcLogsResponse};
use solana_sdk::account::Account;
use solana_sdk::bs58;
use solana_sdk::commitment_config::CommitmentConfig;
use solana_sdk::signature::{Signature, Signer};
use solana_sdk::transaction::Transaction;
@ -112,18 +111,6 @@ impl Program {
self.cfg.cluster.url(),
self.cfg.payer.clone(),
self.cfg.options,
RequestNamespace::Global,
)
}
/// Returns a request builder for program state.
pub fn state_request(&self) -> RequestBuilder {
RequestBuilder::from(
self.program_id,
self.cfg.cluster.url(),
self.cfg.payer.clone(),
self.cfg.options,
RequestNamespace::State { new: false },
)
}
@ -155,11 +142,8 @@ impl Program {
&self,
filters: Vec<RpcFilterType>,
) -> Result<ProgramAccountsIterator<T>, ClientError> {
let account_type_filter = RpcFilterType::Memcmp(Memcmp {
offset: 0,
bytes: MemcmpEncodedBytes::Base58(bs58::encode(T::discriminator()).into_string()),
encoding: None,
});
let account_type_filter =
RpcFilterType::Memcmp(Memcmp::new_base58_encoded(0, &T::discriminator()));
let config = RpcProgramAccountsConfig {
filters: Some([vec![account_type_filter], filters].concat()),
account_config: RpcAccountInfoConfig {
@ -179,10 +163,6 @@ impl Program {
})
}
pub fn state<T: AccountDeserialize>(&self) -> Result<T, ClientError> {
self.account(anchor_lang::__private::state::address(&self.program_id))
}
pub fn rpc(&self) -> RpcClient {
RpcClient::new_with_commitment(
self.cfg.cluster.url().to_string(),
@ -285,7 +265,7 @@ fn handle_program_log<T: anchor_lang::Event + anchor_lang::AnchorDeserialize>(
.strip_prefix(PROGRAM_LOG)
.or_else(|| l.strip_prefix(PROGRAM_DATA))
{
let borsh_bytes = match anchor_lang::__private::base64::decode(&log) {
let borsh_bytes = match anchor_lang::__private::base64::decode(log) {
Ok(borsh_bytes) => borsh_bytes,
_ => {
#[cfg(feature = "debug")]
@ -403,18 +383,6 @@ pub struct RequestBuilder<'a> {
// Serialized instruction data for the target RPC.
instruction_data: Option<Vec<u8>>,
signers: Vec<&'a dyn Signer>,
// True if the user is sending a state instruction.
namespace: RequestNamespace,
}
#[derive(PartialEq)]
pub enum RequestNamespace {
Global,
State {
// True if the request is to the state's new ctor.
new: bool,
},
Interface,
}
impl<'a> RequestBuilder<'a> {
@ -423,7 +391,6 @@ impl<'a> RequestBuilder<'a> {
cluster: &str,
payer: Rc<dyn Signer>,
options: Option<CommitmentConfig>,
namespace: RequestNamespace,
) -> Self {
Self {
program_id,
@ -434,7 +401,6 @@ impl<'a> RequestBuilder<'a> {
instructions: Vec::new(),
instruction_data: None,
signers: Vec::new(),
namespace,
}
}
@ -481,16 +447,6 @@ impl<'a> RequestBuilder<'a> {
self
}
/// Invokes the `#[state]`'s `new` constructor.
#[allow(clippy::wrong_self_convention)]
#[must_use]
pub fn new(mut self, args: impl InstructionData) -> Self {
assert!(self.namespace == RequestNamespace::State { new: false });
self.namespace = RequestNamespace::State { new: true };
self.instruction_data = Some(args.data());
self
}
#[must_use]
pub fn signer(mut self, signer: &'a dyn Signer) -> Self {
self.signers.push(signer);
@ -498,59 +454,54 @@ impl<'a> RequestBuilder<'a> {
}
pub fn instructions(&self) -> Result<Vec<Instruction>, ClientError> {
let mut accounts = match self.namespace {
RequestNamespace::State { new } => match new {
false => vec![AccountMeta::new(
anchor_lang::__private::state::address(&self.program_id),
false,
)],
true => vec![
AccountMeta::new_readonly(self.payer.pubkey(), true),
AccountMeta::new(
anchor_lang::__private::state::address(&self.program_id),
false,
),
AccountMeta::new_readonly(
Pubkey::find_program_address(&[], &self.program_id).0,
false,
),
AccountMeta::new_readonly(system_program::ID, false),
AccountMeta::new_readonly(self.program_id, false),
],
},
_ => Vec::new(),
};
accounts.extend_from_slice(&self.accounts);
let mut instructions = self.instructions.clone();
if let Some(ix_data) = &self.instruction_data {
instructions.push(Instruction {
program_id: self.program_id,
data: ix_data.clone(),
accounts,
accounts: self.accounts.clone(),
});
}
Ok(instructions)
}
pub fn send(self) -> Result<Signature, ClientError> {
fn signed_transaction_with_blockhash(
&self,
latest_hash: Hash,
) -> Result<Transaction, ClientError> {
let instructions = self.instructions()?;
let mut signers = self.signers;
let mut signers = self.signers.clone();
signers.push(&*self.payer);
let rpc_client = RpcClient::new_with_commitment(self.cluster, self.options);
let tx = Transaction::new_signed_with_payer(
&instructions,
Some(&self.payer.pubkey()),
&signers,
latest_hash,
);
let tx = {
let latest_hash = rpc_client.get_latest_blockhash()?;
Transaction::new_signed_with_payer(
&instructions,
Some(&self.payer.pubkey()),
&signers,
latest_hash,
)
};
Ok(tx)
}
pub fn signed_transaction(&self) -> Result<Transaction, ClientError> {
let latest_hash =
RpcClient::new_with_commitment(&self.cluster, self.options).get_latest_blockhash()?;
let tx = self.signed_transaction_with_blockhash(latest_hash)?;
Ok(tx)
}
pub fn transaction(&self) -> Result<Transaction, ClientError> {
let instructions = &self.instructions;
let tx = Transaction::new_with_payer(instructions, Some(&self.payer.pubkey()));
Ok(tx)
}
pub fn send(self) -> Result<Signature, ClientError> {
let rpc_client = RpcClient::new_with_commitment(&self.cluster, self.options);
let latest_hash = rpc_client.get_latest_blockhash()?;
let tx = self.signed_transaction_with_blockhash(latest_hash)?;
rpc_client
.send_and_confirm_transaction(&tx)
@ -561,22 +512,9 @@ impl<'a> RequestBuilder<'a> {
self,
config: RpcSendTransactionConfig,
) -> Result<Signature, ClientError> {
let instructions = self.instructions()?;
let mut signers = self.signers;
signers.push(&*self.payer);
let rpc_client = RpcClient::new_with_commitment(self.cluster, self.options);
let tx = {
let latest_hash = rpc_client.get_latest_blockhash()?;
Transaction::new_signed_with_payer(
&instructions,
Some(&self.payer.pubkey()),
&signers,
latest_hash,
)
};
let rpc_client = RpcClient::new_with_commitment(&self.cluster, self.options);
let latest_hash = rpc_client.get_latest_blockhash()?;
let tx = self.signed_transaction_with_blockhash(latest_hash)?;
rpc_client
.send_and_confirm_transaction_with_spinner_and_config(

View File

@ -2,11 +2,11 @@ WORKDIR=$(PWD)
#
# Extract anchor version from the Cargo.toml.
#
ANCHOR_CLI=v$(shell awk -F ' = ' '$$1 ~ /version/ { gsub(/[\"]/, "", $$2); printf("%s",$$2) }' ../cli/Cargo.toml)
ANCHOR_CLI=v0.26.0
#
# Solana toolchain.
#
SOLANA_CLI=v1.10.29
SOLANA_CLI=v1.13.5
#
# Build version should match the Anchor cli version.
#

View File

@ -6,7 +6,8 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
"lint": "next lint",
"publish:typedoc": "gh-pages -d ./src/.vuepress/dist"
},
"browserslist": "defaults, not ie <= 11",
"dependencies": {
@ -19,6 +20,7 @@
"autoprefixer": "^10.4.7",
"clsx": "^1.2.1",
"focus-visible": "^5.2.0",
"gh-pages": "^4.0.0",
"next": "12.2.1",
"next-plausible": "^3.2.0",
"postcss-focus-visible": "^6.0.4",

View File

@ -16,6 +16,6 @@ cpi = ["no-entrypoint"]
default = []
[dependencies]
anchor-lang = "=0.24.1"
anchor-lang = "=0.26.0"
num-traits = "0.2"
num-derive = "0.3"

View File

@ -20,6 +20,7 @@ FLAGS:
-V, --version Prints version information
SUBCOMMANDS:
account Fetch and deserialize an account using the IDL provided
build Builds the workspace
cluster Cluster commands
deploy Deploys each program in the workspace
@ -37,6 +38,28 @@ SUBCOMMANDS:
Cargo.toml
```
## Account
```
anchor account <program-name>.<AccountTypeName> <account_pubkey>
```
Fetches an account with the given public key and deserializes the data to JSON using the type name provided. If this command is run from within a workspace, the workspace's IDL files will be used to get the data types. Otherwise, the path to the IDL file must be provided.
The `program-name` is the name of the program where the account struct resides, usually under `programs/<program-name>`. `program-name` should be provided in a case-sensitive manner exactly as the folder name, usually in kebab-case.
The `AccountTypeName` is the name of the account struct, usually in PascalCase.
The `account_pubkey` refers to the Pubkey of the account to deserialise, in Base58.
Example Usage: `anchor account anchor-escrow.EscrowAccount 3PNkzWKXCsbjijbasnx55NEpJe8DFXvEEbJKdRKpDcfK`, deserializes an account in the given pubkey with the account struct `EscrowAccount` defined in the `anchor-escrow` program.
```
anchor account <program-name>.<AccountTypeName> <account_pubkey> --idl <path/to/idl.json>
```
Deserializes the account with the data types provided in the given IDL file even if inside a workspace.
## Build
```shell
@ -186,7 +209,7 @@ from the workspace's `Anchor.toml`. For example,
```javascript
// File: migrations/deploys.js
const anchor = require('@project-serum/anchor')
const anchor = require('@coral-xyz/anchor')
module.exports = async function (provider) {
anchor.setProvider(provider)

View File

@ -147,8 +147,8 @@ impl<'info> PullStrings<'info> {
We can verify that everything works as expected by replacing the contents of the `puppet.ts` file with:
```ts
import * as anchor from '@project-serum/anchor'
import { Program } from '@project-serum/anchor'
import * as anchor from '@coral-xyz/anchor'
import { Program } from '@coral-xyz/anchor'
import { Keypair } from '@solana/web3.js'
import { expect } from 'chai'
import { Puppet } from '../target/types/puppet'
@ -286,8 +286,8 @@ impl<'info> PullStrings<'info> {
Finally, change the test:
```ts
import * as anchor from '@project-serum/anchor'
import { Program } from '@project-serum/anchor'
import * as anchor from '@coral-xyz/anchor'
import { Program } from '@coral-xyz/anchor'
import { Keypair } from '@solana/web3.js'
import { Puppet } from '../target/types/puppet'
import { PuppetMaster } from '../target/types/puppet_master'

View File

@ -32,13 +32,13 @@ Anchor version manager is a tool for using multiple versions of the anchor-cli.
Install `avm` using Cargo. Note this will replace your `anchor` binary if you had one installed.
```shell
cargo install --git https://github.com/project-serum/anchor avm --locked --force
cargo install --git https://github.com/coral-xyz/anchor avm --locked --force
```
On Linux systems you may need to install additional dependencies if cargo install fails. E.g. on Ubuntu:
```shell
sudo apt-get update && sudo apt-get upgrade && sudo apt-get install -y pkg-config build-essential libudev-dev
sudo apt-get update && sudo apt-get upgrade && sudo apt-get install -y pkg-config build-essential libudev-dev libssl-dev
```
Install the latest version of the CLI using `avm`, and then set it to be the version to use.
@ -56,14 +56,14 @@ anchor --version
### 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'.
Anchor binaries are available via an NPM package [`@coral-xyz/anchor-cli`](https://www.npmjs.com/package/@coral-xyz/anchor-cli). Only `x86_64` Linux is supported currently, you must build from source for other OS'.
### Build from source for other operating systems without avm
We can also use Cargo to install the CLI directly. Make sure that the `--tag` argument uses the version you want (the version here is just an example).
```shell
cargo install --git https://github.com/project-serum/anchor --tag v0.24.1 anchor-cli --locked
cargo install --git https://github.com/coral-xyz/anchor --tag v0.26.0 anchor-cli --locked
```
On Linux systems you may need to install additional dependencies if cargo install fails. On Ubuntu,

View File

@ -31,7 +31,7 @@ The first point means that even if in theory the program may read and write to a
> This design is partly responsible for Solanas high throughput. The runtime can look at all the incoming transactions of a program (and even across programs) and can check whether the memory regions in the first argument of the transactions overlap. If they dont, the runtime can run these transactions in parallel because they dont conflict with each other. Even better, if the runtime sees that two transactions access overlapping memory regions but only read and dont write, it can also parallelize those transactions because they do not conflict with each other.
How exactly can a transaction specify a memory region/account? To answer that, we need to look deeper into what properties an account has ([docs here](https://docs.rs/solana-program/latest/solana_program/account_info/struct.AccountInfo.html). This is the data structure for an account in a transaction. The `is_signer` and `is_writable` fields are set per transaction (e.g. `is_signed` is set if the corresponding private key of the account's `key` field signed the transaction) and are not part of the metadata that is saved in the heap). In front of the user data that the account can store (in the `data` field) , there is some metadata connected to each account. First, it has a key property which is a ed25519 public key and serves as the address of the account. This is how the transaction can specify which accounts the program may access in the transaction.
How exactly can a transaction specify a memory region/account? To answer that, we need to look deeper into what properties an account has ([docs here](https://docs.rs/solana-program/latest/solana_program/account_info/struct.AccountInfo.html)). This is the data structure for an account in a transaction. The `is_signer` and `is_writable` fields are set per transaction (e.g. `is_signed` is set if the corresponding private key of the account's `key` field signed the transaction) and are not part of the metadata that is saved in the heap). In front of the user data that the account can store (in the `data` field) , there is some metadata connected to each account. First, it has a key property which is a ed25519 public key and serves as the address of the account. This is how the transaction can specify which accounts the program may access in the transaction.
![Transaction](/transaction.svg)

View File

@ -37,6 +37,51 @@ Example:
url = "https://anchor.projectserum.com"
```
## features
#### seeds
This tells the IDL to include seed generation for PDA Accounts. The default is `false`
Example:
```
[features]
seeds = true
```
## workspace
#### types
Adds a directory where you want the `<idl>.ts` file to be copied when running `anchor build` or `anchor idl parse`. This is helpful when you want to keep this file in version control, like when using it on the frontend, which will probably not have access to the `target` directory generated by anchor.
Example:
```toml
[workspace]
types = "app/src/idl/"
```
#### members
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.
Example:
```toml
[workspace]
members = [
"programs/*",
"other_place/my_program"
]
```
## programs
Example:
@ -71,7 +116,7 @@ Example
```toml
[[test.genesis]]
address = "9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin"
address = "srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX"
program = "dex.so"
[[test.genesis]]

View File

@ -190,8 +190,8 @@ pub fn change_user_name(ctx: Context<ChangeUserName>, new_name: String) -> Resul
Finally, let's add a test. Copy this into `game.ts`
```ts
import * as anchor from '@project-serum/anchor'
import { Program } from '@project-serum/anchor'
import * as anchor from '@coral-xyz/anchor'
import { Program } from '@coral-xyz/anchor'
import { PublicKey } from '@solana/web3.js'
import { Game } from '../target/types/game'
import { expect } from 'chai'
@ -306,8 +306,8 @@ The `authority` account is now an `UncheckedAccount` instead of a `Signer`. When
Finally, this is the new `puppet.ts`:
```ts
import * as anchor from '@project-serum/anchor'
import { Program } from '@project-serum/anchor'
import * as anchor from '@coral-xyz/anchor'
import { Program } from '@coral-xyz/anchor'
import { Keypair, PublicKey } from '@solana/web3.js'
import { Puppet } from '../target/types/puppet'
import { PuppetMaster } from '../target/types/puppet_master'

View File

@ -34,7 +34,7 @@ have an `Anchor.toml` to define the build.
An example `Anchor.toml` config looks as follows,
```toml
anchor_version = "0.25.0"
anchor_version = "0.26.0"
[workspace]
members = ["programs/multisig"]

View File

@ -8,7 +8,7 @@ The minor version will be incremented upon a breaking change and the patch versi
---
## [0.25.0] - 2022-07-05
## [0.26.0] - 2022-07-05
### Features

View File

@ -169,8 +169,6 @@ pub struct Initialize<'info> {
}
```
Note the doc comment needs to be a [line or block doc comment](https://doc.rust-lang.org/reference/comments.html#doc-comments) (/// or /\*\*) to be interepreted as doc attribute by Rust. Double slash comments (//) are not interpreted as such.
{% callout type="warning" title="Note" %}
The doc comment needs to be a [line or block doc comment](https://doc.rust-lang.org/reference/comments.html#doc-comments) (/// or /\*\*) to be interepreted as doc attribute by Rust. Double slash comments (//) are not interpreted as such.
{% /callout %}

View File

@ -416,7 +416,7 @@ You can create then a new `it` test, setup the game like in the previous test, b
```typescript
it('player one wins', async () => {
const gameKeypair = anchor.web3.Keypair.generate()
const playerOne = program.provider.wallet
const playerOne = (program.provider as anchor.AnchorProvider).wallet
const playerTwo = anchor.web3.Keypair.generate()
await program.methods
.setupGame(playerTwo.publicKey)
@ -455,7 +455,7 @@ it('player one wins', async () => {
and run `anchor test`.
You can finish writing the test by yourself (or check out [the reference implementation](https://github.com/project-serum/anchor-book/tree/master/programs/tic-tac-toe)). Try to simulate a win and a tie!
You can finish writing the test by yourself (or check out [the reference implementation](https://github.com/coral-xyz/anchor/tree/master/docs/programs/tic-tac-toe)). Try to simulate a win and a tie!
Proper testing also includes tests that try to exploit the contract. You can check whether you've protected yourself properly by calling `play` with unexpected parameters. You can also familiarize yourself with the returned `AnchorErrors` this way. For example:
@ -538,7 +538,7 @@ There is more to deployments than this e.g. understanding how the BPFLoader work
## Program directory organization
> [Program Code](https://github.com/project-serum/anchor-book/tree/master/programs/tic-tac-toe)
> [Program Code](https://github.com/coral-xyz/anchor/tree/master/docs/programs/tic-tac-toe)
Eventually, some programs become too big to keep them in a single file and it makes sense to break them up.

View File

@ -37,10 +37,10 @@ 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:<version>`. For example, to get the image for Anchor `v0.25.0` one can run
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:<version>`. For example, to get the image for Anchor `v0.26.0` one can run
```shell
docker pull projectserum/build:v0.25.0
docker pull projectserum/build:v0.26.0
```
## Removing an Image

13
docs/vercel.sh Executable file
View File

@ -0,0 +1,13 @@
#!/bin/bash
git diff --quiet HEAD^ HEAD ./
has_changes=$?
echo ">> Diff status $has_changes"
if [[ $VERCEL_GIT_COMMIT_REF == "master" ]] || [ $has_changes == 1 ]; then
echo ">> Proceeding with deployment."
exit 1;
else
echo ">> Skipping deployment."
exit 0;
fi

View File

@ -2,7 +2,7 @@
// 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");
const anchor = require("@coral-xyz/anchor");
// Configure the local cluster.
anchor.setProvider(anchor.AnchorProvider.local());

View File

@ -1,6 +1,6 @@
{
"name": "basic-0",
"version": "0.25.0",
"version": "0.26.0",
"license": "(MIT OR Apache-2.0)",
"homepage": "https://github.com/coral-xyz/anchor#readme",
"bugs": {

View File

@ -2,7 +2,7 @@
name = "basic-0"
version = "0.1.0"
description = "Created with Anchor"
rust-version = "1.56"
rust-version = "1.59"
edition = "2021"
[lib]

View File

@ -1,4 +1,4 @@
const anchor = require("@project-serum/anchor");
const anchor = require("@coral-xyz/anchor");
describe("basic-0", () => {
// Configure the client to use the local cluster.
@ -10,7 +10,7 @@ describe("basic-0", () => {
const program = anchor.workspace.Basic0;
// Execute the RPC.
await program.rpc.initialize();
await program.methods.initialize().rpc();
// #endregion code
});
});

View File

@ -1,6 +1,6 @@
{
"name": "basic-1",
"version": "0.25.0",
"version": "0.26.0",
"license": "(MIT OR Apache-2.0)",
"homepage": "https://github.com/coral-xyz/anchor#readme",
"bugs": {

View File

@ -2,7 +2,7 @@
name = "basic-1"
version = "0.1.0"
description = "Created with Anchor"
rust-version = "1.56"
rust-version = "1.59"
edition = "2021"
[lib]

View File

@ -1,5 +1,5 @@
const assert = require("assert");
const anchor = require("@project-serum/anchor");
const anchor = require("@coral-xyz/anchor");
const { SystemProgram } = anchor.web3;
describe("basic-1", () => {
@ -19,14 +19,15 @@ describe("basic-1", () => {
// Create the new account and initialize it with the program.
// #region code-simplified
await program.rpc.initialize(new anchor.BN(1234), {
accounts: {
await program.methods
.initialize(new anchor.BN(1234))
.accounts({
myAccount: myAccount.publicKey,
user: provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
},
signers: [myAccount],
});
})
.signers([myAccount])
.rpc();
// #endregion code-simplified
// Fetch the newly created account from the cluster.
@ -48,11 +49,12 @@ describe("basic-1", () => {
const program = anchor.workspace.Basic1;
// Invoke the update rpc.
await program.rpc.update(new anchor.BN(4321), {
accounts: {
await program.methods
.update(new anchor.BN(4321))
.accounts({
myAccount: myAccount.publicKey,
},
});
})
.rpc();
// Fetch the newly updated account.
const account = await program.account.myAccount.fetch(myAccount.publicKey);

View File

@ -1,6 +1,6 @@
{
"name": "basic-2",
"version": "0.25.0",
"version": "0.26.0",
"license": "(MIT OR Apache-2.0)",
"homepage": "https://github.com/coral-xyz/anchor#readme",
"bugs": {

View File

@ -2,7 +2,7 @@
name = "basic-2"
version = "0.1.0"
description = "Created with Anchor"
rust-version = "1.56"
rust-version = "1.59"
edition = "2021"
[lib]

View File

@ -1,5 +1,5 @@
const assert = require("assert");
const anchor = require("@project-serum/anchor");
const anchor = require("@coral-xyz/anchor");
const { SystemProgram } = anchor.web3;
describe("basic-2", () => {
@ -15,14 +15,15 @@ describe("basic-2", () => {
const program = anchor.workspace.Basic2;
it("Creates a counter", async () => {
await program.rpc.create(provider.wallet.publicKey, {
accounts: {
await program.methods
.create(provider.wallet.publicKey)
.accounts({
counter: counter.publicKey,
user: provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
},
signers: [counter],
});
})
.signers([counter])
.rpc();
let counterAccount = await program.account.counter.fetch(counter.publicKey);
@ -31,12 +32,13 @@ describe("basic-2", () => {
});
it("Updates a counter", async () => {
await program.rpc.increment({
accounts: {
await program.methods
.increment()
.accounts({
counter: counter.publicKey,
authority: provider.wallet.publicKey,
},
});
})
.rpc();
const counterAccount = await program.account.counter.fetch(
counter.publicKey

View File

@ -1,6 +1,6 @@
{
"name": "basic-3",
"version": "0.25.0",
"version": "0.26.0",
"license": "(MIT OR Apache-2.0)",
"homepage": "https://github.com/coral-xyz/anchor#readme",
"bugs": {

View File

@ -2,7 +2,7 @@
name = "puppet-master"
version = "0.1.0"
description = "Created with Anchor"
rust-version = "1.56"
rust-version = "1.59"
edition = "2021"
[lib]

View File

@ -2,7 +2,7 @@
name = "puppet"
version = "0.1.0"
description = "Created with Anchor"
rust-version = "1.56"
rust-version = "1.59"
edition = "2021"
[lib]

View File

@ -1,5 +1,5 @@
const assert = require("assert");
const anchor = require("@project-serum/anchor");
const anchor = require("@coral-xyz/anchor");
const { SystemProgram } = anchor.web3;
describe("basic-3", () => {
@ -14,22 +14,24 @@ describe("basic-3", () => {
// Initialize a new puppet account.
const newPuppetAccount = anchor.web3.Keypair.generate();
const tx = await puppet.rpc.initialize({
accounts: {
const tx = await puppet.methods
.initialize()
.accounts({
puppet: newPuppetAccount.publicKey,
user: provider.wallet.publicKey,
systemProgram: SystemProgram.programId,
},
signers: [newPuppetAccount],
});
})
.signers([newPuppetAccount])
.rpc();
// Invoke the puppet master to perform a CPI to the puppet.
await puppetMaster.rpc.pullStrings(new anchor.BN(111), {
accounts: {
await puppetMaster.methods
.pullStrings(new anchor.BN(111))
.accounts({
puppet: newPuppetAccount.publicKey,
puppetProgram: puppet.programId,
},
});
})
.rpc();
// Check the state updated.
puppetAccount = await puppet.account.data.fetch(newPuppetAccount.publicKey);

View File

@ -1,6 +1,6 @@
{
"name": "basic-4",
"version": "0.25.0",
"version": "0.26.0",
"license": "(MIT OR Apache-2.0)",
"homepage": "https://github.com/coral-xyz/anchor#readme",
"bugs": {

View File

@ -2,7 +2,7 @@
name = "basic-4"
version = "0.1.0"
description = "Created with Anchor"
rust-version = "1.56"
rust-version = "1.59"
edition = "2021"
[lib]

View File

@ -1,5 +1,5 @@
// #region code
use anchor_lang::prelude::*;
use std::ops::DerefMut;
declare_id!("CwrqeMj2U8tFr1Rhkgwc84tpAsqbt9pTt2a4taoTADPr");
@ -7,38 +7,72 @@ declare_id!("CwrqeMj2U8tFr1Rhkgwc84tpAsqbt9pTt2a4taoTADPr");
pub mod basic_4 {
use super::*;
#[state]
pub struct Counter {
pub authority: Pubkey,
pub count: u64,
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
let counter = ctx.accounts.counter.deref_mut();
let bump = *ctx.bumps.get("counter").ok_or(ErrorCode::CannotGetBump)?;
*counter = Counter {
authority: *ctx.accounts.authority.key,
count: 0,
bump,
};
Ok(())
}
impl Counter {
pub fn new(ctx: Context<Auth>) -> anchor_lang::Result<Self> {
Ok(Self {
authority: *ctx.accounts.authority.key,
count: 0,
})
}
pub fn increment(ctx: Context<Increment>) -> Result<()> {
require_keys_eq!(
ctx.accounts.authority.key(),
ctx.accounts.counter.authority,
ErrorCode::Unauthorized
);
pub fn increment(&mut self, ctx: Context<Auth>) -> anchor_lang::Result<()> {
if &self.authority != ctx.accounts.authority.key {
return Err(error!(ErrorCode::Unauthorized));
}
self.count += 1;
Ok(())
}
ctx.accounts.counter.count += 1;
Ok(())
}
}
#[derive(Accounts)]
pub struct Auth<'info> {
pub struct Initialize<'info> {
#[account(
init,
payer = authority,
space = Counter::SIZE,
seeds = [b"counter"],
bump
)]
counter: Account<'info, Counter>,
#[account(mut)]
authority: Signer<'info>,
system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Increment<'info> {
#[account(
mut,
seeds = [b"counter"],
bump = counter.bump
)]
counter: Account<'info, Counter>,
authority: Signer<'info>,
}
// #endregion code
#[account]
pub struct Counter {
pub authority: Pubkey,
pub count: u64,
pub bump: u8,
}
impl Counter {
pub const SIZE: usize = 8 + 32 + 8 + 1;
}
#[error_code]
pub enum ErrorCode {
#[msg("You are not authorized to perform this action.")]
Unauthorized,
#[msg("Cannot get the bump.")]
CannotGetBump,
}

View File

@ -1,5 +1,5 @@
const assert = require("assert");
const anchor = require("@project-serum/anchor");
const anchor = require("@coral-xyz/anchor");
describe("basic-4", () => {
const provider = anchor.AnchorProvider.local();
@ -7,35 +7,45 @@ describe("basic-4", () => {
// Configure the client to use the local cluster.
anchor.setProvider(provider);
const program = anchor.workspace.Basic4;
const program = anchor.workspace.Basic4,
counterSeed = anchor.utils.bytes.utf8.encode("counter");
let counterPubkey;
before(async () => {
[counterPubkey] = await anchor.web3.PublicKey.findProgramAddress(
[counterSeed],
program.programId
);
});
it("Is runs the constructor", async () => {
// #region ctor
// Initialize the program's state struct.
await program.state.rpc.new({
accounts: {
await program.methods
.initialize()
.accounts({
counter: counterPubkey,
authority: provider.wallet.publicKey,
},
});
// #endregion ctor
systemProgram: anchor.web3.SystemProgram.programId,
})
.rpc();
// Fetch the state struct from the network.
// #region accessor
const state = await program.state.fetch();
// #endregion accessor
const counterAccount = await program.account.counter.fetch(counterPubkey);
assert.ok(state.count.eq(new anchor.BN(0)));
assert.ok(counterAccount.count.eq(new anchor.BN(0)));
});
it("Executes a method on the program", async () => {
// #region instruction
await program.state.rpc.increment({
accounts: {
await program.methods
.increment()
.accounts({
counter: counterPubkey,
authority: provider.wallet.publicKey,
},
});
// #endregion instruction
const state = await program.state.fetch();
assert.ok(state.count.eq(new anchor.BN(1)));
})
.rpc();
const counterAccount = await program.account.counter.fetch(counterPubkey);
assert.ok(counterAccount.count.eq(new anchor.BN(1)));
});
});

View File

@ -13,7 +13,7 @@
"basic-4"
],
"dependencies": {
"@project-serum/anchor": "file:../../ts"
"@coral-xyz/anchor": "file:../../ts/packages/anchor"
},
"devDependencies": {
"mocha": "^9.1.3",

View File

@ -2,44 +2,30 @@
# yarn lockfile v1
"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5":
"@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==
"@babel/runtime@^7.17.2":
version "7.20.6"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.6.tgz#facf4879bfed9b5326326273a64220f099b0fce3"
integrity sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA==
dependencies:
"@ethersproject/logger" "^5.5.0"
regenerator-runtime "^0.13.11"
"@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==
"@coral-xyz/anchor@file:../../ts/packages/anchor":
version "0.26.0"
dependencies:
"@ethersproject/bytes" "^5.5.0"
"@ethersproject/logger" "^5.5.0"
hash.js "1.1.7"
"@project-serum/anchor@file:../../ts":
version "0.25.0"
dependencies:
"@project-serum/borsh" "^0.2.5"
"@solana/web3.js" "^1.36.0"
"@coral-xyz/borsh" "^0.26.0"
"@solana/web3.js" "^1.68.0"
base64-js "^1.5.1"
bn.js "^5.1.2"
bs58 "^4.0.1"
buffer-layout "^1.2.2"
camelcase "^5.3.1"
camelcase "^6.3.0"
cross-fetch "^3.1.5"
crypto-hash "^1.3.0"
eventemitter3 "^4.0.7"
@ -49,47 +35,56 @@
superstruct "^0.15.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==
"@coral-xyz/borsh@^0.26.0":
version "0.26.0"
resolved "https://registry.yarnpkg.com/@coral-xyz/borsh/-/borsh-0.26.0.tgz#d054f64536d824634969e74138f9f7c52bbbc0d5"
integrity sha512-uCZ0xus0CszQPHYfWAqKS5swS1UxvePu83oOF+TWpUkedsNlg6p2p4azxZNSSqwXb9uXMFgxhuMBX9r3Xoi0vQ==
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==
"@noble/ed25519@^1.7.0":
version "1.7.1"
resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.1.tgz#6899660f6fbb97798a6fbd227227c4589a454724"
integrity sha512-Rk4SkJFaXZiznFyC/t77Q0NKS4FL7TLJJsVG2V2oiEq3kJVeTdxysEe/yRWSpnWMe808XRDJ+VFh5pt/FN5plw==
"@noble/hashes@^1.1.2":
version "1.1.4"
resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.4.tgz#2611ebf5764c1bf754da7c7794de4fb30512336d"
integrity sha512-+PYsVPrTSqtVjatKt2A/Proukn2Yrz61OBThOCKErc5w2/r1Fh37vbDv0Eah7pyNltrmacjwTvdw3JoR+WE4TA==
"@noble/secp256k1@^1.6.3":
version "1.7.0"
resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.0.tgz#d15357f7c227e751d90aa06b05a0e5cf993ba8c1"
integrity sha512-kbacwGSsH/CTout0ZnZWxnW1B+jH/7r/WAAKLBtrRJ/+CUH7lgmQzl3GTrQua3SGKWNSDsS6lmjnDpIJ5Dxyaw==
"@solana/buffer-layout@^4.0.0":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.1.tgz#b996235eaec15b1e0b5092a8ed6028df77fa6c15"
integrity sha512-E1ImOIAD1tBZFRdjeM4/pzTiTApC0AOBGwyAMS4fwIodCWArzJ3DWdoh8cKxeFM2fElkxBh2Aqts1BPC373rHA==
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==
"@solana/web3.js@^1.68.0":
version "1.70.1"
resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.70.1.tgz#4a2df47cc32a0f67be5161e772b2ceb6512281fa"
integrity sha512-AnaqCF1cJ3w7d0yhvLGAKAcRI+n5o+ursQihhoTe4cUh8/9d4gbT73SoHYElS7e67OtAgLmSfbcC5hcOAgdvnQ==
dependencies:
"@babel/runtime" "^7.12.5"
"@ethersproject/sha2" "^5.5.0"
"@solana/buffer-layout" "^3.0.0"
"@noble/ed25519" "^1.7.0"
"@noble/hashes" "^1.1.2"
"@noble/secp256k1" "^1.6.3"
"@solana/buffer-layout" "^4.0.0"
bigint-buffer "^1.1.5"
bn.js "^5.0.0"
borsh "^0.4.0"
borsh "^0.7.0"
bs58 "^4.0.1"
buffer "6.0.1"
cross-fetch "^3.1.4"
fast-stable-stringify "^1.0.0"
jayson "^3.4.4"
js-sha3 "^0.8.0"
rpc-websockets "^7.4.2"
secp256k1 "^4.0.2"
node-fetch "2"
rpc-websockets "^7.5.0"
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"
@ -199,28 +194,41 @@ base64-js@^1.3.1, base64-js@^1.5.1:
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
bigint-buffer@^1.1.5:
version "1.1.5"
resolved "https://registry.yarnpkg.com/bigint-buffer/-/bigint-buffer-1.1.5.tgz#d038f31c8e4534c1f8d0015209bf34b4fa6dd442"
integrity sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==
dependencies:
bindings "^1.3.0"
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==
bindings@^1.3.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==
dependencies:
file-uri-to-path "1.0.0"
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==
bn.js@^5.2.0:
version "5.2.1"
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.1.tgz#0bc527a6a0d18d0aa8d5b0538ce4a77dccfa7b70"
integrity sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==
borsh@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.7.0.tgz#6e9560d719d86d90dc589bca60ffc8a6c51fec2a"
integrity sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==
dependencies:
"@types/bn.js" "^4.11.5"
bn.js "^5.0.0"
bn.js "^5.2.0"
bs58 "^4.0.0"
text-encoding-utf-8 "^1.0.2"
@ -239,11 +247,6 @@ braces@~3.0.2:
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"
@ -284,16 +287,16 @@ bufferutil@^4.0.1:
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==
camelcase@^6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a"
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
chalk@^4.1.0:
version "4.1.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
@ -317,11 +320,6 @@ chokidar@3.5.2:
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"
@ -353,13 +351,6 @@ concat-map@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"
@ -402,19 +393,6 @@ dot-case@^3.0.4:
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"
@ -452,6 +430,16 @@ eyes@^0.1.8:
resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0"
integrity sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=
fast-stable-stringify@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz#5c5543462b22aeeefd36d05b34e51c78cb86d313"
integrity sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==
file-uri-to-path@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd"
integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
@ -516,28 +504,11 @@ has-flag@^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"
@ -551,7 +522,7 @@ inflight@^1.0.4:
once "^1.3.0"
wrappy "1"
inherits@2, inherits@^2.0.3, inherits@^2.0.4:
inherits@2:
version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@ -631,11 +602,6 @@ js-sha256@^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"
@ -680,16 +646,6 @@ lower-case@^2.0.2:
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"
@ -750,24 +706,14 @@ no-case@^3.0.4:
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:
node-fetch@2, 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:
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==
@ -837,6 +783,11 @@ readdirp@~3.6.0:
dependencies:
picomatch "^2.2.1"
regenerator-runtime@^0.13.11:
version "0.13.11"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
regenerator-runtime@^0.13.4:
version "0.13.9"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52"
@ -847,16 +798,15 @@ require-directory@^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==
rpc-websockets@^7.5.0:
version "7.5.0"
resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.5.0.tgz#bbeb87572e66703ff151e50af1658f98098e2748"
integrity sha512-9tIRi1uZGy7YmDjErf1Ax3wtqdSSLIlnmL5OtOzgd5eqPKbsPpwDP5whUDO2LQay3Xp0CcHlcNSGzacNRluBaQ==
dependencies:
"@babel/runtime" "^7.11.2"
circular-json "^0.5.9"
"@babel/runtime" "^7.17.2"
eventemitter3 "^4.0.7"
uuid "^8.3.0"
ws "^7.4.5"
uuid "^8.3.2"
ws "^8.5.0"
optionalDependencies:
bufferutil "^4.0.1"
utf-8-validate "^5.0.2"
@ -866,15 +816,6 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.0:
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"
@ -967,11 +908,6 @@ tslib@^2.0.3:
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"
@ -984,7 +920,7 @@ uuid@^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:
uuid@^8.3.2:
version "8.3.2"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
@ -1033,6 +969,11 @@ ws@^7.4.5:
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.5.tgz#8b4bc4af518cfabd0473ae4f99144287b33eb881"
integrity sha512-BAkMFcAzl8as1G/hArkxOxq3G7pjUqQ3gzYbLL0/5zNkph70e+lCoxBGnm6AW1+/aiNeV4fnKqZ8m4GZewmH2w==
ws@^8.5.0:
version "8.11.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143"
integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==
y18n@^5.0.5:
version "5.0.8"
resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"

View File

@ -1,14 +1,15 @@
[package]
name = "anchor-lang"
version = "0.25.0"
version = "0.26.0"
authors = ["Serum Foundation <foundation@projectserum.com>"]
repository = "https://github.com/coral-xyz/anchor"
rust-version = "1.56"
rust-version = "1.59"
edition = "2021"
license = "Apache-2.0"
description = "Solana Sealevel eDSL"
[features]
allow-missing-optionals = ["anchor-derive-accounts/allow-missing-optionals"]
init-if-needed = ["anchor-derive-accounts/init-if-needed"]
derive = []
default = []
@ -18,27 +19,23 @@ 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.25.0" }
anchor-attribute-account = { path = "./attribute/account", version = "0.25.0" }
anchor-attribute-constant = { path = "./attribute/constant", version = "0.25.0" }
anchor-attribute-error = { path = "./attribute/error", version = "0.25.0" }
anchor-attribute-program = { path = "./attribute/program", version = "0.25.0" }
anchor-attribute-state = { path = "./attribute/state", version = "0.25.0" }
anchor-attribute-interface = { path = "./attribute/interface", version = "0.25.0" }
anchor-attribute-event = { path = "./attribute/event", version = "0.25.0" }
anchor-derive-accounts = { path = "./derive/accounts", version = "0.25.0" }
anchor-attribute-access-control = { path = "./attribute/access-control", version = "0.26.0" }
anchor-attribute-account = { path = "./attribute/account", version = "0.26.0" }
anchor-attribute-constant = { path = "./attribute/constant", version = "0.26.0" }
anchor-attribute-error = { path = "./attribute/error", version = "0.26.0" }
anchor-attribute-program = { path = "./attribute/program", version = "0.26.0" }
anchor-attribute-event = { path = "./attribute/event", version = "0.26.0" }
anchor-derive-accounts = { path = "./derive/accounts", version = "0.26.0" }
arrayref = "0.3.6"
base64 = "0.13.0"
borsh = "0.9"
bytemuck = "1.4.0"
solana-program = "~1.10.29"
solana-program = "1.13.5"
thiserror = "1.0.20"
bincode = "1.3.3"

View File

@ -1,11 +1,11 @@
[package]
name = "anchor-attribute-access-control"
version = "0.25.0"
version = "0.26.0"
authors = ["Serum Foundation <foundation@projectserum.com>"]
repository = "https://github.com/coral-xyz/anchor"
license = "Apache-2.0"
description = "Anchor attribute macro for instruction access control"
rust-version = "1.56"
rust-version = "1.59"
edition = "2021"
[lib]
@ -19,5 +19,5 @@ proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "1.0.60", features = ["full"] }
anyhow = "1.0.32"
anchor-syn = { path = "../../syn", version = "0.25.0" }
anchor-syn = { path = "../../syn", version = "0.26.0" }
regex = "1.0"

View File

@ -1,11 +1,11 @@
[package]
name = "anchor-attribute-account"
version = "0.25.0"
version = "0.26.0"
authors = ["Serum Foundation <foundation@projectserum.com>"]
repository = "https://github.com/coral-xyz/anchor"
license = "Apache-2.0"
description = "Anchor attribute macro for defining an account"
rust-version = "1.56"
rust-version = "1.59"
edition = "2021"
[lib]
@ -19,6 +19,6 @@ proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "1.0.60", features = ["full"] }
anyhow = "1.0.32"
anchor-syn = { path = "../../syn", version = "0.25.0", features = ["hash"] }
anchor-syn = { path = "../../syn", version = "0.26.0", features = ["hash"] }
rustversion = "1.0.3"
bs58 = "0.4.0"

View File

@ -214,10 +214,10 @@ fn parse_pubkey(
) -> Result<proc_macro2::TokenStream> {
let id_vec = bs58::decode(id_literal.value())
.into_vec()
.map_err(|_| syn::Error::new_spanned(&id_literal, "failed to decode base58 string"))?;
.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,
id_literal,
format!("pubkey array is not 32 bytes long: len={}", id_vec.len()),
)
})?;

View File

@ -139,9 +139,7 @@ pub fn account(
#[automatically_derived]
impl #impl_gen anchor_lang::Discriminator for #account_name #type_gen #where_clause {
fn discriminator() -> [u8; 8] {
#discriminator
}
const DISCRIMINATOR: [u8; 8] = #discriminator;
}
// This trait is useful for clients deserializing accounts.
@ -211,9 +209,7 @@ pub fn account(
#[automatically_derived]
impl #impl_gen anchor_lang::Discriminator for #account_name #type_gen #where_clause {
fn discriminator() -> [u8; 8] {
#discriminator
}
const DISCRIMINATOR: [u8; 8] = #discriminator;
}
#owner_impl

View File

@ -1,11 +1,11 @@
[package]
name = "anchor-attribute-constant"
version = "0.25.0"
version = "0.26.0"
authors = ["Serum Foundation <foundation@projectserum.com>"]
repository = "https://github.com/coral-xyz/anchor"
license = "Apache-2.0"
description = "Anchor attribute macro for creating constant types"
rust-version = "1.56"
rust-version = "1.59"
edition = "2021"
[lib]
@ -17,4 +17,4 @@ anchor-debug = ["anchor-syn/anchor-debug"]
[dependencies]
proc-macro2 = "1.0"
syn = { version = "1.0.60", features = ["full"] }
anchor-syn = { path = "../../syn", version = "0.25.0" }
anchor-syn = { path = "../../syn", version = "0.26.0" }

View File

@ -1,11 +1,11 @@
[package]
name = "anchor-attribute-error"
version = "0.25.0"
version = "0.26.0"
authors = ["Serum Foundation <foundation@projectserum.com>"]
repository = "https://github.com/coral-xyz/anchor"
license = "Apache-2.0"
description = "Anchor attribute macro for creating error types"
rust-version = "1.56"
rust-version = "1.59"
edition = "2021"
[lib]
@ -18,4 +18,4 @@ anchor-debug = ["anchor-syn/anchor-debug"]
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "1.0.60", features = ["full"] }
anchor-syn = { path = "../../syn", version = "0.25.0" }
anchor-syn = { path = "../../syn", version = "0.26.0" }

View File

@ -1,11 +1,11 @@
[package]
name = "anchor-attribute-event"
version = "0.25.0"
version = "0.26.0"
authors = ["Serum Foundation <foundation@projectserum.com>"]
repository = "https://github.com/coral-xyz/anchor"
license = "Apache-2.0"
description = "Anchor attribute macro for defining an event"
rust-version = "1.56"
rust-version = "1.59"
edition = "2021"
[lib]
@ -19,4 +19,4 @@ proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "1.0.60", features = ["full"] }
anyhow = "1.0.32"
anchor-syn = { path = "../../syn", version = "0.25.0", features = ["hash"] }
anchor-syn = { path = "../../syn", version = "0.26.0", features = ["hash"] }

View File

@ -40,9 +40,7 @@ pub fn event(
}
impl anchor_lang::Discriminator for #event_name {
fn discriminator() -> [u8; 8] {
#discriminator
}
const DISCRIMINATOR: [u8; 8] = #discriminator;
}
})
}

View File

@ -1,23 +0,0 @@
[package]
name = "anchor-attribute-interface"
version = "0.25.0"
authors = ["Serum Foundation <foundation@projectserum.com>"]
repository = "https://github.com/coral-xyz/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.25.0" }
heck = "0.3.2"

View File

@ -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<T>, 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<Empty>, auth_program: Pubkey) -> Result<Self> {
/// Ok(Self {
/// count: 0,
/// auth_program,
/// })
/// }
///
/// #[access_control(SetCount::accounts(&self, &ctx))]
/// pub fn set_count(&mut self, ctx: Context<SetCount>, 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<SetCount>) -> 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<Empty>, 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<proc_macro2::TokenStream> = 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<syn::Pat>> = 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)*
}
})
}

View File

@ -1,11 +1,11 @@
[package]
name = "anchor-attribute-program"
version = "0.25.0"
version = "0.26.0"
authors = ["Serum Foundation <foundation@projectserum.com>"]
repository = "https://github.com/coral-xyz/anchor"
license = "Apache-2.0"
description = "Anchor attribute macro for defining a program"
rust-version = "1.56"
rust-version = "1.59"
edition = "2021"
[lib]
@ -19,4 +19,4 @@ proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "1.0.60", features = ["full"] }
anyhow = "1.0.32"
anchor-syn = { path = "../../syn", version = "0.25.0" }
anchor-syn = { path = "../../syn", version = "0.26.0" }

View File

@ -1,22 +0,0 @@
[package]
name = "anchor-attribute-state"
version = "0.25.0"
authors = ["Serum Foundation <foundation@projectserum.com>"]
repository = "https://github.com/coral-xyz/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.25.0" }

View File

@ -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<u64> {
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<u64> {
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<u64> {
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
})
}

View File

@ -1,17 +1,18 @@
[package]
name = "anchor-derive-accounts"
version = "0.25.0"
version = "0.26.0"
authors = ["Serum Foundation <foundation@projectserum.com>"]
repository = "https://github.com/coral-xyz/anchor"
license = "Apache-2.0"
description = "Anchor Derive macro for accounts"
rust-version = "1.56"
rust-version = "1.59"
edition = "2021"
[lib]
proc-macro = true
[features]
allow-missing-optionals = ["anchor-syn/allow-missing-optionals"]
init-if-needed = ["anchor-syn/init-if-needed"]
default = []
anchor-debug = ["anchor-syn/anchor-debug"]
@ -21,4 +22,4 @@ proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "1.0.60", features = ["full"] }
anyhow = "1.0.32"
anchor-syn = { path = "../../syn", version = "0.25.0" }
anchor-syn = { path = "../../syn", version = "0.26.0" }

View File

@ -336,8 +336,8 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AccountsEx
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 {
// Only persist if the owner is the current program and the account is not closed.
if &T::owner() == program_id && !crate::common::is_closed(&self.info) {
let info = self.to_account_info();
let mut data = info.try_borrow_mut_data()?;
let dst: &mut [u8] = &mut data;
@ -348,13 +348,6 @@ impl<'info, T: AccountSerialize + AccountDeserialize + Owner + Clone> AccountsEx
}
}
/// 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>
{
@ -404,7 +397,7 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Owner + Clone> Deref for Acc
type Target = T;
fn deref(&self) -> &Self::Target {
&(*self).account
&(self).account
}
}

View File

@ -235,22 +235,18 @@ impl<'info, T: ZeroCopy + Owner> Accounts<'info> for AccountLoader<'info, T> {
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();
fn exit(&self, program_id: &Pubkey) -> Result<()> {
// Only persist if the owner is the current program and the account is not closed.
if &T::owner() == program_id && !crate::common::is_closed(&self.acc_info) {
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)

View File

@ -1,149 +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, BTreeSet};
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<Inner<'info, T>>,
}
#[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<CpiState<'info, T>> {
let mut data: &[u8] = &info.try_borrow_data()?;
Ok(CpiState::new(info.clone(), T::try_deserialize(&mut data)?))
}
fn seed() -> &'static str {
ProgramState::<T>::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<String, u8>,
_reallocs: &mut BTreeSet<Pubkey>,
) -> Result<Self> {
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 = <account-name>)] 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<bool>) -> Vec<AccountMeta> {
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<AccountInfo<'info>> {
vec![self.inner.info.clone()]
}
}
#[allow(deprecated)]
impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AsRef<AccountInfo<'info>>
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
}
}

View File

@ -179,21 +179,17 @@ impl<'info, T: ZeroCopy> Accounts<'info> for Loader<'info, T> {
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();
// Only persist if the account is not closed.
if !crate::common::is_closed(&self.acc_info) {
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<()> {

View File

@ -9,18 +9,13 @@ pub mod boxed;
pub mod cpi_account;
#[doc(hidden)]
#[allow(deprecated)]
pub mod cpi_state;
#[doc(hidden)]
#[allow(deprecated)]
pub mod loader;
pub mod option;
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;

View File

@ -0,0 +1,84 @@
//! Option<T> type for optional accounts.
//!
//! # Example
//! ```ignore
//! #[derive(Accounts)]
//! pub struct Example {
//! pub my_acc: Option<Account<'info, MyData>>
//! }
//! ```
use std::collections::{BTreeMap, BTreeSet};
use solana_program::account_info::AccountInfo;
use solana_program::instruction::AccountMeta;
use solana_program::pubkey::Pubkey;
use crate::{
error::ErrorCode, Accounts, AccountsClose, AccountsExit, Result, ToAccountInfos, ToAccountMetas,
};
impl<'info, T: Accounts<'info>> Accounts<'info> for Option<T> {
fn try_accounts(
program_id: &Pubkey,
accounts: &mut &[AccountInfo<'info>],
ix_data: &[u8],
bumps: &mut BTreeMap<String, u8>,
reallocs: &mut BTreeSet<Pubkey>,
) -> Result<Self> {
if accounts.is_empty() {
return if cfg!(feature = "allow-missing-optionals") {
// We don't care if accounts is empty (when this feature is active),
// so if that's the case we return None. This allows adding optional
// accounts at the end of the Accounts struct without causing a breaking
// change. This is safe and will error out if a required account is then
// added after the optional account and the accounts aren't passed in.
Ok(None)
} else {
// If the feature is inactive (it is off by default), then we error out
// like every other Account.
Err(ErrorCode::AccountNotEnoughKeys.into())
};
}
// If there are enough accounts, it will check the program_id and return
// None if it matches, popping the first account off the accounts vec.
if accounts[0].key == program_id {
*accounts = &accounts[1..];
Ok(None)
} else {
// If the program_id doesn't equal the account key, we default to
// the try_accounts implementation for the inner type and then wrap that with
// Some. This should handle all possible valid cases.
T::try_accounts(program_id, accounts, ix_data, bumps, reallocs).map(Some)
}
}
}
impl<'info, T: ToAccountInfos<'info>> ToAccountInfos<'info> for Option<T> {
fn to_account_infos(&self) -> Vec<AccountInfo<'info>> {
self.as_ref()
.map_or_else(Vec::new, |account| account.to_account_infos())
}
}
impl<T: ToAccountMetas> ToAccountMetas for Option<T> {
fn to_account_metas(&self, is_signer: Option<bool>) -> Vec<AccountMeta> {
self.as_ref()
.expect("Cannot run `to_account_metas` on None")
.to_account_metas(is_signer)
}
}
impl<'info, T: AccountsClose<'info>> AccountsClose<'info> for Option<T> {
fn close(&self, sol_destination: AccountInfo<'info>) -> Result<()> {
self.as_ref()
.map_or(Ok(()), |t| T::close(t, sol_destination))
}
}
impl<'info, T: AccountsExit<'info>> AccountsExit<'info> for Option<T> {
fn exit(&self, program_id: &Pubkey) -> Result<()> {
self.as_ref().map_or(Ok(()), |t| t.exit(program_id))
}
}

View File

@ -99,22 +99,18 @@ 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)?;
// Only persist if the account is not closed.
if !crate::common::is_closed(&self.inner.info) {
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>
@ -161,7 +157,7 @@ impl<'a, T: AccountSerialize + AccountDeserialize + Clone> Deref for ProgramAcco
type Target = T;
fn deref(&self) -> &Self::Target {
&(*self.inner).account
&(self.inner).account
}
}

View File

@ -1,171 +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, BTreeSet};
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<Inner<'info, T>>,
}
#[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<ProgramState<'a, T>> {
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<String, u8>,
_reallocs: &mut BTreeSet<Pubkey>,
) -> Result<Self> {
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<bool>) -> Vec<AccountMeta> {
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<AccountInfo<'info>> {
vec![self.inner.info.clone()]
}
}
#[allow(deprecated)]
impl<'info, T: AccountSerialize + AccountDeserialize + Clone> AsRef<AccountInfo<'info>>
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<CpiAccount<'info, T>> 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
}
}

View File

@ -1,9 +1,7 @@
use crate::bpf_writer::BpfWriter;
use crate::error::ErrorCode;
use crate::prelude::error;
use crate::prelude::{Id, System};
use crate::Result;
use solana_program::account_info::AccountInfo;
use std::io::Write;
use solana_program::system_program;
pub fn close<'info>(info: AccountInfo<'info>, sol_destination: AccountInfo<'info>) -> Result<()> {
// Transfer tokens from the account to the sol_destination.
@ -12,11 +10,10 @@ pub fn close<'info>(info: AccountInfo<'info>, sol_destination: AccountInfo<'info
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))
info.assign(&system_program::ID);
info.realloc(0, false).map_err(Into::into)
}
pub fn is_closed(info: &AccountInfo) -> bool {
info.owner == &System::id() && info.data_is_empty()
}

View File

@ -241,85 +241,3 @@ impl<'info, T: ToAccountInfos<'info> + ToAccountMetas> ToAccountMetas
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<bool>) -> Vec<AccountMeta> {
// 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<AccountInfo<'info>> {
let mut infos = self.cpi_ctx.accounts.to_account_infos();
infos.push(self.state.clone());
infos.push(self.cpi_ctx.program.clone());
infos
}
}

View File

@ -12,7 +12,6 @@ pub const ERROR_CODE_OFFSET: u32 = 6000;
/// - &gt;= 1000 IDL error codes
/// - &gt;= 2000 constraint error codes
/// - &gt;= 3000 account error codes
/// - = 4000 state error code
/// - &gt;= 4100 misc error codes
/// - = 5000 deprecated error code
///
@ -67,8 +66,8 @@ pub enum ErrorCode {
/// 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")]
/// 2008 - Deprecated Error, feel free to replace with something else
#[msg("Deprecated Error, feel free to replace with something else")]
ConstraintState,
/// 2009 - An associated constraint was violated
#[msg("An associated constraint was violated")]
@ -105,6 +104,9 @@ pub enum ErrorCode {
/// 2019 - A space constraint was violated
#[msg("A space constraint was violated")]
ConstraintSpace,
/// 2020 - A required account for the constraint is None
#[msg("A required account for the constraint is None")]
ConstraintAccountIsNone,
// Require
/// 2500 - A require expression was violated
@ -185,15 +187,13 @@ pub enum ErrorCode {
#[msg("The account was duplicated for more than one reallocation")]
AccountDuplicateReallocs,
// 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,
/// 4101 - You cannot/should not initialize the payer account as a program account
#[msg("You cannot/should not initialize the payer account as a program account")]
TryingToInitPayerAsProgramAccount = 4101,
// Deprecated
/// 5000 - The API being used is deprecated and should no longer be used

View File

@ -49,9 +49,7 @@ 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};
@ -182,8 +180,12 @@ pub trait ZeroCopy: Discriminator + Copy + Clone + Zeroable + Pod {}
/// `Sha256(<namespace>:<method_name>)[..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<u8>;
pub trait InstructionData: Discriminator + AnchorSerialize {
fn data(&self) -> Vec<u8> {
let mut d = Self::discriminator().to_vec();
d.append(&mut self.try_to_vec().expect("Should always serialize"));
d
}
}
/// An event that can be emitted via a Solana log. See [`emit!`](crate::prelude::emit) for an example.
@ -201,7 +203,10 @@ pub trait EventData: AnchorSerialize + Discriminator {
/// 8 byte unique identifier for a type.
pub trait Discriminator {
fn discriminator() -> [u8; 8];
const DISCRIMINATOR: [u8; 8];
fn discriminator() -> [u8; 8] {
Self::DISCRIMINATOR
}
}
/// Bump seed for program derived addresses.
@ -238,12 +243,12 @@ pub mod prelude {
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,
context::Context, context::CpiContext, declare_id, emit, err, error, event, program,
require, require_eq, require_gt, require_gte, require_keys_eq, require_keys_neq,
require_neq, solana_program::bpf_loader_upgradeable::UpgradeableLoaderState, source,
system_program::System, zero_copy, AccountDeserialize, AccountSerialize, Accounts,
AccountsExit, AnchorDeserialize, AnchorSerialize, Id, Key, Owner, ProgramData, Result,
ToAccountInfo, ToAccountInfos, ToAccountMetas,
AccountsClose, AccountsExit, AnchorDeserialize, AnchorSerialize, Id, Key, Owner,
ProgramData, Result, ToAccountInfo, ToAccountInfos, ToAccountMetas,
};
pub use anchor_attribute_error::*;
pub use borsh;
@ -268,7 +273,6 @@ pub mod prelude {
/// 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];
@ -284,17 +288,6 @@ pub mod __private {
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<u64>;
}
// Very experimental trait.
#[doc(hidden)]
pub trait ZeroCopyAccessor<Ty> {
@ -311,9 +304,6 @@ pub mod __private {
input.to_bytes()
}
}
#[doc(hidden)]
pub use crate::accounts::state::PROGRAM_STATE_SEED;
}
/// Ensures a condition is true, otherwise returns with the given error.

View File

@ -1,14 +1,15 @@
[package]
name = "anchor-syn"
version = "0.25.0"
version = "0.26.0"
authors = ["Serum Foundation <foundation@projectserum.com>"]
repository = "https://github.com/coral-xyz/anchor"
license = "Apache-2.0"
description = "Anchor syntax parsing and code generation tools"
rust-version = "1.56"
rust-version = "1.59"
edition = "2021"
[features]
allow-missing-optionals = []
init-if-needed = []
idl = []
hash = []

View File

@ -61,9 +61,16 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
} else {
quote!()
};
quote! {
#docs
pub #name: anchor_lang::solana_program::pubkey::Pubkey
if f.is_optional {
quote! {
#docs
pub #name: Option<anchor_lang::solana_program::pubkey::Pubkey>
}
} else {
quote! {
#docs
pub #name: anchor_lang::solana_program::pubkey::Pubkey
}
}
}
})
@ -93,8 +100,18 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
true => quote! { anchor_lang::solana_program::instruction::AccountMeta::new },
};
let name = &f.ident;
quote! {
account_metas.push(#meta(self.#name, #is_signer));
if f.is_optional {
quote! {
if let Some(#name) = &self.#name {
account_metas.push(#meta(*#name, #is_signer));
} else {
account_metas.push(anchor_lang::solana_program::instruction::AccountMeta::new_readonly(crate::ID, false));
}
}
} else {
quote! {
account_metas.push(#meta(self.#name, #is_signer));
}
}
}
})

View File

@ -62,9 +62,16 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
} else {
quote!()
};
quote! {
#docs
pub #name: anchor_lang::solana_program::account_info::AccountInfo<'info>
if f.is_optional {
quote! {
#docs
pub #name: Option<anchor_lang::solana_program::account_info::AccountInfo<'info>>
}
} else {
quote! {
#docs
pub #name: anchor_lang::solana_program::account_info::AccountInfo<'info>
}
}
}
})
@ -94,8 +101,18 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
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));
if f.is_optional {
quote! {
if let Some(#name) = &self.#name {
account_metas.push(#meta(anchor_lang::Key::key(#name), #is_signer));
} else {
account_metas.push(anchor_lang::solana_program::instruction::AccountMeta::new_readonly(crate::ID, false));
}
}
} else {
quote! {
account_metas.push(#meta(anchor_lang::Key::key(&self.#name), #is_signer));
}
}
}
})
@ -104,18 +121,10 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
let account_struct_infos: Vec<proc_macro2::TokenStream> = 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));
}
.map(|f: &AccountField| {
let name = &f.ident();
quote! {
account_infos.extend(anchor_lang::ToAccountInfos::to_account_infos(&self.#name));
}
})
.collect();

View File

@ -1,9 +1,11 @@
use crate::*;
use proc_macro2_diagnostics::SpanDiagnosticExt;
use quote::quote;
use std::collections::HashSet;
use syn::Expr;
pub fn generate(f: &Field) -> proc_macro2::TokenStream {
use crate::*;
pub fn generate(f: &Field, accs: &AccountsStruct) -> proc_macro2::TokenStream {
let constraints = linearize(&f.constraints);
let rent = constraints
@ -14,12 +16,41 @@ pub fn generate(f: &Field) -> proc_macro2::TokenStream {
let checks: Vec<proc_macro2::TokenStream> = constraints
.iter()
.map(|c| generate_constraint(f, c))
.map(|c| generate_constraint(f, c, accs))
.collect();
let mut all_checks = quote! {#(#checks)*};
// If the field is optional we do all the inner checks as if the account
// wasn't optional. If the account is init we also need to return an Option
// by wrapping the resulting value with Some or returning None if it doesn't exist.
if f.is_optional && !constraints.is_empty() {
let ident = &f.ident;
let ty_decl = f.ty_decl(false);
all_checks = match &constraints[0] {
Constraint::Init(_) | Constraint::Zeroed(_) => {
quote! {
let #ident: #ty_decl = if let Some(#ident) = #ident {
#all_checks
Some(#ident)
} else {
None
};
}
}
_ => {
quote! {
if let Some(#ident) = &#ident {
#all_checks
}
}
}
};
}
quote! {
#rent
#(#checks)*
#all_checks
}
}
@ -53,7 +84,6 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec<Constraint> {
rent_exempt,
seeds,
executable,
state,
close,
address,
associated_token,
@ -97,9 +127,6 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec<Constraint> {
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));
}
@ -115,12 +142,16 @@ pub fn linearize(c_group: &ConstraintGroup) -> Vec<Constraint> {
constraints
}
fn generate_constraint(f: &Field, c: &Constraint) -> proc_macro2::TokenStream {
fn generate_constraint(
f: &Field,
c: &Constraint,
accs: &AccountsStruct,
) -> proc_macro2::TokenStream {
match c {
Constraint::Init(c) => generate_constraint_init(f, c),
Constraint::Init(c) => generate_constraint_init(f, c, accs),
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::HasOne(c) => generate_constraint_has_one(f, c, accs),
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),
@ -128,13 +159,12 @@ fn generate_constraint(f: &Field, c: &Constraint) -> proc_macro2::TokenStream {
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::Close(c) => generate_constraint_close(f, c, accs),
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),
Constraint::Realloc(c) => generate_constraint_realloc(f, c),
Constraint::AssociatedToken(c) => generate_constraint_associated_token(f, c, accs),
Constraint::TokenAccount(c) => generate_constraint_token_account(f, c, accs),
Constraint::Mint(c) => generate_constraint_mint(f, c, accs),
Constraint::Realloc(c) => generate_constraint_realloc(f, c, accs),
}
}
@ -166,14 +196,18 @@ fn generate_constraint_address(f: &Field, c: &ConstraintAddress) -> proc_macro2:
}
}
pub fn generate_constraint_init(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::TokenStream {
generate_constraint_init_group(f, c)
pub fn generate_constraint_init(
f: &Field,
c: &ConstraintInitGroup,
accs: &AccountsStruct,
) -> proc_macro2::TokenStream {
generate_constraint_init_group(f, c, accs)
}
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 ty_decl = f.ty_decl(true);
let from_account_info = f.from_account_info(None, false);
quote! {
let #field: #ty_decl = {
@ -189,13 +223,22 @@ pub fn generate_constraint_zeroed(f: &Field, _c: &ConstraintZeroed) -> proc_macr
}
}
pub fn generate_constraint_close(f: &Field, c: &ConstraintClose) -> proc_macro2::TokenStream {
pub fn generate_constraint_close(
f: &Field,
c: &ConstraintClose,
accs: &AccountsStruct,
) -> proc_macro2::TokenStream {
let field = &f.ident;
let name_str = field.to_string();
let target = &c.sol_dest;
let target_optional_check =
OptionalCheckScope::new_with_field(accs, field).generate_check(target);
quote! {
if #field.key() == #target.key() {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintClose).with_account_name(#name_str));
{
#target_optional_check
if #field.key() == #target.key() {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintClose).with_account_name(#name_str));
}
}
}
}
@ -210,8 +253,12 @@ pub fn generate_constraint_mut(f: &Field, c: &ConstraintMut) -> proc_macro2::Tok
}
}
pub fn generate_constraint_has_one(f: &Field, c: &ConstraintHasOne) -> proc_macro2::TokenStream {
let target = c.join_target.clone();
pub fn generate_constraint_has_one(
f: &Field,
c: &ConstraintHasOne,
accs: &AccountsStruct,
) -> proc_macro2::TokenStream {
let target = &c.join_target;
let ident = &f.ident;
let field = match &f.ty {
Ty::Loader(_) => quote! {#ident.load()?},
@ -224,8 +271,12 @@ pub fn generate_constraint_has_one(f: &Field, c: &ConstraintHasOne) -> proc_macr
quote! { ConstraintHasOne },
&Some(&(quote! { my_key }, quote! { target_key })),
);
let target_optional_check =
OptionalCheckScope::new_with_field(accs, &field).generate_check(target);
quote! {
{
#target_optional_check
let my_key = #field.#target;
let target_key = #target.key();
if my_key != target_key {
@ -325,13 +376,22 @@ pub fn generate_constraint_rent_exempt(
}
}
fn generate_constraint_realloc(f: &Field, c: &ConstraintReallocGroup) -> proc_macro2::TokenStream {
fn generate_constraint_realloc(
f: &Field,
c: &ConstraintReallocGroup,
accs: &AccountsStruct,
) -> proc_macro2::TokenStream {
let field = &f.ident;
let account_name = field.to_string();
let new_space = &c.space;
let payer = &c.payer;
let zero = &c.zero;
let mut optional_check_scope = OptionalCheckScope::new_with_field(accs, field);
let payer_optional_check = optional_check_scope.generate_check(payer);
let system_program_optional_check =
optional_check_scope.generate_check(quote! {system_program});
quote! {
// Blocks duplicate account reallocs in a single instruction to prevent accidental account overwrites
// and to ensure the calculation of the change in bytes is based on account size at program entry
@ -349,7 +409,9 @@ fn generate_constraint_realloc(f: &Field, c: &ConstraintReallocGroup) -> proc_ma
.unwrap();
if __delta_space != 0 {
#payer_optional_check
if __delta_space > 0 {
#system_program_optional_check
if ::std::convert::TryInto::<usize>::try_into(__delta_space).unwrap() > anchor_lang::solana_program::entrypoint::MAX_PERMITTED_DATA_INCREASE {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::AccountReallocExceedsLimit).with_account_name(#account_name));
}
@ -378,10 +440,14 @@ fn generate_constraint_realloc(f: &Field, c: &ConstraintReallocGroup) -> proc_ma
}
}
fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_macro2::TokenStream {
fn generate_constraint_init_group(
f: &Field,
c: &ConstraintInitGroup,
accs: &AccountsStruct,
) -> proc_macro2::TokenStream {
let field = &f.ident;
let name_str = f.ident.to_string();
let ty_decl = f.ty_decl();
let ty_decl = f.ty_decl(true);
let if_needed = if c.if_needed {
quote! {true}
} else {
@ -389,13 +455,7 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
};
let space = &c.space;
// Payer for rent exemption.
let payer = {
let p = &c.payer;
quote! {
let payer = #p.to_account_info();
}
};
let payer = &c.payer;
// Convert from account info to account context wrapper type.
let from_account_info = f.from_account_info(Some(&c.kind), true);
@ -417,6 +477,36 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
quote! { #seeds, }
});
let validate_pda = {
// 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 find_pda variable.
if c.bump.is_some() {
let b = c.bump.as_ref().unwrap();
quote! {
if #field.key() != __pda_address {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_pubkeys((#field.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)));
}
}
} else {
// 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 find_pda variable.
quote! {
if #field.key() != __pda_address {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintSeeds).with_account_name(#name_str).with_pubkeys((#field.key(), __pda_address)));
}
}
}
};
(
quote! {
let (__pda_address, __bump) = Pubkey::find_program_address(
@ -424,6 +514,7 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
program_id,
);
__bumps.insert(#name_str.to_string(), __bump);
#validate_pda
},
quote! {
&[
@ -435,36 +526,63 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
}
};
// Optional check idents
let system_program = &quote! {system_program};
let token_program = &quote! {token_program};
let associated_token_program = &quote! {associated_token_program};
let rent = &quote! {rent};
let mut check_scope = OptionalCheckScope::new_with_field(accs, field);
match &c.kind {
InitKind::Token { owner, mint } => {
let owner_optional_check = check_scope.generate_check(owner);
let mint_optional_check = check_scope.generate_check(mint);
let system_program_optional_check = check_scope.generate_check(system_program);
let token_program_optional_check = check_scope.generate_check(token_program);
let rent_optional_check = check_scope.generate_check(rent);
let optional_checks = quote! {
#system_program_optional_check
#token_program_optional_check
#rent_optional_check
#owner_optional_check
#mint_optional_check
};
let payer_optional_check = check_scope.generate_check(payer);
let create_account = generate_create_account(
field,
quote! {anchor_spl::token::TokenAccount::LEN},
quote! {&token_program.key()},
quote! {#payer},
seeds_with_bump,
);
quote! {
// Define the bump and pda variable.
#find_pda
let #field: #ty_decl = {
// Checks that all the required accounts for this operation are present.
#optional_checks
if !#if_needed || AsRef::<AccountInfo>::as_ref(&#field).owner == &anchor_lang::solana_program::system_program::ID {
// Define payer variable.
#payer
#payer_optional_check
// 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 {
let accounts = anchor_spl::token::InitializeAccount3 {
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)?;
anchor_spl::token::initialize_account3(cpi_ctx)?;
}
let pa: #ty_decl = #from_account_info_unchecked;
@ -481,23 +599,45 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
}
}
InitKind::AssociatedToken { owner, mint } => {
let owner_optional_check = check_scope.generate_check(owner);
let mint_optional_check = check_scope.generate_check(mint);
let system_program_optional_check = check_scope.generate_check(system_program);
let token_program_optional_check = check_scope.generate_check(token_program);
let associated_token_program_optional_check =
check_scope.generate_check(associated_token_program);
let rent_optional_check = check_scope.generate_check(rent);
let optional_checks = quote! {
#system_program_optional_check
#token_program_optional_check
#associated_token_program_optional_check
#rent_optional_check
#owner_optional_check
#mint_optional_check
};
let payer_optional_check = check_scope.generate_check(payer);
quote! {
// Define the bump and pda variable.
#find_pda
let #field: #ty_decl = {
// Checks that all the required accounts for this operation are present.
#optional_checks
if !#if_needed || AsRef::<AccountInfo>::as_ref(&#field).owner == &anchor_lang::solana_program::system_program::ID {
#payer
#payer_optional_check
let cpi_program = associated_token_program.to_account_info();
let cpi_accounts = anchor_spl::associated_token::Create {
payer: payer.to_account_info(),
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)?;
@ -524,36 +664,61 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
decimals,
freeze_authority,
} => {
let owner_optional_check = check_scope.generate_check(owner);
let freeze_authority_optional_check = match freeze_authority {
Some(fa) => check_scope.generate_check(fa),
None => quote! {},
};
let system_program_optional_check = check_scope.generate_check(system_program);
let token_program_optional_check = check_scope.generate_check(token_program);
let rent_optional_check = check_scope.generate_check(rent);
let optional_checks = quote! {
#system_program_optional_check
#token_program_optional_check
#rent_optional_check
#owner_optional_check
#freeze_authority_optional_check
};
let payer_optional_check = check_scope.generate_check(payer);
let create_account = generate_create_account(
field,
quote! {anchor_spl::token::Mint::LEN},
quote! {&token_program.key()},
quote! {#payer},
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 = {
// Checks that all the required accounts for this operation are present.
#optional_checks
if !#if_needed || AsRef::<AccountInfo>::as_ref(&#field).owner == &anchor_lang::solana_program::system_program::ID {
// Define payer variable.
#payer
#payer_optional_check
// 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 {
let accounts = anchor_spl::token::InitializeMint2 {
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)?;
anchor_spl::token::initialize_mint2(cpi_ctx, #decimals, &#owner.key(), #freeze_authority)?;
}
let pa: #ty_decl = #from_account_info_unchecked;
if #if_needed {
@ -578,20 +743,45 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
// Define the space variable.
let space = quote! {let space = #space;};
let system_program_optional_check = check_scope.generate_check(system_program);
// 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
},
let (owner, owner_optional_check) = match owner {
None => (
quote! {
program_id
},
quote! {},
),
Some(o) => {
// We clone the `check_scope` here to avoid collisions with the
// `payer_optional_check`, which is in a separate scope
let owner_optional_check = check_scope.clone().generate_check(o);
(
quote! {
&#o
},
owner_optional_check,
)
}
};
let payer_optional_check = check_scope.generate_check(payer);
let optional_checks = quote! {
#system_program_optional_check
};
// CPI to the system program to create the account.
let create_account =
generate_create_account(field, quote! {space}, owner.clone(), seeds_with_bump);
let create_account = generate_create_account(
field,
quote! {space},
owner.clone(),
quote! {#payer},
seeds_with_bump,
);
// Put it all together.
quote! {
@ -599,6 +789,9 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
#find_pda
let #field = {
// Checks that all the required accounts for this operation are present.
#optional_checks
let actual_field = #field.to_account_info();
let actual_owner = actual_field.owner;
@ -608,8 +801,7 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
// 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
#payer_optional_check
// CPI to the system program to create.
#create_account
@ -623,6 +815,7 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
// Assert the account was created correctly.
if #if_needed {
#owner_optional_check
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())));
}
@ -648,59 +841,36 @@ fn generate_constraint_init_group(f: &Field, c: &ConstraintInitGroup) -> proc_ma
}
fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2::TokenStream {
let name = &f.ident;
let name_str = name.to_string();
if c.is_init {
// Note that for `#[account(init, seeds)]`, the seed generation and checks is checked in
// the init constraint find_pda/validate_pda block, so we don't do anything here and
// return nothing!
quote! {}
} else {
let name = &f.ident;
let name_str = name.to_string();
let s = &mut c.seeds.clone();
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 });
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)));
}
// 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());
}
}
// 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, }
});
// Not init here, so do all the checks.
let define_pda = match c.bump.as_ref() {
// Bump target not given. Find it.
None => quote! {
@ -733,13 +903,25 @@ fn generate_constraint_seeds(f: &Field, c: &ConstraintSeedsGroup) -> proc_macro2
fn generate_constraint_associated_token(
f: &Field,
c: &ConstraintAssociatedToken,
accs: &AccountsStruct,
) -> 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;
let mut optional_check_scope = OptionalCheckScope::new_with_field(accs, name);
let wallet_address_optional_check = optional_check_scope.generate_check(wallet_address);
let spl_token_mint_address_optional_check =
optional_check_scope.generate_check(spl_token_mint_address);
let optional_checks = quote! {
#wallet_address_optional_check
#spl_token_mint_address_optional_check
};
quote! {
{
#optional_checks
let my_owner = #name.owner;
let wallet_address = #wallet_address.key();
if my_owner != wallet_address {
@ -757,27 +939,43 @@ fn generate_constraint_associated_token(
fn generate_constraint_token_account(
f: &Field,
c: &ConstraintTokenAccountGroup,
accs: &AccountsStruct,
) -> proc_macro2::TokenStream {
let name = &f.ident;
let mut optional_check_scope = OptionalCheckScope::new_with_field(accs, name);
let authority_check = match &c.authority {
Some(authority) => {
quote! { if #name.owner != #authority.key() { return Err(anchor_lang::error::ErrorCode::ConstraintTokenOwner.into()); } }
let authority_optional_check = optional_check_scope.generate_check(authority);
quote! {
#authority_optional_check
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()); } }
let mint_optional_check = optional_check_scope.generate_check(mint);
quote! {
#mint_optional_check
if #name.mint != #mint.key() { return Err(anchor_lang::error::ErrorCode::ConstraintTokenMint.into()); }
}
}
None => quote! {},
};
quote! {
#authority_check
#mint_check
{
#authority_check
#mint_check
}
}
}
fn generate_constraint_mint(f: &Field, c: &ConstraintTokenMintGroup) -> proc_macro2::TokenStream {
fn generate_constraint_mint(
f: &Field,
c: &ConstraintTokenMintGroup,
accs: &AccountsStruct,
) -> proc_macro2::TokenStream {
let name = &f.ident;
let decimal_check = match &c.decimals {
@ -788,26 +986,77 @@ fn generate_constraint_mint(f: &Field, c: &ConstraintTokenMintGroup) -> proc_mac
},
None => quote! {},
};
let mut optional_check_scope = OptionalCheckScope::new_with_field(accs, name);
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());
Some(mint_authority) => {
let mint_authority_optional_check = optional_check_scope.generate_check(mint_authority);
quote! {
#mint_authority_optional_check
if #name.mint_authority != anchor_lang::solana_program::program_option::COption::Some(#mint_authority.key()) {
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());
Some(freeze_authority) => {
let freeze_authority_optional_check =
optional_check_scope.generate_check(freeze_authority);
quote! {
#freeze_authority_optional_check
if #name.freeze_authority != anchor_lang::solana_program::program_option::COption::Some(#freeze_authority.key()) {
return Err(anchor_lang::error::ErrorCode::ConstraintMintFreezeAuthority.into());
}
}
},
}
None => quote! {},
};
quote! {
#decimal_check
#mint_authority_check
#freeze_authority_check
{
#decimal_check
#mint_authority_check
#freeze_authority_check
}
}
}
#[derive(Clone, Debug)]
pub struct OptionalCheckScope<'a> {
seen: HashSet<String>,
accounts: &'a AccountsStruct,
}
impl<'a> OptionalCheckScope<'a> {
pub fn new(accounts: &'a AccountsStruct) -> Self {
Self {
seen: HashSet::new(),
accounts,
}
}
pub fn new_with_field(accounts: &'a AccountsStruct, field: impl ToString) -> Self {
let mut check_scope = Self::new(accounts);
check_scope.seen.insert(field.to_string());
check_scope
}
pub fn generate_check(&mut self, field: impl ToTokens) -> TokenStream {
let field_name = tts_to_string(&field);
if self.seen.contains(&field_name) {
quote! {}
} else {
self.seen.insert(field_name.clone());
if self.accounts.is_field_optional(&field) {
quote! {
let #field = if let Some(ref account) = #field {
account
} else {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintAccountIsNone).with_account_name(#field_name));
};
}
} else {
quote! {}
}
}
}
}
@ -816,12 +1065,16 @@ fn generate_constraint_mint(f: &Field, c: &ConstraintTokenMintGroup) -> proc_mac
//
// `seeds_with_nonce` should be given for creating PDAs. Otherwise it's an
// empty stream.
pub fn generate_create_account(
//
// This should only be run within scopes where `system_program` is not Optional
fn generate_create_account(
field: &Ident,
space: proc_macro2::TokenStream,
owner: proc_macro2::TokenStream,
payer: proc_macro2::TokenStream,
seeds_with_nonce: proc_macro2::TokenStream,
) -> proc_macro2::TokenStream {
// Field, payer, and system program are already validated to not be an Option at this point
quote! {
// If the account being initialized already has lamports, then
// return them all back to the payer so that the account has
@ -832,12 +1085,13 @@ pub fn generate_create_account(
// 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(),
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 {
require_keys_neq!(#payer.key(), #field.key(), anchor_lang::error::ErrorCode::TryingToInitPayerAsProgramAccount);
// Fund the account for rent exemption.
let required_lamports = __anchor_rent
.minimum_balance(#space)
@ -845,7 +1099,7 @@ pub fn generate_create_account(
.saturating_sub(__current_lamports);
if required_lamports > 0 {
let cpi_accounts = anchor_lang::system_program::Transfer {
from: payer.to_account_info(),
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);
@ -873,6 +1127,9 @@ pub fn generate_constraint_executable(
) -> proc_macro2::TokenStream {
let name = &f.ident;
let name_str = name.to_string();
// because we are only acting on the field, we know it isnt optional at this point
// as it was unwrapped in `generate_constraint`
quote! {
if !#name.to_account_info().executable {
return Err(anchor_lang::error::Error::from(anchor_lang::error::ErrorCode::ConstraintExecutable).with_account_name(#name_str));
@ -880,26 +1137,6 @@ pub fn generate_constraint_executable(
}
}
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::<AccountInfo>::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<Expr>,

View File

@ -1,3 +1,4 @@
use crate::accounts_codegen::constraints::OptionalCheckScope;
use crate::codegen::accounts::{generics, ParsedGenerics};
use crate::{AccountField, AccountsStruct};
use quote::quote;
@ -29,11 +30,18 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
let name_str = ident.to_string();
if f.constraints.is_close() {
let close_target = &f.constraints.close.as_ref().unwrap().sol_dest;
let close_target_optional_check =
OptionalCheckScope::new(accs).generate_check(close_target);
quote! {
anchor_lang::AccountsClose::close(
&self.#ident,
self.#close_target.to_account_info(),
).map_err(|e| e.with_account_name(#name_str))?;
{
let #close_target = &self.#close_target;
#close_target_optional_check
anchor_lang::AccountsClose::close(
&self.#ident,
#close_target.to_account_info(),
).map_err(|e| e.with_account_name(#name_str))?;
}
}
} else {
match f.constraints.is_mutable() {

View File

@ -16,13 +16,8 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
.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());
}
let name = &f.ident();
quote! { account_infos.extend(self.#name.to_account_infos()); }
})
.collect();
quote! {

View File

@ -9,18 +9,28 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
.fields
.iter()
.map(|f: &AccountField| {
let (name, is_signer) = match f {
AccountField::CompositeField(s) => (&s.ident, quote! {None}),
let (name, is_signer, is_optional) = match f {
AccountField::CompositeField(s) => (&s.ident, quote! {None}, false),
AccountField::Field(f) => {
let is_signer = match f.constraints.is_signer() {
false => quote! {None},
true => quote! {Some(true)},
};
(&f.ident, is_signer)
(&f.ident, is_signer, f.is_optional)
}
};
quote! {
account_metas.extend(self.#name.to_account_metas(#is_signer));
if is_optional {
quote! {
if let Some(#name) = &self.#name {
account_metas.extend(#name.to_account_metas(#is_signer));
} else {
account_metas.push(AccountMeta::new_readonly(crate::ID, false));
}
}
} else {
quote! {
account_metas.extend(self.#name.to_account_metas(#is_signer));
}
}
})
.collect();

View File

@ -32,14 +32,38 @@ pub fn generate(accs: &AccountsStruct) -> proc_macro2::TokenStream {
// `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() {
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());
// Optional accounts have slightly different behavior here and
// we can't leverage the try_accounts implementation for zero and init.
if f.is_optional {
// Thus, this block essentially reimplements the try_accounts
// behavior with optional accounts minus the deserialziation.
let empty_behavior = if cfg!(feature = "allow-missing-optionals") {
quote!{ None }
} else {
quote!{ return Err(anchor_lang::error::ErrorCode::AccountNotEnoughKeys.into()); }
};
quote! {
let #name = if accounts.is_empty() {
#empty_behavior
} else if accounts[0].key == program_id {
*accounts = &accounts[1..];
None
} else {
let account = &accounts[0];
*accounts = &accounts[1..];
Some(account)
};
}
} else {
quote!{
if accounts.is_empty() {
return Err(anchor_lang::error::ErrorCode::AccountNotEnoughKeys.into());
}
let #name = &accounts[0];
*accounts = &accounts[1..];
}
let #name = &accounts[0];
*accounts = &accounts[1..];
}
} else {
let name = f.ident.to_string();
@ -129,14 +153,14 @@ pub fn generate_constraints(accs: &AccountsStruct) -> proc_macro2::TokenStream {
true => Some(f),
},
})
.map(constraints::generate)
.map(|f| constraints::generate(f, accs))
.collect();
// Constraint checks for each account fields.
let access_checks: Vec<proc_macro2::TokenStream> = non_init_fields
.iter()
.map(|af: &&AccountField| match af {
AccountField::Field(f) => constraints::generate(f),
AccountField::Field(f) => constraints::generate(f, accs),
AccountField::CompositeField(s) => constraints::generate_composite(s),
})
.collect();

View File

@ -5,30 +5,6 @@ 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;

View File

@ -1,11 +1,7 @@
use crate::parser;
use crate::{IxArg, State};
use crate::IxArg;
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";
@ -22,10 +18,6 @@ pub fn sighash(namespace: &str, name: &str) -> [u8; 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 = {
@ -45,39 +37,3 @@ pub fn generate_ix_variant(name: String, args: &[IxArg]) -> proc_macro2::TokenSt
}
}
}
pub fn generate_ctor_args(state: &State) -> Vec<syn::Pat> {
generate_ctor_typed_args(state)
.iter()
.map(|pat_ty| *pat_ty.pat.clone())
.collect()
}
pub fn generate_ctor_typed_args(state: &State) -> Vec<syn::PatType> {
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()
}

View File

@ -1,61 +1,9 @@
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<proc_macro2::TokenStream> = 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<proc_macro2::TokenStream> = program
.ixs
@ -123,11 +71,6 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
use super::*;
use std::marker::PhantomData;
pub mod state {
use super::*;
#(#state_cpi_methods)*
}
pub struct Return<T> {
phantom: std::marker::PhantomData<T>
@ -150,30 +93,6 @@ pub fn generate(program: &Program) -> proc_macro2::TokenStream {
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;

Some files were not shown because too many files have changed in this diff Show More