From b97e53bb942220ca2c2127c3b7cb6d543b8c5947 Mon Sep 17 00:00:00 2001 From: Fernando Otero Date: Wed, 25 Jan 2023 23:34:50 +0000 Subject: [PATCH] Add new programmable asset class (#947) * wip: added new instructions * Add mint test * wip: add transfer test * wip: add sysvar to account list * wip: add master edition account to mint * wip: fix sysvar instructions account * update ix * wip: add sale delegate test * fix shank annotations * fix update and add migrate * fix migrate * fix tests * wip: add revoke sale delegate test * add guide * wip: mint processor * bump deps, add rules program * stuff sorta works * wip: mint processor (to be renamed to create_metadata) * Rename mint instruction to create_metadata * merge create/mint changes * transfer: standard assets * refactor transfer processor to new pattern * wip: minting non-fungible assets * Rename create_metadata instruction to create * Improve code comments * Freezing token account after mint * Add more tests * Add create_and_mint helper * wip: add sale delegate creation * transfer programmable nft succeeds * Add JS tests * merge changes * refactor transfer to be cleaner & check programmable_config * add new Metadata fields to custom metadata-deserializer * fix DelegateRecord name; js test imports * add DelegateRecord * fix clippy lint * remove redundant delegate seeds; fix cargo check lints * add account checks to transfer * Add test suffix * Improve account validation * Tweak instruction builder parameters * Refactor delegate instruction * Add revoke processor implementation * refactor update; add update JS tests * bump Solita version and fix update js tests * add more update tests * clean up * fix update BPF test * add more update JS tests * fix transfer account order and set up JS tests * add more transfer tests * add auth rules back to transfer * add Cargo lock file * Remove duplicated imports * Renamed sale delegate to transfer * refactor for auth rules update; simplify Rust bpf tests * Add revoke tests * Fix clippy * Rename sale to transfer * finish delegate transfer test * refactor for mpl-auth-rules update * Tidy up the code and added more tests * Add authorization rules to mint instruction * fix clippy lints; add local Operation enum; refactor transfer accounts * refactor 'update' for account indices * update transfer fn for new auth rules changes * add payload type locally; update JS * update to token-auth-rules 0.1.6 and fix JS tests * comment out migrate test * add auth rules structure to update; add more JS tests * finish update tests * (js/tests): fix various things; add mpl-token-auth-rules program to amman conf * clean up * refactor: remove authorization data from valid auth assert * refactor: change operation to string; update tests * bump mpl-token-auth-rules to v0.1.7 * finish update handler & JS tests * Add delegate test * Token standard as required field * Formatting * Add delegate details to DelegateState struct * formatting * Add sale delegate * Add delegate state deserialization * Add transfer and sale delegate tests * Add transfer and sale delegate revoke * Add revoke JS tests * Add accounts context (#919) * Add accounts context * Formatting * Add macro to create account instruction helpers * Updated create instruction to use the macro generated helpers * Tweak macro version * Add support to generic types on args * Formatting * Adding full feature to syn. * Filling out cargo metadata * Adding readme. Co-authored-by: blockiosaurus * finish transfer logic and write basic BPF tests * refactor Transfer to use AccountsContext * refactor Update to use AccountsContext * regenerate JS api, fix test helper functions * refactor Mint to use AccountsContext * update Mint bpf tests * refactor Delegate to use AccountsContext * refactor Revoke to use AccountsContext; update tests * Remove authorization rules account from instruction * Tweak account list * Tweak account list for Update * Tweak account list for delegate/revoke * Bump token auth rules version * Rename as_context to to_context * Tweak accounts and logic on transfer * wip: transfer tests * Update metadata-deserializer.ts * Update edition account name * Fix rule set serialization in transfer test * test/js: add transfer programmable program-owned * test/js add more transfer tests * transfer: refactor validate auth rules to be generic; add test * add more tests * add migrate handler skeleton and test * add a couple more migrate tests * Minting to existing token account * Add log message * Add more create and mint tests * set programmable config in migrate * bump to mpl-token-auth-rules 0.2.1 * fix migrate to check update auth * Add optional token owner account to mint instruction * Add instruction builder example and links (#927) * Add instruction builder example and links * Fix code indentation * Fix comments * Add link to token authorization rules program (#929) * change ProgrammableConfig to triple opt; fix migrate config setting (#928) * change ProgrammableConfig to triple opt; fix migrate config setting * remove old ruleset file * Add Toggle data type for optional fields Co-authored-by: febo * Add optional ATA initialization * Delegate refactoring * Fix data enums with new Solita generation * Add state flag to programmable config (#934) * wip: programmable state * Refactored instructions and tests * Renamed programmable state * Fix warnings * Split instruction match (#936) * Add authorization args * Add comments to processor * Check authority type on instruction * Add update and utility delegate * More delegate tests * Bump solana dependency * Fix dependencies version * Add programmable token standard to master edition (#937) * Add programmable token standard byte to master edition * Improve account serialization * Add token standard assert * Add asset state to metadata (#943) * Add asset state to metadata * Update tests * Updated JS generated code * Update JS tests * Revert module name change * Add assert function * Refactored * Add delegate lock tests * Support for fungible assets * Refactored utility into lock and unlock * Refactored utility tests into lock and unlock * Check the delegate type on assert * More tests * Moved programmable state to pda * Add mint and delegate to account * Fix legacy instruction test * Fix authority type check * Update JS tests (#946) * refactor transfer (#944) * refactor transfer * add rooster program for transfer tests * update Cargo lock * refactor transfer logic * address review comments * fix clippy lint * add sale delegate test * fix non-pNFT tests * fix things; add new_token_record to transfer * fix delegate auth test * fix sale delegate test * add utility delegate test * PR review fixes * reorganize new handlers; disable migrate * finish name changes for token_records * Updated auto-generated JS * Fix JS lint * Updated token auth rules dependency * Fix import path * Fix another import path * Linting * fix tests * Skip sdk tests * add versions to deps * chore: Release * chore: Release mpl-utils version 0.0.6 * bump mpl-utils version * add lock file * add symbol stripping * lint * Adding slot advancement to prevent local test timeouts. * chore: Release * chore: update @metaplex-foundation/mpl-token-metadata to v2.6.0-alpha.0 * chore: update @metaplex-foundation/mpl-token-metadata to v2.6.0-alpha.1 * Clippy * Instructions breakdown and optional account details (#945) * Instructions breakdown and optional account details This PR adds a list of operations for each instruction with an indication of whether they are completed or not. It also includes details of how optional positional accounts are used, which is important for users creating instructions without using the provided SDK/Rust crate. * Add update and utility operations * Typos * Add link to cretes and npm * Formatting * Change font size * More formatting * Improve markdown * Using circles * Using small circles * Squares * Check boxes * Improve list * More lists * Add missing use instruction * Move token state to fix byte offset * Update auth rules dependency * Typos * Fix create with minted mint account * Remove token record from account list * reorder processor match statement; add unsupported error burn and verify * refactor wallet-to-wallet to be more robust (#950) * chore: Release * Bump program version * chore: update @metaplex-foundation/mpl-token-metadata to v2.6.0-alpha.2 * Updated library links * Update transfer JS tests (#952) * Add custom errors * Update JS tests * Rename to match name convention * Avoid multiple deserialization * Check token status on processor (#953) * Check token status on processor * Disabled holder lock/unlock * Add missing signer * Update tests to include delegate * Add token owner account * Update JS tests * Token account is required * Update JS tests * Improve locked test * Add update with locked token test * Fix assert on error * Updated auth rules dependency * Add length check on edition byte test (#960) * fix bug in transfer when no rule set present on NFT (#961) * Refactor token state (#963) * Update authority signer account * Update update authority to PDA * Checking for optional previous authority * Add listed token state * Bump auth rules version * Update JS tests * Removed unused optional account * Add migration delegate support * Remove utility transfer tests * Add migration enum discriminator * Simplified token standard check * Add token owner as delegate test * Update JS auto-generated code * Update ProgrammableNFTGuide.md (#957) * Update ProgrammableNFTGuide.md * Update ProgrammableNFTGuide.md * Update ProgrammableNFTGuide.md * Update ProgrammableNFTGuide.md * chore: Release * chore: update @metaplex-foundation/mpl-token-metadata to v2.6.0-alpha.3 * fix delegate table in programmable guide * box builders and remove clones to save stack space (#958) * Pin auth rules version * Bump token auth rules version * Remove pinned version * implement nft -> pnft migration case (#956) * implement nft -> pnft migration case * write token standard to end of edition account * fix bugs * fix token freeze logic * remove collection member check, it's in mpl-migration-validator; set Migration delegate instead of Utility * Store rule set revision on delegate (#964) * Bump mpl-token-auth-rules version * Storing rule set revision * Soring rule set revision on delegate * Fix token record account size * Update BPF tests * Revision as u64 * Update tests * Add revision comment * rebuild lock file to fix dep issues * chore: Release * chore: Release * chore: update @metaplex-foundation/mpl-token-metadata to v2.7.0-beta.0 * chore: update @metaplex-foundation/mpl-token-metadata to v2.7.0-beta.1 * Token address on token record seed (#965) * add ser/deser implementations for pubkeys (#968) * Add enum to represent the print supply (#969) * Add enum to represent the print supply * Update JS tests * Typo * Typo in error * Add test for non-fungoble editions * Update print supply message * Move test for non-fungible editions * clippy fixes and one import fix (#970) * No default for optional accounts (#971) * No default for optional accounts * WIP: update tests * Add missing optional accounts to tests * Improve errors on instructions (#972) * WIP: more JS tests * Merge branch 'feat/programmable-asset' into febo/js-tests * Update JS tests * Refactor/remove deprecated fn (#973) * remove mint_new_edition_from_master_edition_via_vault_proxy * remove unused assertions * change token owner to token in migrate * Fix Cargo.lock for the bpf channel * chore: Release * chore: Release * chore: update @metaplex-foundation/mpl-token-metadata to v2.7.0-beta.2 * Add local setup instruction (#966) * WIP: add token record information * Add local setup information * Add syntax highlight * Add yarn build command * Formatting * Add transfer test with lookup table (#975) * Add transfer test with lookup table * Linting * Clone required args in instruction builder macro (#974) * Currently this doesn't work with Vec args because `ok_or` consumes `self` which results in a move and it cannot move out of a shared reference. * This should be low impact to stack size because the cloned() value is put in a `Box`. * chore: Release * Removed serde-feature from auth rules dependency * Remove unused update authority (#977) * Remove unused update authority * Updated JS tests * Bump auth rules version (#978) * Update lock file * run nightly cargo fmt * update CI solana version Co-authored-by: Sammy <41593264+stegaBOB@users.noreply.github.com> Co-authored-by: Nhan Phan Co-authored-by: Samuel Vanderwaal Co-authored-by: blockiosaurus Co-authored-by: Loris Leiva Co-authored-by: austbot Co-authored-by: Austin Adams Co-authored-by: Michael Danenberg <56533526+danenbm@users.noreply.github.com> --- .base-ammanrc.js | 5 + .github/workflows/program-reusable.yml | 2 +- .github/workflows/sdk.yml | 1 + core/rust/utils/Cargo.lock | 2 +- core/rust/utils/Cargo.toml | 4 +- core/rust/utils/src/token/cpi.rs | 47 + token-metadata/Cargo.lock | 1530 ++++---- token-metadata/Cargo.toml | 5 + token-metadata/cli/Cargo.toml | 15 +- token-metadata/js/.ammanrc.js | 13 +- token-metadata/js/.prettierrc.js | 1 - token-metadata/js/build-and-run-test.sh | 8 + token-metadata/js/idl/mpl_token_metadata.json | 3032 +++++++++++++-- token-metadata/js/package.json | 18 +- token-metadata/js/run-test.sh | 40 + .../js/src/custom/metadata-deserializer.ts | 15 +- .../js/src/generated/accounts/Metadata.ts | 6 + .../accounts/MetadataDelegateRecord.ts | 175 + .../js/src/generated/accounts/TokenRecord.ts | 172 + .../js/src/generated/accounts/index.ts | 6 + .../js/src/generated/errors/index.ts | 770 +++- .../js/src/generated/instructions/Burn.ts | 142 + .../js/src/generated/instructions/Create.ts | 141 + .../js/src/generated/instructions/Delegate.ts | 176 + .../js/src/generated/instructions/Lock.ts | 169 + .../js/src/generated/instructions/Migrate.ts | 183 + .../js/src/generated/instructions/Mint.ts | 183 + .../js/src/generated/instructions/Revoke.ts | 176 + .../js/src/generated/instructions/Transfer.ts | 197 + .../js/src/generated/instructions/Unlock.ts | 169 + .../js/src/generated/instructions/Update.ts | 155 + .../js/src/generated/instructions/Use.ts | 162 + .../js/src/generated/instructions/Utilize.ts | 1 - .../js/src/generated/instructions/Verify.ts | 114 + .../js/src/generated/instructions/index.ts | 12 + .../js/src/generated/types/AssetData.ts | 51 + .../js/src/generated/types/AuthorityType.ts | 27 + .../src/generated/types/AuthorizationData.ts | 21 + .../js/src/generated/types/BurnArgs.ts | 50 + .../src/generated/types/CollectionDetails.ts | 2 +- .../types/CollectionDetailsToggle.ts | 62 + .../src/generated/types/CollectionToggle.ts | 62 + .../js/src/generated/types/CreateArgs.ts | 60 + .../js/src/generated/types/DelegateArgs.ts | 135 + .../js/src/generated/types/EscrowAuthority.ts | 2 +- token-metadata/js/src/generated/types/Key.ts | 2 + .../js/src/generated/types/LeafInfo.ts | 24 + .../js/src/generated/types/LockArgs.ts | 50 + .../generated/types/MetadataDelegateRole.ts | 26 + .../js/src/generated/types/MigrateArgs.ts | 56 + .../js/src/generated/types/MigrationType.ts | 25 + .../js/src/generated/types/MintArgs.ts | 53 + .../js/src/generated/types/Payload.ts | 22 + .../js/src/generated/types/PayloadKey.ts | 27 + .../js/src/generated/types/PayloadType.ts | 85 + .../js/src/generated/types/PrintSupply.ts | 59 + .../src/generated/types/ProgrammableConfig.ts | 53 + .../js/src/generated/types/RevokeArgs.ts | 30 + .../js/src/generated/types/RuleSetToggle.ts | 60 + .../js/src/generated/types/SeedsVec.ts | 20 + .../src/generated/types/TokenDelegateRole.ts | 29 + .../js/src/generated/types/TokenStandard.ts | 1 + .../js/src/generated/types/TokenState.ts | 26 + .../js/src/generated/types/TransferArgs.ts | 54 + .../js/src/generated/types/UnlockArgs.ts | 51 + .../js/src/generated/types/UpdateArgs.ts | 78 + .../js/src/generated/types/UseArgs.ts | 50 + .../js/src/generated/types/UsesToggle.ts | 59 + .../js/src/generated/types/VerifyArgs.ts | 51 + .../js/src/generated/types/index.ts | 30 + token-metadata/js/test/create.test.ts | 819 +++++ token-metadata/js/test/delegate.test.ts | 521 +++ ...a-deserializer.ts => deserializer.test.ts} | 0 token-metadata/js/test/lock.test.ts | 617 ++++ token-metadata/js/test/mint.test.ts | 277 ++ token-metadata/js/test/revoke.test.ts | 289 ++ token-metadata/js/test/setup/amman.ts | 11 + token-metadata/js/test/setup/index.ts | 14 + token-metadata/js/test/setup/log.ts | 5 + token-metadata/js/test/setup/lut.ts | 88 + token-metadata/js/test/setup/txs-init.ts | 785 ++++ token-metadata/js/test/transfer.test.ts | 1202 ++++++ token-metadata/js/test/unlock.test.ts | 399 ++ token-metadata/js/test/update.test.ts | 1424 ++++++++ .../js/test/utils/digital-asset-manager.ts | 133 + token-metadata/js/test/utils/errors.ts | 4 + token-metadata/js/test/utils/index.ts | 61 + token-metadata/js/test/utils/programmable.ts | 33 + .../js/test/utils/update-test-data.ts | 33 + token-metadata/js/tsconfig.build.json | 2 +- token-metadata/js/yarn.lock | 882 ++++- token-metadata/macro/Cargo.toml | 16 + token-metadata/macro/README.md | 1 + token-metadata/macro/src/lib.rs | 514 +++ token-metadata/program/Cargo.lock | 3253 ----------------- token-metadata/program/Cargo.toml | 25 +- .../program/ProgrammableNFTGuide.md | 451 +++ .../program/src/assertions/edition.rs | 40 +- .../program/src/assertions/metadata.rs | 67 +- token-metadata/program/src/assertions/misc.rs | 33 +- token-metadata/program/src/assertions/mod.rs | 1 + .../program/src/assertions/programmable.rs | 31 + token-metadata/program/src/error.rs | 147 +- .../program/src/instruction/burn.rs | 16 +- .../program/src/instruction/collection.rs | 12 +- .../program/src/instruction/delegate.rs | 198 + .../program/src/instruction/metadata.rs | 536 ++- token-metadata/program/src/instruction/mod.rs | 268 +- .../program/src/instruction/scratchpad.rs | 146 + .../program/src/instruction/state.rs | 139 + .../program/src/instruction/uses.rs | 12 +- token-metadata/program/src/pda.rs | 34 + .../program/src/processor/burn/burn.rs | 11 + .../program/src/processor/burn/burn_nft.rs | 6 +- .../program/src/processor/burn/mod.rs | 3 + .../program/src/processor/collection/mod.rs | 2 + .../collection/revoke_collection_authority.rs | 2 +- .../src/processor/collection/verify.rs | 11 + .../src/processor/delegate/delegate.rs | 386 ++ .../program/src/processor/delegate/mod.rs | 6 + .../program/src/processor/delegate/revoke.rs | 312 ++ ...ion_from_master_edition_via_vault_proxy.rs | 130 - .../program/src/processor/deprecated/mod.rs | 2 - .../processor/escrow/create_escrow_account.rs | 2 +- .../src/processor/escrow/transfer_out.rs | 3 +- .../freeze/freeze_delegated_account.rs | 6 +- .../freeze/thaw_delegated_account.rs | 6 +- .../program/src/processor/metadata/create.rs | 208 ++ .../program/src/processor/metadata/migrate.rs | 227 ++ .../program/src/processor/metadata/mint.rs | 286 ++ .../program/src/processor/metadata/mod.rs | 10 + .../src/processor/metadata/transfer.rs | 391 ++ .../program/src/processor/metadata/update.rs | 186 + token-metadata/program/src/processor/mod.rs | 238 +- .../program/src/processor/state/lock.rs | 36 + .../program/src/processor/state/mod.rs | 267 ++ .../program/src/processor/state/unlock.rs | 36 + .../processor/uses/approve_use_authority.rs | 5 +- .../program/src/state/asset_data.rs | 83 + .../program/src/state/collection.rs | 1 + token-metadata/program/src/state/delegate.rs | 55 + token-metadata/program/src/state/edition.rs | 1 + .../program/src/state/edition_marker.rs | 5 +- .../program/src/state/master_edition.rs | 11 +- token-metadata/program/src/state/metadata.rs | 214 +- token-metadata/program/src/state/migrate.rs | 9 + token-metadata/program/src/state/mod.rs | 53 +- .../program/src/state/programmable.rs | 317 ++ .../program/src/state/token_auth_payload.rs | 48 + .../program/src/utils/compression.rs | 1 - .../program/src/utils/master_edition.rs | 101 +- token-metadata/program/src/utils/metadata.rs | 31 +- token-metadata/program/src/utils/mod.rs | 46 +- .../program/src/utils/programmable_asset.rs | 321 ++ .../program/tests/burn_edition_nft.rs | 2 + token-metadata/program/tests/create.rs | 450 +++ .../program/tests/create_metadata_account.rs | 3 +- token-metadata/program/tests/delegate.rs | 396 ++ token-metadata/program/tests/escrow.rs | 2 + token-metadata/program/tests/lock.rs | 298 ++ token-metadata/program/tests/migrate.rs | 151 + token-metadata/program/tests/mint.rs | 164 + ...w_edition_from_master_edition_via_token.rs | 14 +- .../tests/process_legacy_instruction.rs | 195 + token-metadata/program/tests/revoke.rs | 426 +++ token-metadata/program/tests/transfer.rs | 983 +++++ token-metadata/program/tests/unlock.rs | 166 + token-metadata/program/tests/update.rs | 164 + token-metadata/program/tests/utils/assert.rs | 27 + .../program/tests/utils/digital_asset.rs | 720 ++++ .../program/tests/utils/edition_marker.rs | 1 + .../program/tests/utils/external_price.rs | 88 - .../program/tests/utils/metadata.rs | 43 +- token-metadata/program/tests/utils/mod.rs | 20 +- .../program/tests/utils/programmable.rs | 175 + .../program/tests/utils/rooster_manager.rs | 130 + token-metadata/program/tests/utils/vault.rs | 229 -- yarn.lock | 1967 ++++++++++ 178 files changed, 28872 insertions(+), 4896 deletions(-) create mode 100755 token-metadata/js/build-and-run-test.sh create mode 100755 token-metadata/js/run-test.sh create mode 100644 token-metadata/js/src/generated/accounts/MetadataDelegateRecord.ts create mode 100644 token-metadata/js/src/generated/accounts/TokenRecord.ts create mode 100644 token-metadata/js/src/generated/instructions/Burn.ts create mode 100644 token-metadata/js/src/generated/instructions/Create.ts create mode 100644 token-metadata/js/src/generated/instructions/Delegate.ts create mode 100644 token-metadata/js/src/generated/instructions/Lock.ts create mode 100644 token-metadata/js/src/generated/instructions/Migrate.ts create mode 100644 token-metadata/js/src/generated/instructions/Mint.ts create mode 100644 token-metadata/js/src/generated/instructions/Revoke.ts create mode 100644 token-metadata/js/src/generated/instructions/Transfer.ts create mode 100644 token-metadata/js/src/generated/instructions/Unlock.ts create mode 100644 token-metadata/js/src/generated/instructions/Update.ts create mode 100644 token-metadata/js/src/generated/instructions/Use.ts create mode 100644 token-metadata/js/src/generated/instructions/Verify.ts create mode 100644 token-metadata/js/src/generated/types/AssetData.ts create mode 100644 token-metadata/js/src/generated/types/AuthorityType.ts create mode 100644 token-metadata/js/src/generated/types/AuthorizationData.ts create mode 100644 token-metadata/js/src/generated/types/BurnArgs.ts create mode 100644 token-metadata/js/src/generated/types/CollectionDetailsToggle.ts create mode 100644 token-metadata/js/src/generated/types/CollectionToggle.ts create mode 100644 token-metadata/js/src/generated/types/CreateArgs.ts create mode 100644 token-metadata/js/src/generated/types/DelegateArgs.ts create mode 100644 token-metadata/js/src/generated/types/LeafInfo.ts create mode 100644 token-metadata/js/src/generated/types/LockArgs.ts create mode 100644 token-metadata/js/src/generated/types/MetadataDelegateRole.ts create mode 100644 token-metadata/js/src/generated/types/MigrateArgs.ts create mode 100644 token-metadata/js/src/generated/types/MigrationType.ts create mode 100644 token-metadata/js/src/generated/types/MintArgs.ts create mode 100644 token-metadata/js/src/generated/types/Payload.ts create mode 100644 token-metadata/js/src/generated/types/PayloadKey.ts create mode 100644 token-metadata/js/src/generated/types/PayloadType.ts create mode 100644 token-metadata/js/src/generated/types/PrintSupply.ts create mode 100644 token-metadata/js/src/generated/types/ProgrammableConfig.ts create mode 100644 token-metadata/js/src/generated/types/RevokeArgs.ts create mode 100644 token-metadata/js/src/generated/types/RuleSetToggle.ts create mode 100644 token-metadata/js/src/generated/types/SeedsVec.ts create mode 100644 token-metadata/js/src/generated/types/TokenDelegateRole.ts create mode 100644 token-metadata/js/src/generated/types/TokenState.ts create mode 100644 token-metadata/js/src/generated/types/TransferArgs.ts create mode 100644 token-metadata/js/src/generated/types/UnlockArgs.ts create mode 100644 token-metadata/js/src/generated/types/UpdateArgs.ts create mode 100644 token-metadata/js/src/generated/types/UseArgs.ts create mode 100644 token-metadata/js/src/generated/types/UsesToggle.ts create mode 100644 token-metadata/js/src/generated/types/VerifyArgs.ts create mode 100644 token-metadata/js/test/create.test.ts create mode 100644 token-metadata/js/test/delegate.test.ts rename token-metadata/js/test/{token-metadata-deserializer.ts => deserializer.test.ts} (100%) create mode 100644 token-metadata/js/test/lock.test.ts create mode 100644 token-metadata/js/test/mint.test.ts create mode 100644 token-metadata/js/test/revoke.test.ts create mode 100644 token-metadata/js/test/setup/amman.ts create mode 100644 token-metadata/js/test/setup/index.ts create mode 100644 token-metadata/js/test/setup/log.ts create mode 100644 token-metadata/js/test/setup/lut.ts create mode 100644 token-metadata/js/test/setup/txs-init.ts create mode 100644 token-metadata/js/test/transfer.test.ts create mode 100644 token-metadata/js/test/unlock.test.ts create mode 100644 token-metadata/js/test/update.test.ts create mode 100644 token-metadata/js/test/utils/digital-asset-manager.ts create mode 100644 token-metadata/js/test/utils/errors.ts create mode 100644 token-metadata/js/test/utils/index.ts create mode 100644 token-metadata/js/test/utils/programmable.ts create mode 100644 token-metadata/js/test/utils/update-test-data.ts create mode 100644 token-metadata/macro/Cargo.toml create mode 100644 token-metadata/macro/README.md create mode 100644 token-metadata/macro/src/lib.rs delete mode 100644 token-metadata/program/Cargo.lock create mode 100644 token-metadata/program/ProgrammableNFTGuide.md create mode 100644 token-metadata/program/src/assertions/programmable.rs create mode 100644 token-metadata/program/src/instruction/delegate.rs create mode 100644 token-metadata/program/src/instruction/scratchpad.rs create mode 100644 token-metadata/program/src/instruction/state.rs create mode 100644 token-metadata/program/src/processor/burn/burn.rs create mode 100644 token-metadata/program/src/processor/collection/verify.rs create mode 100644 token-metadata/program/src/processor/delegate/delegate.rs create mode 100644 token-metadata/program/src/processor/delegate/mod.rs create mode 100644 token-metadata/program/src/processor/delegate/revoke.rs delete mode 100644 token-metadata/program/src/processor/deprecated/mint_new_edition_from_master_edition_via_vault_proxy.rs create mode 100644 token-metadata/program/src/processor/metadata/create.rs create mode 100644 token-metadata/program/src/processor/metadata/migrate.rs create mode 100644 token-metadata/program/src/processor/metadata/mint.rs create mode 100644 token-metadata/program/src/processor/metadata/transfer.rs create mode 100644 token-metadata/program/src/processor/metadata/update.rs create mode 100644 token-metadata/program/src/processor/state/lock.rs create mode 100644 token-metadata/program/src/processor/state/mod.rs create mode 100644 token-metadata/program/src/processor/state/unlock.rs create mode 100644 token-metadata/program/src/state/asset_data.rs create mode 100644 token-metadata/program/src/state/delegate.rs create mode 100644 token-metadata/program/src/state/migrate.rs create mode 100644 token-metadata/program/src/state/programmable.rs create mode 100644 token-metadata/program/src/state/token_auth_payload.rs create mode 100644 token-metadata/program/src/utils/programmable_asset.rs create mode 100644 token-metadata/program/tests/create.rs create mode 100644 token-metadata/program/tests/delegate.rs create mode 100644 token-metadata/program/tests/lock.rs create mode 100644 token-metadata/program/tests/migrate.rs create mode 100644 token-metadata/program/tests/mint.rs create mode 100644 token-metadata/program/tests/process_legacy_instruction.rs create mode 100644 token-metadata/program/tests/revoke.rs create mode 100644 token-metadata/program/tests/transfer.rs create mode 100644 token-metadata/program/tests/unlock.rs create mode 100644 token-metadata/program/tests/update.rs create mode 100644 token-metadata/program/tests/utils/digital_asset.rs delete mode 100644 token-metadata/program/tests/utils/external_price.rs create mode 100644 token-metadata/program/tests/utils/programmable.rs create mode 100644 token-metadata/program/tests/utils/rooster_manager.rs delete mode 100644 token-metadata/program/tests/utils/vault.rs create mode 100644 yarn.lock diff --git a/.base-ammanrc.js b/.base-ammanrc.js index 64c7d5b89d..f80d8e648d 100644 --- a/.base-ammanrc.js +++ b/.base-ammanrc.js @@ -35,6 +35,11 @@ const programs = { programId: 'CndyV3LdqHUfDLmE5naZjVN8rBZz4tqhdefbAnjHG3JR', deployPath: localDeployPath('mpl_candy_machine_core'), }, + token_auth_rules: { + label: 'Token Auth Rules', + programId: 'auth9SigNpDKz4sJJ1DfCTuZrZNSAgh9sFD3rboVmgg', + deployPath: localDeployPath('mpl_token_auth_rules'), + }, }; const validator = { diff --git a/.github/workflows/program-reusable.yml b/.github/workflows/program-reusable.yml index 185040154a..9cbc7fb0ce 100644 --- a/.github/workflows/program-reusable.yml +++ b/.github/workflows/program-reusable.yml @@ -15,7 +15,7 @@ on: env: CARGO_TERM_COLOR: always - SOLANA_VERSION: 1.10.40 + SOLANA_VERSION: 1.14.13 RUST_TOOLCHAIN: stable CACHE: ${{ inputs.cache }} diff --git a/.github/workflows/sdk.yml b/.github/workflows/sdk.yml index ec6a6c2e78..71af4cb580 100644 --- a/.github/workflows/sdk.yml +++ b/.github/workflows/sdk.yml @@ -37,6 +37,7 @@ jobs: skip_test: true - name: "token-entangler" - name: "token-metadata" + skip_test: true uses: ./.github/workflows/sdk-reusable.yml with: diff --git a/core/rust/utils/Cargo.lock b/core/rust/utils/Cargo.lock index 6bed4ac4ab..abcebbca3e 100644 --- a/core/rust/utils/Cargo.lock +++ b/core/rust/utils/Cargo.lock @@ -609,7 +609,7 @@ dependencies = [ [[package]] name = "mpl-utils" -version = "0.0.5" +version = "0.0.6" dependencies = [ "arrayref", "borsh", diff --git a/core/rust/utils/Cargo.toml b/core/rust/utils/Cargo.toml index 2b642641b6..7f940a97df 100644 --- a/core/rust/utils/Cargo.toml +++ b/core/rust/utils/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mpl-utils" -version = "0.0.5" +version = "0.0.6" edition = "2021" description = "MPL Core Utils library" authors = ["Metaplex Developers "] @@ -14,7 +14,7 @@ crate-type = ["lib", "cdylib"] path = "src/lib.rs" [dependencies] -solana-program = "1.10" +solana-program = "1.13.5" borsh = "0.9.3" arrayref = "0.3.6" spl-token = { version = "3.2.0", features = ["no-entrypoint"], optional = true } diff --git a/core/rust/utils/src/token/cpi.rs b/core/rust/utils/src/token/cpi.rs index 4713f553ca..0eca720cf2 100644 --- a/core/rust/utils/src/token/cpi.rs +++ b/core/rust/utils/src/token/cpi.rs @@ -126,3 +126,50 @@ pub struct TokenMintToParams<'a: 'b, 'b> { /// token_program pub token_program: AccountInfo<'a>, } + +pub fn spl_token_transfer(params: TokenTransferParams<'_, '_>) -> ProgramResult { + let TokenTransferParams { + mint: _, + source, + destination, + amount, + authority, + token_program, + authority_signer_seeds, + } = params; + let mut seeds: Vec<&[&[u8]]> = vec![]; + if let Some(seed) = authority_signer_seeds { + seeds.push(seed); + } + invoke_signed( + &spl_token::instruction::transfer( + token_program.key, + source.key, + destination.key, + authority.key, + &[authority.key], + amount, + )?, + &[source, destination, authority], + seeds.as_slice(), + ) +} + +/// TokenTransferParams +#[derive(Debug)] +pub struct TokenTransferParams<'a: 'b, 'b> { + /// mint + pub mint: AccountInfo<'a>, + /// source + pub source: AccountInfo<'a>, + /// destination + pub destination: AccountInfo<'a>, + /// amount + pub amount: u64, + /// authority + pub authority: AccountInfo<'a>, + /// authority_signer_seeds + pub authority_signer_seeds: Option<&'b [&'b [u8]]>, + /// token_program + pub token_program: AccountInfo<'a>, +} diff --git a/token-metadata/Cargo.lock b/token-metadata/Cargo.lock index 7095efe54d..6af36dda59 100644 --- a/token-metadata/Cargo.lock +++ b/token-metadata/Cargo.lock @@ -60,16 +60,16 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.8", "once_cell", "version_check", ] [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] @@ -82,24 +82,24 @@ checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" [[package]] name = "alloc-no-stdlib" -version = "2.0.3" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35ef4730490ad1c4eae5c4325b2a95f521d023e5c885853ff7aca0a6a1631db3" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" [[package]] name = "alloc-stdlib" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697ed7edc0f1711de49ce108c541623a0af97c6c60b2f6e2b65229847ac843c2" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" dependencies = [ "alloc-no-stdlib", ] [[package]] name = "android_system_properties" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7ed72e1635e121ca3e79420540282af22da58be50de153d36f81ddc6b83aa9e" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] @@ -115,9 +115,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.61" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "508b352bb5c066aac251f6daf6b36eccd03e8a88e8081cd44959ea277a3af9a8" +checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" [[package]] name = "arrayref" @@ -150,7 +150,7 @@ dependencies = [ "num-traits", "rusticata-macros", "thiserror", - "time 0.3.13", + "time 0.3.17", ] [[package]] @@ -159,9 +159,9 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726535892e8eae7e70657b4c8ea93d26b8553afb1ce617caee529ef96d7dee6c" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", "synstructure", ] @@ -171,9 +171,9 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2777730b2039ac0f95f093556e61b6d26cebed5393ca6f152717777cec3a42ed" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -184,9 +184,9 @@ checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "async-compression" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345fd392ab01f746c717b1357165b76f0b67a60192007b234058c9045fdcf695" +checksum = "942c7cd7ae39e91bde4820d74132e9862e62c2f386c3aa90ccf55949f5bad63a" dependencies = [ "brotli", "flate2", @@ -207,13 +207,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.57" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" +checksum = "eff18d764974428cf3a9328e23fc5c986f5fbed46e6cd4cdf42544df5d297ec1" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -222,7 +222,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -241,15 +241,21 @@ checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" [[package]] name = "base64" -version = "0.13.0" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + +[[package]] +name = "base64" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" [[package]] name = "base64ct" -version = "1.5.1" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bdca834647821e0b13d9539a8634eb62d3501b6b6c2cec1722786ee6671b851" +checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf" [[package]] name = "bincode" @@ -277,16 +283,16 @@ dependencies = [ [[package]] name = "blake3" -version = "1.3.1" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08e53fc5a564bb15bfe6fae56bd71522205f1f91893f9c0116edad6496c183f" +checksum = "42ae2468a89544a466886840aa467a25b766499f4f04bf7d9fcd10ecee9fccef" dependencies = [ "arrayref", "arrayvec", "cc", "cfg-if", "constant_time_eq", - "digest 0.10.3", + "digest 0.10.6", ] [[package]] @@ -301,9 +307,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" dependencies = [ "generic-array", ] @@ -333,8 +339,8 @@ dependencies = [ "borsh-derive-internal", "borsh-schema-derive-internal", "proc-macro-crate 0.1.5", - "proc-macro2 1.0.43", - "syn 1.0.99", + "proc-macro2 1.0.50", + "syn 1.0.107", ] [[package]] @@ -343,9 +349,9 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5449c28a7b352f2d1e592a8a28bf139bc71afb0764a14f3c02500935d8c44065" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -354,9 +360,9 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdbd5696d8bfa21d53d9fe39a714a18538bad11492a42d066dbbc395fb1951c0" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -372,9 +378,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "2.3.2" +version = "2.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ad2d4653bf5ca36ae797b1f4bb4dbddb60ce49ca4aed8a2ce4829f60425b80" +checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -388,9 +394,9 @@ checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" [[package]] name = "bumpalo" -version = "3.10.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "bv" @@ -404,22 +410,22 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.11.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5377c8865e74a160d21f29c2d40669f53286db6eab59b88540cbb12ffc8b835" +checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.1.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfd2f4180c5721da6335cc9e9061cce522b87a35e51cc57636d28d22a9863c80" +checksum = "1aca418a974d83d40a0c1f0c5cba6ff4bc28d8df099109ca459a2118d40b6322" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -430,15 +436,15 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" [[package]] name = "bzip2" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" dependencies = [ "bzip2-sys", "libc", @@ -457,9 +463,9 @@ dependencies = [ [[package]] name = "caps" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938c50180feacea622ef3b8f4a496057c868dcf8ac7a64d781dd8f3f51a9c143" +checksum = "190baaad529bcfbde9e1a19022c42781bdb6ff9de25721abdb8fd98c0807730b" dependencies = [ "libc", "thiserror", @@ -467,9 +473,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.73" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" dependencies = [ "jobserver", ] @@ -482,25 +488,25 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" dependencies = [ "iana-time-zone", "js-sys", "num-integer", "num-traits", "serde", - "time 0.1.44", + "time 0.1.45", "wasm-bindgen", "winapi", ] [[package]] name = "chrono-humanize" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eddc119501d583fd930cb92144e605f44e0252c38dd89d9247fffa1993375cb" +checksum = "32dce1ea1988dbdf9f9815ff11425828523bd2a134ec0805d2ac8af26ee6096e" dependencies = [ "chrono", ] @@ -541,9 +547,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.17" +version = "3.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29e724a68d9319343bb3328c9cc2dfde263f4b3142ee1059a9980580171c954b" +checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" dependencies = [ "atty", "bitflags", @@ -552,7 +558,7 @@ dependencies = [ "once_cell", "strsim 0.10.0", "termcolor", - "textwrap 0.15.0", + "textwrap 0.16.0", ] [[package]] @@ -564,6 +570,16 @@ dependencies = [ "os_str_bytes", ] +[[package]] +name = "codespan-reporting" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +dependencies = [ + "termcolor", + "unicode-width", +] + [[package]] name = "combine" version = "3.8.1" @@ -579,16 +595,15 @@ dependencies = [ [[package]] name = "console" -version = "0.15.1" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89eab4d20ce20cea182308bca13088fecea9c05f6776cf287205d41a0ed3c847" +checksum = "c3d79fbe8970a77e3e34151cc13d3b3e248aa0faaecb9f6091fa07ebefe5ad60" dependencies = [ "encode_unicode", + "lazy_static", "libc", - "once_cell", - "terminal_size", "unicode-width", - "winapi", + "windows-sys", ] [[package]] @@ -619,9 +634,9 @@ checksum = "e4c78c047431fee22c1a7bb92e00ad095a02a983affe4d8a72e2a2c62c1b94f3" [[package]] name = "constant_time_eq" -version = "0.1.5" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +checksum = "f3ad85c1f65dc7b37604eb0e89748faf0b9653065f2a8ef69f96a687ec1e9279" [[package]] name = "core-foundation" @@ -641,9 +656,9 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" [[package]] name = "cpufeatures" -version = "0.2.2" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" dependencies = [ "libc", ] @@ -680,26 +695,24 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.10" +version = "0.9.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "045ebe27666471bb549370b4b0b3e51b07f56325befa4284db65fc89c02511b1" +checksum = "01a9af1f4c2ef74bb8aa1f7e19706bc72d03598c8a570bb5de72243c7a9d9d5a" dependencies = [ "autocfg", "cfg-if", "crossbeam-utils", - "memoffset", - "once_cell", + "memoffset 0.7.1", "scopeguard", ] [[package]] name = "crossbeam-utils" -version = "0.8.11" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" dependencies = [ "cfg-if", - "once_cell", ] [[package]] @@ -751,6 +764,50 @@ dependencies = [ "zeroize", ] +[[package]] +name = "cxx" +version = "1.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b61a7545f753a88bcbe0a70de1fcc0221e10bfc752f576754fa91e663db1622e" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f464457d494b5ed6905c63b0c4704842aba319084a0a3561cdc1359536b53200" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2 1.0.50", + "quote 1.0.23", + "scratch", + "syn 1.0.107", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43c7119ce3a3701ed81aca8410b9acf6fc399d2629d057b87e2efa4e63a3aaea" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.87" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65e07508b90551e610910fa648a1878991d367064997a596135b86df30daf07e" +dependencies = [ + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", +] + [[package]] name = "darling" version = "0.13.4" @@ -769,10 +826,10 @@ checksum = "859d65a907b6852c9361e3185c862aae7fafd2887876799fa55f5f99dc40d610" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.43", - "quote 1.0.21", + "proc-macro2 1.0.50", + "quote 1.0.23", "strsim 0.10.0", - "syn 1.0.99", + "syn 1.0.107", ] [[package]] @@ -782,8 +839,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ "darling_core", - "quote 1.0.21", - "syn 1.0.99", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -799,9 +856,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" +checksum = "23d8666cb01533c39dde32bcbab8e227b4ed6679b2c925eba05feabea39508fb" [[package]] name = "der" @@ -834,11 +891,12 @@ checksum = "6e5c37193a1db1d8ed868c03ec7b152175f26160a5b740e5e484143877e0adf0" [[package]] name = "dialoguer" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92e7e37ecef6857fdc0c0c5d42fd5b0938e46590c2183cc92dd310a6d078eb1" +checksum = "af3c796f3b0b408d9fd581611b47fa850821fcb84aa640b83a3c1a5be2d691f2" dependencies = [ "console", + "shell-words", "tempfile", "zeroize", ] @@ -854,11 +912,11 @@ dependencies = [ [[package]] name = "digest" -version = "0.10.3" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ - "block-buffer 0.10.2", + "block-buffer 0.10.3", "crypto-common", "subtle", ] @@ -899,9 +957,9 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -935,9 +993,9 @@ checksum = "abe71d579d1812060163dff96056261deb5bf6729b100fa2e36a68b9649ba3d3" [[package]] name = "ed25519" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e9c280362032ea4203659fc489832d0204ef09f247a0506f170dafcac08c369" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" dependencies = [ "signature", ] @@ -965,26 +1023,26 @@ dependencies = [ "derivation-path", "ed25519-dalek", "hmac 0.12.1", - "sha2 0.10.2", + "sha2 0.10.6", ] [[package]] name = "educe" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07b7cc9cd8c08d10db74fca3b20949b9b6199725c04a0cce6d543496098fcac" +checksum = "cb0188e3c3ba8df5753894d54461f0e39bc91741dc5b22e1c46999ec2c71f4e4" dependencies = [ "enum-ordinalize", - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "either" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" +checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" [[package]] name = "encode_unicode" @@ -1016,42 +1074,42 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8958699f9359f0b04e691a13850d48b7de329138023876d07cbd024c2c820598" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "enum-ordinalize" -version = "3.1.11" +version = "3.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2170fc0efee383079a8bdd05d6ea2a184d2a0f07a1c1dcabdb2fd5e9f24bc36c" +checksum = "a62bb1df8b45ecb7ffa78dca1c17a438fb193eb083db0b1b494d2a61bcb5096a" dependencies = [ "num-bigint 0.4.3", "num-traits", - "proc-macro2 1.0.43", - "quote 1.0.21", + "proc-macro2 1.0.50", + "quote 1.0.23", "rustc_version", - "syn 1.0.99", + "syn 1.0.107", ] [[package]] name = "enum_dispatch" -version = "0.3.8" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eb359f1476bf611266ac1f5355bc14aeca37b299d0ebccc038ee7058891c9cb" +checksum = "11f36e95862220b211a6e2aa5eca09b4fa391b13cd52ceb8035a24bf65a79de2" dependencies = [ "once_cell", - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "env_logger" -version = "0.9.0" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" dependencies = [ "atty", "humantime", @@ -1083,9 +1141,9 @@ checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" [[package]] name = "filetime" -version = "0.2.17" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94a7bbaa59354bc20dd75b67f23e2797b4490e9d6928203fb105c79e448c86c" +checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9" dependencies = [ "cfg-if", "libc", @@ -1095,9 +1153,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" dependencies = [ "crc32fast", "miniz_oxide", @@ -1111,19 +1169,18 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" dependencies = [ - "matches", "percent-encoding", ] [[package]] name = "futures" -version = "0.3.21" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0" dependencies = [ "futures-channel", "futures-core", @@ -1136,9 +1193,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.21" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" dependencies = [ "futures-core", "futures-sink", @@ -1146,15 +1203,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.21" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" [[package]] name = "futures-executor" -version = "0.3.21" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2" dependencies = [ "futures-core", "futures-task", @@ -1163,38 +1220,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.21" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" +checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" [[package]] name = "futures-macro" -version = "0.3.21" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "futures-sink" -version = "0.3.21" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" +checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" [[package]] name = "futures-task" -version = "0.3.21" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" [[package]] name = "futures-util" -version = "0.3.21" +version = "0.3.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" dependencies = [ "futures-channel", "futures-core", @@ -1253,9 +1310,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", "js-sys", @@ -1266,9 +1323,9 @@ dependencies = [ [[package]] name = "goblin" -version = "0.5.3" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91766b1121940d622933a13e20665857648681816089c9bc2075c4b75a6e4f6b" +checksum = "a7666983ed0dd8d21a6f6576ee00053ca0926fb281a5522577a4dbd0f1b54143" dependencies = [ "log", "plain", @@ -1277,9 +1334,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" dependencies = [ "bytes", "fnv", @@ -1336,6 +1393,15 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "histogram" version = "0.6.9" @@ -1358,7 +1424,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest 0.10.3", + "digest 0.10.6", ] [[package]] @@ -1396,9 +1462,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" @@ -1414,9 +1480,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.20" +version = "0.14.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" dependencies = [ "bytes", "futures-channel", @@ -1438,9 +1504,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.23.0" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" +checksum = "1788965e61b367cd03a62950836d5cd41560c3577d90e40e0819373194d1661c" dependencies = [ "http", "hyper", @@ -1451,17 +1517,28 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.44" +version = "0.1.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf7d67cf4a22adc5be66e75ebdf769b3f2ea032041437a7061f97a63dad4b" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" dependencies = [ "android_system_properties", "core-foundation-sys", + "iana-time-zone-haiku", "js-sys", "wasm-bindgen", "winapi", ] +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1470,11 +1547,10 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] @@ -1486,7 +1562,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0acd33ff0285af998aaf9b57342af478078f53492322fafc47450e09397e0e9" dependencies = [ "bitmaps", - "rand_core 0.6.3", + "rand_core 0.6.4", "rand_xoshiro", "rayon", "serde", @@ -1503,9 +1579,9 @@ checksum = "5a9d968042a4902e08810946fc7cd5851eb75e80301342305af755ca06cb82ce" [[package]] name = "indexmap" -version = "1.9.1" +version = "1.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", "hashbrown 0.12.3", @@ -1543,39 +1619,39 @@ dependencies = [ [[package]] name = "ipnet" -version = "2.5.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "879d54834c8c76457ef4293a689b2a8c59b076067ad77b15efafbb05f92a592b" +checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146" [[package]] name = "itertools" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "jobserver" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" dependencies = [ "libc", ] [[package]] name = "js-sys" -version = "0.3.59" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2" +checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" dependencies = [ "wasm-bindgen", ] @@ -1597,9 +1673,12 @@ dependencies = [ [[package]] name = "keccak" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9b7d56ba4a8344d6be9729995e6b06f928af29998cdf79fe390cbf6b1fee838" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] [[package]] name = "lazy_static" @@ -1609,15 +1688,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.131" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04c3b4822ccebfa39c02fc03d1534441b22ead323fa0f48bb7ddd8e6ba076a40" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libloading" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" dependencies = [ "cfg-if", "winapi", @@ -1671,6 +1750,15 @@ dependencies = [ "libsecp256k1-core", ] +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -1679,9 +1767,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ "autocfg", "scopeguard", @@ -1696,11 +1784,20 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "lru" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999beba7b6e8345721bd280141ed958096a2e4abdf74f67ff4ce49b4b54e47a" +dependencies = [ + "hashbrown 0.12.3", +] + [[package]] name = "lz4" -version = "1.23.3" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4edcb94251b1c375c459e5abe9fb0168c1c826c3370172684844f8f3f8d1a885" +checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1" dependencies = [ "libc", "lz4-sys", @@ -1708,20 +1805,14 @@ dependencies = [ [[package]] name = "lz4-sys" -version = "1.9.3" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7be8908e2ed6f31c02db8a9fa962f03e36c53fbfde437363eae3306b85d7e17" +checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" dependencies = [ "cc", "libc", ] -[[package]] -name = "matches" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - [[package]] name = "memchr" version = "2.5.0" @@ -1730,9 +1821,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memmap2" -version = "0.5.6" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2e4455be2010e8c5e77f0d10234b30f3a636a5305725609b5a71ad00d22577" +checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" dependencies = [ "libc", ] @@ -1746,6 +1837,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" +dependencies = [ + "autocfg", +] + [[package]] name = "merlin" version = "3.0.0" @@ -1754,7 +1854,7 @@ checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" dependencies = [ "byteorder", "keccak", - "rand_core 0.6.3", + "rand_core 0.6.4", "zeroize", ] @@ -1765,7 +1865,7 @@ dependencies = [ "bincode", "borsh", "clap 2.34.0", - "mpl-token-metadata", + "mpl-token-metadata 1.7.0-beta.2", "solana-clap-utils", "solana-cli-config", "solana-client", @@ -1789,9 +1889,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.5.3" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" dependencies = [ "adler", ] @@ -1834,24 +1934,45 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a7d5f7076603ebc68de2dc6a650ec331a062a13abaa346975be747bbfa4b789" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "mpl-token-auth-rules" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a14e1ac5350734fd07f17d7eab733d27b7b45ca963642c565ec34ab015d9ceda" +dependencies = [ + "borsh", + "mpl-token-metadata-context-derive 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num-derive", + "num-traits", + "rmp-serde", + "serde", + "shank 0.0.11", + "solana-program", + "solana-zk-token-sdk", + "thiserror", ] [[package]] name = "mpl-token-metadata" -version = "1.6.5" +version = "1.7.0-beta.2" dependencies = [ "arrayref", "borsh", - "mpl-token-vault", - "mpl-utils", + "mpl-token-auth-rules", + "mpl-token-metadata-context-derive 0.2.1", + "mpl-utils 0.0.6", "num-derive", "num-traits", + "rmp-serde", + "rooster", "serde", "serde_with", - "shank", + "shank 0.0.11", "solana-program", "solana-program-test", "solana-sdk", @@ -1861,19 +1982,51 @@ dependencies = [ ] [[package]] -name = "mpl-token-vault" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ade4ef15bc06a6033076c4ff28cba9b42521df5ec61211d6f419415ace2746a" +name = "mpl-token-metadata" +version = "1.7.0-beta.2" +source = "git+https://github.com/metaplex-foundation/metaplex-program-library?branch=feat/programmable-asset#329ef43e4bed9a1250599571e32194248070bc83" dependencies = [ + "arrayref", "borsh", + "mpl-token-auth-rules", + "mpl-token-metadata-context-derive 0.2.1 (git+https://github.com/metaplex-foundation/metaplex-program-library?branch=feat/programmable-asset)", + "mpl-utils 0.0.6 (git+https://github.com/metaplex-foundation/metaplex-program-library?branch=feat/programmable-asset)", "num-derive", "num-traits", + "shank 0.0.11", "solana-program", + "spl-associated-token-account", "spl-token", "thiserror", ] +[[package]] +name = "mpl-token-metadata-context-derive" +version = "0.2.1" +dependencies = [ + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "mpl-token-metadata-context-derive" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12989bc45715b0ee91944855130131479f9c772e198a910c3eb0ea327d5bffc3" +dependencies = [ + "quote 1.0.23", + "syn 1.0.107", +] + +[[package]] +name = "mpl-token-metadata-context-derive" +version = "0.2.1" +source = "git+https://github.com/metaplex-foundation/metaplex-program-library?branch=feat/programmable-asset#329ef43e4bed9a1250599571e32194248070bc83" +dependencies = [ + "quote 1.0.23", + "syn 1.0.107", +] + [[package]] name = "mpl-utils" version = "0.0.5" @@ -1886,28 +2039,58 @@ dependencies = [ "spl-token", ] +[[package]] +name = "mpl-utils" +version = "0.0.6" +dependencies = [ + "arrayref", + "borsh", + "solana-program", + "spl-token", +] + +[[package]] +name = "mpl-utils" +version = "0.0.6" +source = "git+https://github.com/metaplex-foundation/metaplex-program-library?branch=feat/programmable-asset#329ef43e4bed9a1250599571e32194248070bc83" +dependencies = [ + "arrayref", + "borsh", + "solana-program", + "spl-token", +] + [[package]] name = "nix" -version = "0.24.2" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" dependencies = [ "bitflags", "cfg-if", "libc", - "memoffset", + "memoffset 0.6.5", ] [[package]] name = "nom" -version = "7.1.1" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] +[[package]] +name = "nom8" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" +dependencies = [ + "memchr", +] + [[package]] name = "ntapi" version = "0.3.7" @@ -1969,9 +2152,9 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -2018,42 +2201,33 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.13.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" dependencies = [ - "hermit-abi", + "hermit-abi 0.2.6", "libc", ] [[package]] name = "num_enum" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +checksum = "8d829733185c1ca374f17e52b762f24f535ec625d2cc1f070e34c8a9068f341b" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +checksum = "2be1598bf1c313dcdd12092e3f1920f463462525a21b7b4e11b4168353d0123e" dependencies = [ - "proc-macro-crate 1.2.1", - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", -] - -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", + "proc-macro-crate 1.3.0", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -2064,18 +2238,18 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "oid-registry" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d4bda43fd1b844cbc6e6e54b5444e2b1bc7838bce59ad205902cccbb26d6761" +checksum = "9bedf36ffb6ba96c2eb7144ef6270557b52e54b20c0a8e1eb2ff99a6c6959bff" dependencies = [ "asn1-rs", ] [[package]] name = "once_cell" -version = "1.13.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "opaque-debug" @@ -2110,15 +2284,15 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.2.0" +version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "648001efe5d5c0102d8cea768e348da85d90af8ba91f0bea908f157951493cd4" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" [[package]] name = "ouroboros" -version = "0.15.2" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7425ea87a1e31df63a27b6d31e21a35a9003268032a876465e8d43c2364b0de2" +checksum = "dfbb50b356159620db6ac971c6d5c9ab788c9cc38a6f49619fca2a27acb062ca" dependencies = [ "aliasable", "ouroboros_macro", @@ -2126,15 +2300,15 @@ dependencies = [ [[package]] name = "ouroboros_macro" -version = "0.15.2" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "734aa7a4a6390b162112523cac2923a18e4f23b917880a68c826bf6e8bf48f06" +checksum = "4a0d9d1a6191c4f391f87219d1ea42b23f09ee84d64763cd05ee6ea88d9f384d" dependencies = [ "Inflector", "proc-macro-error", - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -2145,7 +2319,7 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" dependencies = [ "instant", "lock_api", - "parking_lot_core 0.8.5", + "parking_lot_core 0.8.6", ] [[package]] @@ -2155,14 +2329,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" dependencies = [ "lock_api", - "parking_lot_core 0.9.3", + "parking_lot_core 0.9.6", ] [[package]] name = "parking_lot_core" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" dependencies = [ "cfg-if", "instant", @@ -2174,9 +2348,9 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.3" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" dependencies = [ "cfg-if", "libc", @@ -2185,6 +2359,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "paste" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" + [[package]] name = "pbkdf2" version = "0.4.0" @@ -2200,23 +2380,23 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" dependencies = [ - "digest 0.10.3", + "digest 0.10.6", ] [[package]] name = "pem" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c64931a1a212348ec4f3b4362585eca7159d0d09cbdf4a7f74f02173596fd4" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", ] [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "percentage" @@ -2229,22 +2409,22 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78203e83c48cffbe01e4a2d35d566ca4de445d79a85372fc64e378bfc812a260" +checksum = "ad29a609b6bcd67fee905812e544992d216af9d755757c05ed2d0e15a74c6ecc" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "710faf75e1b33345361201d36d04e98ac1ed8909151a017ed384700836104c74" +checksum = "069bdb1e05adc7a8990dce9cc75370895fbe4e3d58b9b73bf1aee56359344a55" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -2272,9 +2452,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "plain" @@ -2296,9 +2476,9 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro-crate" @@ -2311,13 +2491,12 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34" dependencies = [ "once_cell", - "thiserror", - "toml", + "toml_edit", ] [[package]] @@ -2327,9 +2506,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" dependencies = [ "proc-macro-error-attr", - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", "version_check", ] @@ -2339,8 +2518,8 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", + "proc-macro2 1.0.50", + "quote 1.0.23", "version_check", ] @@ -2355,9 +2534,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.43" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" dependencies = [ "unicode-ident", ] @@ -2373,9 +2552,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21afdc492bf2a8688cb386be6605d1163b6ace89afa5e3b529037d2b4334b860" +checksum = "5b435e71d9bfa0d8889927231970c51fb89c58fa63bffcab117c9c7a41e5ef8f" dependencies = [ "bytes", "futures-channel", @@ -2412,9 +2591,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f832d8958db3e84d2ec93b5eb2272b45aa23cf7f8fe6e79f578896f4e6c231b" +checksum = "b07946277141531aea269befd949ed16b2c85a780ba1043244eda0969e538e54" dependencies = [ "futures-util", "libc", @@ -2435,11 +2614,11 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ - "proc-macro2 1.0.43", + "proc-macro2 1.0.50", ] [[package]] @@ -2463,7 +2642,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha 0.3.1", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -2483,7 +2662,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] @@ -2497,11 +2676,11 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.8", ] [[package]] @@ -2519,26 +2698,24 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" dependencies = [ - "rand_core 0.6.3", + "rand_core 0.6.4", ] [[package]] name = "rayon" -version = "1.5.3" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +checksum = "6db3a213adf02b3bcfd2d3846bb41cb22857d131789e01df434fb7e7bc0759b7" dependencies = [ - "autocfg", - "crossbeam-deque", "either", "rayon-core", ] [[package]] name = "rayon-core" -version = "1.9.3" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +checksum = "356a0625f1954f730c0201cdab48611198dc6ce21f4acff55089b5a78e6e835b" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -2554,7 +2731,7 @@ checksum = "6413f3de1edee53342e6138e75b56d32e7bc6e332b3bd62d497b1929d4cfbcdd" dependencies = [ "pem", "ring", - "time 0.3.13", + "time 0.3.17", "yasna", ] @@ -2573,16 +2750,16 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ - "getrandom 0.2.7", + "getrandom 0.2.8", "redox_syscall", "thiserror", ] [[package]] name = "regex" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", @@ -2591,9 +2768,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "remove_dir_all" @@ -2606,12 +2783,12 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.11" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" +checksum = "21eed90ec8570952d53b772ecf8f206aa1ec9a3d76b2521c56c42973f2d91ee9" dependencies = [ "async-compression", - "base64 0.13.0", + "base64 0.21.0", "bytes", "encoding_rs", "futures-core", @@ -2623,13 +2800,13 @@ dependencies = [ "hyper-rustls", "ipnet", "js-sys", - "lazy_static", "log", "mime", + "once_cell", "percent-encoding", "pin-project-lite", "rustls", - "rustls-pemfile 1.0.1", + "rustls-pemfile 1.0.2", "serde", "serde_json", "serde_urlencoded", @@ -2660,6 +2837,43 @@ dependencies = [ "winapi", ] +[[package]] +name = "rmp" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44519172358fd6d58656c86ab8e7fbc9e1490c3e8f14d35ed78ca0dd07403c9f" +dependencies = [ + "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5b13be192e0220b8afb7222aa5813cb62cc269ebb5cac346ca6487681d2913e" +dependencies = [ + "byteorder", + "rmp", + "serde", +] + +[[package]] +name = "rooster" +version = "0.1.0" +source = "git+https://github.com/metaplex-foundation/rooster#7033c7defe2cc475d40a70cb4ed4c868924e3be4" +dependencies = [ + "borsh", + "mpl-token-metadata 1.7.0-beta.2 (git+https://github.com/metaplex-foundation/metaplex-program-library?branch=feat/programmable-asset)", + "mpl-utils 0.0.5", + "num-derive", + "num-traits", + "shank 0.0.10", + "solana-program", + "thiserror", +] + [[package]] name = "rpassword" version = "6.0.1" @@ -2704,9 +2918,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.20.6" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ "log", "ring", @@ -2721,7 +2935,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0167bac7a9f490495f3c33013e7722b53cb087ecbe082fb0c6387c96f634ea50" dependencies = [ "openssl-probe", - "rustls-pemfile 1.0.1", + "rustls-pemfile 1.0.2", "schannel", "security-framework", ] @@ -2732,29 +2946,29 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", ] [[package]] name = "rustls-pemfile" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0864aeff53f8c05aa08d86e5ef839d3dfcf07aeba2db32f12db0ef716e87bd55" +checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" dependencies = [ - "base64 0.13.0", + "base64 0.21.0", ] [[package]] name = "rustversion" -version = "1.0.9" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" +checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70" [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "same-file" @@ -2767,11 +2981,10 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" +checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" dependencies = [ - "lazy_static", "windows-sys", ] @@ -2781,6 +2994,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scratch" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" + [[package]] name = "scroll" version = "0.11.0" @@ -2796,9 +3015,9 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdbda6ac5cd1321e724fa9cee216f3a61885889b896f073b8f82322789c5250e" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -2813,9 +3032,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.6.1" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc" +checksum = "7c4437699b6d34972de58652c68b98cb5b53a4199ab126db8e20ec8ded29a721" dependencies = [ "bitflags", "core-foundation", @@ -2826,9 +3045,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.6.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" +checksum = "31c9bb296072e961fcbd8853511dd39c2d8be2deb1e17c6860b1d30732b323b4" dependencies = [ "core-foundation-sys", "libc", @@ -2836,44 +3055,44 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.13" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f6841e709003d68bb2deee8c343572bf446003ec20a583e76f7b15cebf3711" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" [[package]] name = "serde" -version = "1.0.143" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] [[package]] name = "serde_bytes" -version = "0.11.7" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfc50e8183eeeb6178dcb167ae34a8051d63535023ae38b5d8d12beae193d37b" +checksum = "718dc5fff5b36f99093fc49b280cfc96ce6fc824317783bff5a1fed0c7a64819" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.143" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "serde_json" -version = "1.0.83" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" dependencies = [ "itoa", "ryu", @@ -2909,9 +3128,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ "darling", - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -2928,13 +3147,13 @@ dependencies = [ [[package]] name = "sha-1" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.3", + "digest 0.10.6", ] [[package]] @@ -2952,13 +3171,13 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.2" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55deaec60f81eefe3cce0dc50bda92d6d8e88f2a27df7c5033b42afeb1ed2676" +checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" dependencies = [ "cfg-if", "cpufeatures", - "digest 0.10.3", + "digest 0.10.6", ] [[package]] @@ -2975,21 +3194,42 @@ dependencies = [ [[package]] name = "sha3" -version = "0.10.2" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a31480366ec990f395a61b7c08122d99bd40544fdb5abcfc1b06bb29994312c" +checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" dependencies = [ - "digest 0.10.3", + "digest 0.10.6", "keccak", ] +[[package]] +name = "shank" +version = "0.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90b0d7b8998cc311310854451e7f5903cf3a151ca93ef6aa2b8b6c369a037b21" +dependencies = [ + "shank_macro 0.0.10", +] + [[package]] name = "shank" version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b63e565b5e95ad88ab38f312e89444c749360641c509ef2de0093b49f55974a5" dependencies = [ - "shank_macro", + "shank_macro 0.0.11", +] + +[[package]] +name = "shank_macro" +version = "0.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a37d6cf5df84eb622cb6fc74071d5d773c6a23b9ec55691db7b6a8c2286926" +dependencies = [ + "proc-macro2 1.0.50", + "quote 1.0.23", + "shank_macro_impl 0.0.10", + "syn 1.0.107", ] [[package]] @@ -2998,10 +3238,23 @@ version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63927d22a1e8b74bda98cc6e151fcdf178b7abb0dc6c4f81e0bbf5ffe2fc4ec8" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", - "shank_macro_impl", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "shank_macro_impl 0.0.11", + "syn 1.0.107", +] + +[[package]] +name = "shank_macro_impl" +version = "0.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1314ff5d5a3dffd9b93de1463e2b6afc2350f17596d7d9b3fe0924c9edd250df" +dependencies = [ + "anyhow", + "proc-macro2 1.0.50", + "quote 1.0.23", + "serde", + "syn 1.0.107", ] [[package]] @@ -3011,10 +3264,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ce03403df682f80f4dc1efafa87a4d0cb89b03726d0565e6364bdca5b9a441" dependencies = [ "anyhow", - "proc-macro2 1.0.43", - "quote 1.0.21", + "proc-macro2 1.0.50", + "quote 1.0.23", "serde", - "syn 1.0.99", + "syn 1.0.107", ] [[package]] @@ -3026,6 +3279,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shell-words" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde" + [[package]] name = "signal-hook-registry" version = "1.4.0" @@ -3037,9 +3296,9 @@ dependencies = [ [[package]] name = "signature" -version = "1.5.0" +version = "1.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f054c6c1a6e95179d6f23ed974060dcefb2d9388bb7256900badad682c499de4" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" [[package]] name = "sized-chunks" @@ -3062,15 +3321,15 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "socket2" -version = "0.4.4" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" dependencies = [ "libc", "winapi", @@ -3078,12 +3337,12 @@ dependencies = [ [[package]] name = "solana-account-decoder" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4d44753c0ce5415a61a7a23705e895e92202291116c3fc9beb1946a998098a" +checksum = "b04c1316932017ae5f947e83d77cc0356c4a395130a480cdc17ffb0570a0c115" dependencies = [ "Inflector", - "base64 0.13.0", + "base64 0.13.1", "bincode", "bs58", "bv", @@ -3091,6 +3350,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", + "solana-address-lookup-table-program", "solana-config-program", "solana-sdk", "solana-vote-program", @@ -3102,9 +3362,9 @@ dependencies = [ [[package]] name = "solana-address-lookup-table-program" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da6722570d4a167048ce713a8114f432b5f2cbaeb0a08e9ed4d1e4338ce2b272" +checksum = "5be490ed850c99286a4e4ba169ce20695336fe666c56bd823bfd8db689d23a58" dependencies = [ "bincode", "bytemuck", @@ -3123,9 +3383,9 @@ dependencies = [ [[package]] name = "solana-banks-client" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94259a7b8980809f68069589b40139633fcde459399afe7579e2bf767bcee7f0" +checksum = "3b21aa8e362024521991202613a8623c1b7268cb3be1530842419302feb57695" dependencies = [ "borsh", "futures", @@ -3140,9 +3400,9 @@ dependencies = [ [[package]] name = "solana-banks-interface" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55651e195c1f91ca890dc91e8ba59950dbb95529d418a77fa4216641b4f065dd" +checksum = "2e0cb35613656f5884041196a93598d20f3ebbcd46266600004299ed6c734f1e" dependencies = [ "serde", "solana-sdk", @@ -3151,9 +3411,9 @@ dependencies = [ [[package]] name = "solana-banks-server" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55c321254138824dba2340e28cdfddc9eca00fd755f47caee38e42b82bfc1a79" +checksum = "bf4169068e52c0b5af58a44a8a56621f2d3766b184d639795c1453382e45f69b" dependencies = [ "bincode", "crossbeam-channel", @@ -3171,9 +3431,9 @@ dependencies = [ [[package]] name = "solana-bpf-loader-program" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b20cb83962ec6e7cf7a5779e7129629583dce7abc6a322d3d4e3165384465d5" +checksum = "ddde9efdbca9681b3c59592cbcd3e24a4c3768134fad12dc0a649c4d5313ac8c" dependencies = [ "bincode", "byteorder", @@ -3190,9 +3450,9 @@ dependencies = [ [[package]] name = "solana-bucket-map" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4f05045db6908efe298965c614bd47f450fe5af17705731d35b111a842876c1" +checksum = "54fbbc3a256dd22f5dae449098edef9e9c16a2732a6df61228b382ccfff8a578" dependencies = [ "log", "memmap2", @@ -3205,9 +3465,9 @@ dependencies = [ [[package]] name = "solana-clap-utils" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18926f8ed5f6b81309e0b713fb714b6cdfa1f54dee34d191e2fba3fa0752aa30" +checksum = "36228e03e14bc7d7707189b66f625981993f1a000b0b192d5b42367349901d91" dependencies = [ "chrono", "clap 2.34.0", @@ -3223,9 +3483,9 @@ dependencies = [ [[package]] name = "solana-cli-config" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7ceeb39d00d3837a019e8b2dd0f87a81bf428d48b1a0576df0f062f314a422c" +checksum = "6c43b08f24fd605eaeaafe0e834dc9b209137ac253bc874d32a5bdd791cbd318" dependencies = [ "dirs-next", "lazy_static", @@ -3239,13 +3499,13 @@ dependencies = [ [[package]] name = "solana-client" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ec5b2b3a9e5798c11ef3f83f18362c655e0c55f9aa0dbadbf9380ea460f073" +checksum = "a3e270b1afd0b360c2aec42ae302ae7980ebb226017275b32a6156ab2ccbdad9" dependencies = [ "async-mutex", "async-trait", - "base64 0.13.0", + "base64 0.13.1", "bincode", "bs58", "bytes", @@ -3293,9 +3553,9 @@ dependencies = [ [[package]] name = "solana-compute-budget-program" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b33d7ff1b527bdf23277e710362986eac87c6bac681ab170038cab33e9e1cd58" +checksum = "57103610e5cd924399ac520238a11b7c65a869b14d89ce651f4f3b60072b5cdb" dependencies = [ "solana-program-runtime", "solana-sdk", @@ -3303,9 +3563,9 @@ dependencies = [ [[package]] name = "solana-config-program" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b5a54c257d0c4004f8a70bca534ebe949b0608fb4e0ce92cb0ace6446168f0" +checksum = "fb275d80a482134f0f0c5439b0c40ba3f04bef70dbc46c0e47f6107f6ae482a8" dependencies = [ "bincode", "chrono", @@ -3317,9 +3577,9 @@ dependencies = [ [[package]] name = "solana-faucet" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4bccb1d77d2cc3adcdabb091f0d4064151c4a20bfa53f2201f3db94e675a4f2" +checksum = "b3ef95ad1f87b8c011d0e4d85a46f4a703e9dd7e722459659b395ed70d6ba924" dependencies = [ "bincode", "byteorder", @@ -3341,9 +3601,9 @@ dependencies = [ [[package]] name = "solana-frozen-abi" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28e4e35bc58c465f161bde764ebce41fdfcb503583cf3a77e0211274cc12b22d" +checksum = "f44a019070a6cec4d3ad8605c5caa65bdaa13f00b5f1849340f44ffea63b625b" dependencies = [ "ahash", "blake3", @@ -3361,13 +3621,13 @@ dependencies = [ "log", "memmap2", "once_cell", - "rand_core 0.6.3", + "rand_core 0.6.4", "rustc_version", "serde", "serde_bytes", "serde_derive", "serde_json", - "sha2 0.10.2", + "sha2 0.10.6", "solana-frozen-abi-macro", "subtle", "thiserror", @@ -3375,21 +3635,21 @@ dependencies = [ [[package]] name = "solana-frozen-abi-macro" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "708f837d748e574b1e53b250ab1f4a69ba330bbc10d041d02381165f0f36291a" +checksum = "be23cc7a382f54dfe1348edb94610e5cc146b8eb21563cdd04062a403c75ba62" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", + "proc-macro2 1.0.50", + "quote 1.0.23", "rustc_version", - "syn 1.0.99", + "syn 1.0.107", ] [[package]] name = "solana-logger" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ea6fc68d63d33d862d919d4c8ad7f613ec243ccf6762d595c660020b289b57" +checksum = "447d16a70a1b5383736ef44801050c0e1affd022303b22ed899352f958c2de4b" dependencies = [ "env_logger", "lazy_static", @@ -3398,9 +3658,9 @@ dependencies = [ [[package]] name = "solana-measure" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83282c7a5852e68428582f5c7ce3d7cb7662f53c12210e4738bad7c7a42430b6" +checksum = "2400d2534a19f7605c5059060edea0499600a223f1a1f6a4b172666c04946a77" dependencies = [ "log", "solana-sdk", @@ -3408,9 +3668,9 @@ dependencies = [ [[package]] name = "solana-metrics" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2036de5b39a105c835ac2fa13ebe105beca99544d5f5585a780e2d4387b41b09" +checksum = "68aaa3d683945dc3b6ca38923ef952ca1f96a27b61f898a1ddf9f4cd79f2df92" dependencies = [ "crossbeam-channel", "gethostname", @@ -3422,12 +3682,12 @@ dependencies = [ [[package]] name = "solana-net-utils" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebea63e292727e5074a3de53826dd6ed0c3c50914e012e2613728d1f706f0244" +checksum = "d6d7093739e143d5e2edf3e81e523d47228adb802b847d66f4ab819be7ad6dc8" dependencies = [ "bincode", - "clap 3.2.17", + "clap 3.2.23", "crossbeam-channel", "log", "nix", @@ -3444,9 +3704,9 @@ dependencies = [ [[package]] name = "solana-perf" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0e04922848ea6480e61649000bf497eedb5265749740fd37ea5f6427bf49080" +checksum = "cbc742f8d53f0a6e6f3a27ed11c1d0764b5486813c721d625c56094fcd14e984" dependencies = [ "ahash", "bincode", @@ -3471,11 +3731,11 @@ dependencies = [ [[package]] name = "solana-program" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdd314d85b171bb20ccdcaf07346a9d52a012b10d84f4706f0628813d002fef8" +checksum = "d0937481f080f5dd495fae456c94718a7bacf30fb5fdabb02dcb8a9622e446d5" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", "bincode", "bitflags", "blake3", @@ -3488,14 +3748,14 @@ dependencies = [ "console_error_panic_hook", "console_log", "curve25519-dalek", - "getrandom 0.2.7", + "getrandom 0.2.8", "itertools", "js-sys", "lazy_static", "libc", "libsecp256k1", "log", - "memoffset", + "memoffset 0.6.5", "num-derive", "num-traits", "parking_lot 0.12.1", @@ -3507,8 +3767,8 @@ dependencies = [ "serde_bytes", "serde_derive", "serde_json", - "sha2 0.10.2", - "sha3 0.10.2", + "sha2 0.10.6", + "sha3 0.10.6", "solana-frozen-abi", "solana-frozen-abi-macro", "solana-sdk-macro", @@ -3520,11 +3780,11 @@ dependencies = [ [[package]] name = "solana-program-runtime" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7e08fccfc8b3c15f3a655e7398beb96796b7e9a951c552c9eac54c1b11cbfc4" +checksum = "b4d12047608bac77fca000e18f7a2df3c7fa90656d7c7d387b1cd7faf18b238c" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", "bincode", "eager", "enum-iterator", @@ -3534,6 +3794,7 @@ dependencies = [ "log", "num-derive", "num-traits", + "rand 0.7.3", "rustc_version", "serde", "solana-frozen-abi", @@ -3546,13 +3807,13 @@ dependencies = [ [[package]] name = "solana-program-test" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b5bdace4e08c350dd52079e8ec3158039fc310f2e0b0f6872c6601cb1da01cc" +checksum = "930a7116109f53d97ee35a88a9964c7bef0981399b7818117bd13a9f31f5854f" dependencies = [ "assert_matches", "async-trait", - "base64 0.13.0", + "base64 0.13.1", "bincode", "chrono-humanize", "log", @@ -3571,9 +3832,9 @@ dependencies = [ [[package]] name = "solana-rayon-threadlimit" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2cba29ce4de8a54e5367b6dfc3934f824c7299de4acc02186e8426bd87e9207" +checksum = "b6eca67181e0381532db4bc69a625b1f96a047be461ff9050c451add0165424f" dependencies = [ "lazy_static", "num_cpus", @@ -3581,9 +3842,9 @@ dependencies = [ [[package]] name = "solana-remote-wallet" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0710d1e9164897ddc85548e5b048dd3721322d44fbf980deecc41d9c47fa69c" +checksum = "9b83d035ee90035ebcb07ec73672fdc0272e5b98899846dd29fcb31f856ac78c" dependencies = [ "console", "dialoguer", @@ -3600,9 +3861,9 @@ dependencies = [ [[package]] name = "solana-runtime" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab625aab2b40607937d31a89749591d0c5aea15b2368f884ed2339e20522d75" +checksum = "82b8557969bd479d91902b50cb204d3343e783ce34fc976dc92df28e87f3ebdb" dependencies = [ "arrayref", "bincode", @@ -3621,6 +3882,7 @@ dependencies = [ "itertools", "lazy_static", "log", + "lru", "lz4", "memmap2", "num-derive", @@ -3660,12 +3922,12 @@ dependencies = [ [[package]] name = "solana-sdk" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad7d954df63b267857e26670e3aacfd8e2943ca703653b0418e5afc85046c2f3" +checksum = "390e7481c56dda2ceab2652beeda30a533e9667b34861a2eb4eec92fa1d826d7" dependencies = [ "assert_matches", - "base64 0.13.0", + "base64 0.13.1", "bincode", "bitflags", "borsh", @@ -3674,7 +3936,7 @@ dependencies = [ "byteorder", "chrono", "derivation-path", - "digest 0.10.3", + "digest 0.10.6", "ed25519-dalek", "ed25519-dalek-bip32", "generic-array", @@ -3697,8 +3959,8 @@ dependencies = [ "serde_bytes", "serde_derive", "serde_json", - "sha2 0.10.2", - "sha3 0.10.2", + "sha2 0.10.6", + "sha3 0.10.6", "solana-frozen-abi", "solana-frozen-abi-macro", "solana-logger", @@ -3711,22 +3973,22 @@ dependencies = [ [[package]] name = "solana-sdk-macro" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0d9e81bc46edcc517b2df504856d57a5101c7586ec63f3143ae11fbe2eba613" +checksum = "33d0acbad862093ea123f3a27364336dcb0c8373522cd6810496a34e932c56c1" dependencies = [ "bs58", - "proc-macro2 1.0.43", - "quote 1.0.21", + "proc-macro2 1.0.50", + "quote 1.0.23", "rustversion", - "syn 1.0.99", + "syn 1.0.107", ] [[package]] name = "solana-send-transaction-service" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "295dc0525eda60bd7b97a2abb5e9b54742a6dadbfab3106128e90e8ca6860c3e" +checksum = "1f539ebfe19fc2e3412bfcb5de06fc23d6a70cf44412a4e8edc6ac715db708b3" dependencies = [ "crossbeam-channel", "log", @@ -3739,9 +4001,9 @@ dependencies = [ [[package]] name = "solana-stake-program" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca102b933d8ecda98c08733e9a1bbfee457f09976d3bc825e8e15261ab4bd04" +checksum = "6513db9a3afe6ef1acf70b1cde59ffdf9d0f5b1db8806e01ca39b50c6a984312" dependencies = [ "bincode", "log", @@ -3762,9 +4024,9 @@ dependencies = [ [[package]] name = "solana-streamer" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2341bb432f4249d874f99bfe3e659b8a444e64653fa755105cd3fc098d9824a6" +checksum = "853b0187fdf233c13e8b7ba76e61d0c7cb49ca92c5fdb3b7568ad5ca30e2cf88" dependencies = [ "crossbeam-channel", "futures-util", @@ -3791,12 +4053,12 @@ dependencies = [ [[package]] name = "solana-transaction-status" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd761eb97f88965303ccecccd8745c76d13e1c045873b60c74cf18a6b4652e1" +checksum = "3c5bbdaed99403e4a17763bee60c1e0e3418524503c72b514ebff62efbcc9d33" dependencies = [ "Inflector", - "base64 0.13.0", + "base64 0.13.1", "bincode", "borsh", "bs58", @@ -3806,9 +4068,9 @@ dependencies = [ "serde_derive", "serde_json", "solana-account-decoder", + "solana-address-lookup-table-program", "solana-measure", "solana-metrics", - "solana-runtime", "solana-sdk", "solana-vote-program", "spl-associated-token-account", @@ -3820,9 +4082,9 @@ dependencies = [ [[package]] name = "solana-version" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98db59e373d4849305c66273fab8e9dd6493853c4c2fc936c933407f938b5eee" +checksum = "5a46c9ecb15ccd5388511cec0c5bfb989589425f8286ce432ff64b55dc7bf61e" dependencies = [ "log", "rustc_version", @@ -3836,9 +4098,9 @@ dependencies = [ [[package]] name = "solana-vote-program" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03fb1433ed4f09e3c6e04bd4f30e580afc301c07c71de477068f6d5f2fbb8ffe" +checksum = "81ab9ff8928282cb42871a370435dd4713f700854801afb476cf63066f1337db" dependencies = [ "bincode", "log", @@ -3857,9 +4119,9 @@ dependencies = [ [[package]] name = "solana-zk-token-proof-program" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9675b62500159a8caf471aae016ad2d2aa65ef19a3ec1da083dd850eb7d8ab6d" +checksum = "b02e1c183fc3ef5f2be0292619a6835860ef0151e505c9803bde5ffa8f47bc48" dependencies = [ "bytemuck", "getrandom 0.1.16", @@ -3872,19 +4134,20 @@ dependencies = [ [[package]] name = "solana-zk-token-sdk" -version = "1.11.5" +version = "1.14.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62415c05a9ebfffaf8befaa61b24492ebf88269cf84cbeba714bac4125ec4ea3" +checksum = "cebca4083e982ae01583d1a590c4d679e6f648a4761364ddfb43026d2c433142" dependencies = [ "aes-gcm-siv", "arrayref", - "base64 0.13.0", + "base64 0.13.1", "bincode", "bytemuck", "byteorder", "cipher 0.4.3", "curve25519-dalek", "getrandom 0.1.16", + "itertools", "lazy_static", "merlin", "num-derive", @@ -3936,13 +4199,18 @@ dependencies = [ [[package]] name = "spl-associated-token-account" -version = "1.0.5" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b013067447a1396303ddfc294f36e3d260a32f8a16c501c295bcdc7de39b490" +checksum = "fbc000f0fdf1f12f99d77d398137c1751345b18c88258ce0f99b7872cf6c9bd6" dependencies = [ + "assert_matches", "borsh", + "num-derive", + "num-traits", "solana-program", "spl-token", + "spl-token-2022", + "thiserror", ] [[package]] @@ -3956,9 +4224,9 @@ dependencies = [ [[package]] name = "spl-token" -version = "3.3.1" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32d05653bed5932064a287340dbc8a3cb298ee717e5c7ec3353d7cdb9f8fb7e1" +checksum = "8e85e168a785e82564160dcb87b2a8e04cee9bfd1f4d488c729d53d6a4bd300d" dependencies = [ "arrayref", "bytemuck", @@ -3971,9 +4239,9 @@ dependencies = [ [[package]] name = "spl-token-2022" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0a97cbf60b91b610c846ccf8eecca96d92a24a19ffbf9fe06cd0c84e76ec45e" +checksum = "0edb869dbe159b018f17fb9bfa67118c30f232d7f54a73742bc96794dff77ed8" dependencies = [ "arrayref", "bytemuck", @@ -4021,10 +4289,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" dependencies = [ "heck", - "proc-macro2 1.0.43", - "quote 1.0.21", + "proc-macro2 1.0.50", + "quote 1.0.23", "rustversion", - "syn 1.0.99", + "syn 1.0.107", ] [[package]] @@ -4052,12 +4320,12 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.99" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", + "proc-macro2 1.0.50", + "quote 1.0.23", "unicode-ident", ] @@ -4067,10 +4335,10 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", - "unicode-xid 0.2.3", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", + "unicode-xid 0.2.4", ] [[package]] @@ -4114,9 +4382,9 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee42b4e559f17bce0385ebf511a7beb67d5cc33c12c96b7f4e9789919d9c10f" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -4135,23 +4403,13 @@ dependencies = [ [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] -[[package]] -name = "terminal_size" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "textwrap" version = "0.11.0" @@ -4163,28 +4421,28 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.32" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.32" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -4198,9 +4456,9 @@ dependencies = [ [[package]] name = "time" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" dependencies = [ "libc", "wasi 0.10.0+wasi-snapshot-preview1", @@ -4209,21 +4467,30 @@ dependencies = [ [[package]] name = "time" -version = "0.3.13" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db76ff9fa4b1458b3c7f077f3ff9887394058460d21e634355b273aaf11eea45" +checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" dependencies = [ "itoa", - "libc", - "num_threads", + "serde", + "time-core", "time-macros", ] +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + [[package]] name = "time-macros" -version = "0.2.4" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" +checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +dependencies = [ + "time-core", +] [[package]] name = "tiny-bip39" @@ -4281,13 +4548,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "1.8.0" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] @@ -4319,9 +4586,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df54d54117d6fdc4e4fea40fe1e4e566b3505700e148a6827e59b34b0d2600d9" +checksum = "d660770404473ccd7bc9f8b28494a811bc18542b915c0855c51e8f419d5223ce" dependencies = [ "futures-core", "pin-project-lite", @@ -4375,13 +4642,30 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" + +[[package]] +name = "toml_edit" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "729bfd096e40da9c001f778f5cdecbd2957929a24e10e5883d9392220a751581" +dependencies = [ + "indexmap", + "nom8", + "toml_datetime", +] + [[package]] name = "tower-service" version = "0.3.2" @@ -4390,9 +4674,9 @@ checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.36" +version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "log", @@ -4403,20 +4687,20 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", ] [[package]] name = "tracing-core" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", "valuable", @@ -4437,9 +4721,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" dependencies = [ "sharded-slab", "thread_local", @@ -4448,9 +4732,9 @@ dependencies = [ [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "tungstenite" @@ -4458,7 +4742,7 @@ version = "0.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" dependencies = [ - "base64 0.13.0", + "base64 0.13.1", "byteorder", "bytes", "http", @@ -4476,36 +4760,36 @@ dependencies = [ [[package]] name = "typenum" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" [[package]] name = "unicode-ident" -version = "1.0.3" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-normalization" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "unicode-width" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "unicode-xid" @@ -4515,9 +4799,9 @@ checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" [[package]] name = "unicode-xid" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "universal-hash" @@ -4556,13 +4840,12 @@ dependencies = [ [[package]] name = "url" -version = "2.2.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", "idna", - "matches", "percent-encoding", ] @@ -4637,9 +4920,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d" +checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -4647,24 +4930,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f" +checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.32" +version = "0.4.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa76fb221a1f8acddf5b54ace85912606980ad661ac7a503b4570ffd3a624dad" +checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" dependencies = [ "cfg-if", "js-sys", @@ -4674,38 +4957,38 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602" +checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" dependencies = [ - "quote 1.0.21", + "quote 1.0.23", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da" +checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.82" +version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a" +checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "web-sys" -version = "0.3.59" +version = "0.3.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed055ab27f941423197eb86b2035720b1a3ce40504df082cac2ecc6ed73335a1" +checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" dependencies = [ "js-sys", "wasm-bindgen", @@ -4723,9 +5006,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.4" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c760f0d366a6c24a02ed7816e23e691f5d92291f94d15e836006fd11b04daf" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" dependencies = [ "webpki", ] @@ -4763,46 +5046,60 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ + "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_msvc", "windows_x86_64_gnu", + "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" + [[package]] name = "windows_aarch64_msvc" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "winreg" @@ -4820,7 +5117,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0ecbeb7b67ce215e40e3cc7f2ff902f94a223acf44995934763467e7b1febc8" dependencies = [ "asn1-rs", - "base64 0.13.0", + "base64 0.13.1", "data-encoding", "der-parser", "lazy_static", @@ -4828,7 +5125,7 @@ dependencies = [ "oid-registry", "rusticata-macros", "thiserror", - "time 0.3.13", + "time 0.3.17", ] [[package]] @@ -4851,11 +5148,11 @@ dependencies = [ [[package]] name = "yasna" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "346d34a236c9d3e5f3b9b74563f238f955bbd05fa0b8b4efa53c130c43982f4c" +checksum = "aed2e7a52e3744ab4d0c05c20aa065258e84c49fd4226f5191b2ed29712710b4" dependencies = [ - "time 0.3.13", + "time 0.3.17", ] [[package]] @@ -4869,13 +5166,13 @@ dependencies = [ [[package]] name = "zeroize_derive" -version = "1.3.2" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f8f187641dad4f680d25c4bfc4225b418165984179f26ca76ec4fb6441d3a17" +checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c" dependencies = [ - "proc-macro2 1.0.43", - "quote 1.0.21", - "syn 1.0.99", + "proc-macro2 1.0.50", + "quote 1.0.23", + "syn 1.0.107", "synstructure", ] @@ -4900,10 +5197,11 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.1+zstd.1.5.2" +version = "2.0.5+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fd07cbbc53846d9145dbffdf6dd09a7a0aa52be46741825f5c97bdd4f73f12b" +checksum = "edc50ffce891ad571e9f9afe5039c4837bede781ac4bb13052ed7ae695518596" dependencies = [ "cc", "libc", + "pkg-config", ] diff --git a/token-metadata/Cargo.toml b/token-metadata/Cargo.toml index 2a094db7bf..7863bcbd01 100644 --- a/token-metadata/Cargo.toml +++ b/token-metadata/Cargo.toml @@ -1,8 +1,13 @@ [workspace] members=[ + "macro", "program", "cli" ] [profile.release] overflow-checks = true # Enable integer overflow checks. +strip = true # Automatically strip symbols from the binary. +opt-level = "z" +lto = true +codegen-units = 1 diff --git a/token-metadata/cli/Cargo.toml b/token-metadata/cli/Cargo.toml index be79116e58..5733ece017 100644 --- a/token-metadata/cli/Cargo.toml +++ b/token-metadata/cli/Cargo.toml @@ -9,19 +9,14 @@ edition = "2021" publish = false [dependencies] -solana-client = "1.9.5" -solana-program = "1.9.5" -solana-sdk = "1.9.5" +solana-client = "1.13.5" +solana-program = "1.13.5" +solana-sdk = "1.13.5" bincode = "1.3.2" borsh = "0.9.1" clap = "2.33.0" -solana-clap-utils = "1.9.5" -solana-cli-config = "1.9.5" +solana-clap-utils = "1.13.5" +solana-cli-config = "1.13.5" mpl-token-metadata = { path="../program", features = [ "no-entrypoint" ] } spl-token = { version="3.2.0", features = [ "no-entrypoint" ] } spl-associated-token-account = { version ="*", features = [ "no-entrypoint" ] } - - -[profile.release] -overflow-checks = true # Enable integer overflow checks. - diff --git a/token-metadata/js/.ammanrc.js b/token-metadata/js/.ammanrc.js index cc13f0d94c..5b0f2716bd 100644 --- a/token-metadata/js/.ammanrc.js +++ b/token-metadata/js/.ammanrc.js @@ -2,14 +2,7 @@ // @ts-check const base = require('../../.base-ammanrc.js'); const validator = { - ...base.validator, - programs: [base.programs.metadata], + ...base.validator, + programs: [base.programs.metadata, base.programs.token_auth_rules], }; - -const storage = { - enabled: true, - storageId: 'mock-storage', - clearOnStart: true, -}; - -module.exports = { validator, storage }; +module.exports = { validator }; diff --git a/token-metadata/js/.prettierrc.js b/token-metadata/js/.prettierrc.js index 7c553141f1..c6ea425e1d 100644 --- a/token-metadata/js/.prettierrc.js +++ b/token-metadata/js/.prettierrc.js @@ -1,2 +1 @@ module.exports = require('../../.prettierrc'); - diff --git a/token-metadata/js/build-and-run-test.sh b/token-metadata/js/build-and-run-test.sh new file mode 100755 index 0000000000..d5b9db4e86 --- /dev/null +++ b/token-metadata/js/build-and-run-test.sh @@ -0,0 +1,8 @@ +cd ../../ +build.sh token-metadata +cd token-metadata/js +yarn api:gen +amman stop +amman start & +sleep 5 +run-test.sh $1 \ No newline at end of file diff --git a/token-metadata/js/idl/mpl_token_metadata.json b/token-metadata/js/idl/mpl_token_metadata.json index e44c27a675..ceddd22297 100644 --- a/token-metadata/js/idl/mpl_token_metadata.json +++ b/token-metadata/js/idl/mpl_token_metadata.json @@ -1,5 +1,5 @@ { - "version": "1.6.4", + "version": "1.7.0-beta.2", "name": "mpl_token_metadata", "instructions": [ { @@ -2341,48 +2341,1705 @@ "type": "u8", "value": 40 } + }, + { + "name": "Burn", + "accounts": [ + { + "name": "metadata", + "isMut": true, + "isSigner": false, + "desc": "Metadata (pda of ['metadata', program id, mint id])" + }, + { + "name": "owner", + "isMut": true, + "isSigner": true, + "desc": "Asset owner" + }, + { + "name": "mint", + "isMut": true, + "isSigner": false, + "desc": "Mint of token asset" + }, + { + "name": "tokenAccount", + "isMut": true, + "isSigner": false, + "desc": "Token account to close" + }, + { + "name": "masterEditionAccount", + "isMut": true, + "isSigner": false, + "desc": "MasterEdition of the asset" + }, + { + "name": "splTokenProgram", + "isMut": false, + "isSigner": false, + "desc": "SPL Token Program" + }, + { + "name": "collectionMetadata", + "isMut": true, + "isSigner": false, + "desc": "Metadata of the Collection", + "optional": true + }, + { + "name": "authorizationRules", + "isMut": false, + "isSigner": false, + "desc": "Token Authorization Rules account", + "optional": true + }, + { + "name": "authorizationRulesProgram", + "isMut": false, + "isSigner": false, + "desc": "Token Authorization Rules Program", + "optional": true + } + ], + "args": [ + { + "name": "burnArgs", + "type": { + "defined": "BurnArgs" + } + } + ], + "defaultOptionalAccounts": true, + "discriminant": { + "type": "u8", + "value": 41 + } + }, + { + "name": "Create", + "accounts": [ + { + "name": "metadata", + "isMut": true, + "isSigner": false, + "desc": "Unallocated metadata account with address as pda of ['metadata', program id, mint id]" + }, + { + "name": "masterEdition", + "isMut": true, + "isSigner": false, + "desc": "Unallocated edition account with address as pda of ['metadata', program id, mint, 'edition']", + "optional": true + }, + { + "name": "mint", + "isMut": true, + "isSigner": false, + "desc": "Mint of token asset" + }, + { + "name": "authority", + "isMut": false, + "isSigner": true, + "desc": "Mint authority" + }, + { + "name": "payer", + "isMut": true, + "isSigner": true, + "desc": "Payer" + }, + { + "name": "updateAuthority", + "isMut": false, + "isSigner": false, + "desc": "Update authority for the metadata account" + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "desc": "System program" + }, + { + "name": "sysvarInstructions", + "isMut": false, + "isSigner": false, + "desc": "Instructions sysvar account" + }, + { + "name": "splTokenProgram", + "isMut": false, + "isSigner": false, + "desc": "SPL Token program" + } + ], + "args": [ + { + "name": "createArgs", + "type": { + "defined": "CreateArgs" + } + } + ], + "defaultOptionalAccounts": true, + "discriminant": { + "type": "u8", + "value": 42 + } + }, + { + "name": "Mint", + "accounts": [ + { + "name": "token", + "isMut": true, + "isSigner": false, + "desc": "Token or Associated Token account" + }, + { + "name": "tokenOwner", + "isMut": false, + "isSigner": false, + "desc": "Owner of the token account", + "optional": true + }, + { + "name": "metadata", + "isMut": false, + "isSigner": false, + "desc": "Metadata account (pda of ['metadata', program id, mint id])" + }, + { + "name": "masterEdition", + "isMut": false, + "isSigner": false, + "desc": "Master Edition account", + "optional": true + }, + { + "name": "tokenRecord", + "isMut": true, + "isSigner": false, + "desc": "Token record account", + "optional": true + }, + { + "name": "mint", + "isMut": true, + "isSigner": false, + "desc": "Mint of token asset" + }, + { + "name": "authority", + "isMut": false, + "isSigner": true, + "desc": "(Mint or Update) authority" + }, + { + "name": "delegateRecord", + "isMut": false, + "isSigner": false, + "desc": "Metadata delegate record", + "optional": true + }, + { + "name": "payer", + "isMut": true, + "isSigner": true, + "desc": "Payer" + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "desc": "System program" + }, + { + "name": "sysvarInstructions", + "isMut": false, + "isSigner": false, + "desc": "Instructions sysvar account" + }, + { + "name": "splTokenProgram", + "isMut": false, + "isSigner": false, + "desc": "SPL Token program" + }, + { + "name": "splAtaProgram", + "isMut": false, + "isSigner": false, + "desc": "SPL Associated Token Account program" + }, + { + "name": "authorizationRulesProgram", + "isMut": false, + "isSigner": false, + "desc": "Token Authorization Rules program", + "optional": true + }, + { + "name": "authorizationRules", + "isMut": false, + "isSigner": false, + "desc": "Token Authorization Rules account", + "optional": true + } + ], + "args": [ + { + "name": "mintArgs", + "type": { + "defined": "MintArgs" + } + } + ], + "defaultOptionalAccounts": true, + "discriminant": { + "type": "u8", + "value": 43 + } + }, + { + "name": "Delegate", + "accounts": [ + { + "name": "delegateRecord", + "isMut": true, + "isSigner": false, + "desc": "Delegate record account", + "optional": true + }, + { + "name": "delegate", + "isMut": false, + "isSigner": false, + "desc": "Owner of the delegated account" + }, + { + "name": "metadata", + "isMut": true, + "isSigner": false, + "desc": "Metadata account" + }, + { + "name": "masterEdition", + "isMut": false, + "isSigner": false, + "desc": "Master Edition account", + "optional": true + }, + { + "name": "tokenRecord", + "isMut": true, + "isSigner": false, + "desc": "Token record account", + "optional": true + }, + { + "name": "mint", + "isMut": false, + "isSigner": false, + "desc": "Mint of metadata" + }, + { + "name": "token", + "isMut": true, + "isSigner": false, + "desc": "Token account of mint", + "optional": true + }, + { + "name": "authority", + "isMut": false, + "isSigner": true, + "desc": "Update authority or token owner" + }, + { + "name": "payer", + "isMut": true, + "isSigner": true, + "desc": "Payer" + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "desc": "System Program" + }, + { + "name": "sysvarInstructions", + "isMut": false, + "isSigner": false, + "desc": "Instructions sysvar account" + }, + { + "name": "splTokenProgram", + "isMut": false, + "isSigner": false, + "desc": "SPL Token Program", + "optional": true + }, + { + "name": "authorizationRulesProgram", + "isMut": false, + "isSigner": false, + "desc": "Token Authorization Rules Program", + "optional": true + }, + { + "name": "authorizationRules", + "isMut": false, + "isSigner": false, + "desc": "Token Authorization Rules account", + "optional": true + } + ], + "args": [ + { + "name": "delegateArgs", + "type": { + "defined": "DelegateArgs" + } + } + ], + "defaultOptionalAccounts": true, + "discriminant": { + "type": "u8", + "value": 44 + } + }, + { + "name": "Revoke", + "accounts": [ + { + "name": "delegateRecord", + "isMut": true, + "isSigner": false, + "desc": "Delegate record account", + "optional": true + }, + { + "name": "delegate", + "isMut": false, + "isSigner": false, + "desc": "Owner of the delegated account" + }, + { + "name": "metadata", + "isMut": true, + "isSigner": false, + "desc": "Metadata account" + }, + { + "name": "masterEdition", + "isMut": false, + "isSigner": false, + "desc": "Master Edition account", + "optional": true + }, + { + "name": "tokenRecord", + "isMut": true, + "isSigner": false, + "desc": "Token record account", + "optional": true + }, + { + "name": "mint", + "isMut": false, + "isSigner": false, + "desc": "Mint of metadata" + }, + { + "name": "token", + "isMut": true, + "isSigner": false, + "desc": "Token account of mint", + "optional": true + }, + { + "name": "authority", + "isMut": false, + "isSigner": true, + "desc": "Update authority or token owner" + }, + { + "name": "payer", + "isMut": true, + "isSigner": true, + "desc": "Payer" + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "desc": "System Program" + }, + { + "name": "sysvarInstructions", + "isMut": false, + "isSigner": false, + "desc": "Instructions sysvar account" + }, + { + "name": "splTokenProgram", + "isMut": false, + "isSigner": false, + "desc": "SPL Token Program", + "optional": true + }, + { + "name": "authorizationRulesProgram", + "isMut": false, + "isSigner": false, + "desc": "Token Authorization Rules Program", + "optional": true + }, + { + "name": "authorizationRules", + "isMut": false, + "isSigner": false, + "desc": "Token Authorization Rules account", + "optional": true + } + ], + "args": [ + { + "name": "revokeArgs", + "type": { + "defined": "RevokeArgs" + } + } + ], + "defaultOptionalAccounts": true, + "discriminant": { + "type": "u8", + "value": 45 + } + }, + { + "name": "Lock", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": true, + "desc": "Delegate account" + }, + { + "name": "tokenOwner", + "isMut": false, + "isSigner": false, + "desc": "Token owner account", + "optional": true + }, + { + "name": "token", + "isMut": true, + "isSigner": false, + "desc": "Token account" + }, + { + "name": "mint", + "isMut": false, + "isSigner": false, + "desc": "Mint account" + }, + { + "name": "metadata", + "isMut": true, + "isSigner": false, + "desc": "Metadata account" + }, + { + "name": "edition", + "isMut": false, + "isSigner": false, + "desc": "Edition account", + "optional": true + }, + { + "name": "tokenRecord", + "isMut": true, + "isSigner": false, + "desc": "Token record account", + "optional": true + }, + { + "name": "payer", + "isMut": true, + "isSigner": true, + "desc": "Payer" + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "desc": "System program" + }, + { + "name": "sysvarInstructions", + "isMut": false, + "isSigner": false, + "desc": "System program" + }, + { + "name": "splTokenProgram", + "isMut": false, + "isSigner": false, + "desc": "SPL Token Program", + "optional": true + }, + { + "name": "authorizationRulesProgram", + "isMut": false, + "isSigner": false, + "desc": "Token Authorization Rules Program", + "optional": true + }, + { + "name": "authorizationRules", + "isMut": false, + "isSigner": false, + "desc": "Token Authorization Rules account", + "optional": true + } + ], + "args": [ + { + "name": "lockArgs", + "type": { + "defined": "LockArgs" + } + } + ], + "defaultOptionalAccounts": true, + "discriminant": { + "type": "u8", + "value": 46 + } + }, + { + "name": "Unlock", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": true, + "desc": "Delegate account" + }, + { + "name": "tokenOwner", + "isMut": false, + "isSigner": false, + "desc": "Token owner account", + "optional": true + }, + { + "name": "token", + "isMut": true, + "isSigner": false, + "desc": "Token account" + }, + { + "name": "mint", + "isMut": false, + "isSigner": false, + "desc": "Mint account" + }, + { + "name": "metadata", + "isMut": true, + "isSigner": false, + "desc": "Metadata account" + }, + { + "name": "edition", + "isMut": false, + "isSigner": false, + "desc": "Edition account", + "optional": true + }, + { + "name": "tokenRecord", + "isMut": true, + "isSigner": false, + "desc": "Token record account", + "optional": true + }, + { + "name": "payer", + "isMut": true, + "isSigner": true, + "desc": "Payer" + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "desc": "System program" + }, + { + "name": "sysvarInstructions", + "isMut": false, + "isSigner": false, + "desc": "System program" + }, + { + "name": "splTokenProgram", + "isMut": false, + "isSigner": false, + "desc": "SPL Token Program", + "optional": true + }, + { + "name": "authorizationRulesProgram", + "isMut": false, + "isSigner": false, + "desc": "Token Authorization Rules Program", + "optional": true + }, + { + "name": "authorizationRules", + "isMut": false, + "isSigner": false, + "desc": "Token Authorization Rules account", + "optional": true + } + ], + "args": [ + { + "name": "unlockArgs", + "type": { + "defined": "UnlockArgs" + } + } + ], + "defaultOptionalAccounts": true, + "discriminant": { + "type": "u8", + "value": 47 + } + }, + { + "name": "Migrate", + "accounts": [ + { + "name": "metadata", + "isMut": true, + "isSigner": false, + "desc": "Metadata account" + }, + { + "name": "edition", + "isMut": true, + "isSigner": false, + "desc": "Edition account" + }, + { + "name": "token", + "isMut": true, + "isSigner": false, + "desc": "Token account" + }, + { + "name": "tokenOwner", + "isMut": false, + "isSigner": false, + "desc": "Token account owner" + }, + { + "name": "mint", + "isMut": false, + "isSigner": false, + "desc": "Mint account" + }, + { + "name": "payer", + "isMut": true, + "isSigner": true, + "desc": "Update authority" + }, + { + "name": "authority", + "isMut": false, + "isSigner": true, + "desc": "Update authority" + }, + { + "name": "collectionMetadata", + "isMut": false, + "isSigner": false, + "desc": "Collection metadata account" + }, + { + "name": "delegateRecord", + "isMut": false, + "isSigner": false, + "desc": "Delegate record account" + }, + { + "name": "tokenRecord", + "isMut": false, + "isSigner": false, + "desc": "Token record account" + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "desc": "System program" + }, + { + "name": "sysvarInstructions", + "isMut": false, + "isSigner": false, + "desc": "Instruction sysvar account" + }, + { + "name": "splTokenProgram", + "isMut": false, + "isSigner": false, + "desc": "Token Program" + }, + { + "name": "authorizationRulesProgram", + "isMut": false, + "isSigner": false, + "desc": "Token Authorization Rules Program", + "optional": true + }, + { + "name": "authorizationRules", + "isMut": false, + "isSigner": false, + "desc": "Token Authorization Rules account", + "optional": true + } + ], + "args": [ + { + "name": "migrateArgs", + "type": { + "defined": "MigrateArgs" + } + } + ], + "defaultOptionalAccounts": true, + "discriminant": { + "type": "u8", + "value": 48 + } + }, + { + "name": "Transfer", + "accounts": [ + { + "name": "token", + "isMut": true, + "isSigner": false, + "desc": "Token account" + }, + { + "name": "tokenOwner", + "isMut": false, + "isSigner": false, + "desc": "Token account owner" + }, + { + "name": "destination", + "isMut": true, + "isSigner": false, + "desc": "Destination token account" + }, + { + "name": "destinationOwner", + "isMut": false, + "isSigner": false, + "desc": "Destination token account owner" + }, + { + "name": "mint", + "isMut": false, + "isSigner": false, + "desc": "Mint of token asset" + }, + { + "name": "metadata", + "isMut": true, + "isSigner": false, + "desc": "Metadata (pda of ['metadata', program id, mint id])" + }, + { + "name": "edition", + "isMut": false, + "isSigner": false, + "desc": "Edition of token asset", + "optional": true + }, + { + "name": "ownerTokenRecord", + "isMut": true, + "isSigner": false, + "desc": "Token record account", + "optional": true + }, + { + "name": "destinationTokenRecord", + "isMut": true, + "isSigner": false, + "desc": "Token record account", + "optional": true + }, + { + "name": "authority", + "isMut": false, + "isSigner": true, + "desc": "Transfer authority (token or delegate owner)" + }, + { + "name": "payer", + "isMut": true, + "isSigner": true, + "desc": "Payer" + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "desc": "System Program" + }, + { + "name": "sysvarInstructions", + "isMut": false, + "isSigner": false, + "desc": "Instructions sysvar account" + }, + { + "name": "splTokenProgram", + "isMut": false, + "isSigner": false, + "desc": "SPL Token Program" + }, + { + "name": "splAtaProgram", + "isMut": false, + "isSigner": false, + "desc": "SPL Associated Token Account program" + }, + { + "name": "authorizationRulesProgram", + "isMut": false, + "isSigner": false, + "desc": "Token Authorization Rules Program", + "optional": true + }, + { + "name": "authorizationRules", + "isMut": false, + "isSigner": false, + "desc": "Token Authorization Rules account", + "optional": true + } + ], + "args": [ + { + "name": "transferArgs", + "type": { + "defined": "TransferArgs" + } + } + ], + "defaultOptionalAccounts": true, + "discriminant": { + "type": "u8", + "value": 49 + } + }, + { + "name": "Update", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": true, + "desc": "Update authority or delegate" + }, + { + "name": "delegateRecord", + "isMut": false, + "isSigner": false, + "desc": "Delegate record PDA", + "optional": true + }, + { + "name": "token", + "isMut": false, + "isSigner": false, + "desc": "Token account", + "optional": true + }, + { + "name": "mint", + "isMut": false, + "isSigner": false, + "desc": "Mint account" + }, + { + "name": "metadata", + "isMut": true, + "isSigner": false, + "desc": "Metadata account" + }, + { + "name": "edition", + "isMut": true, + "isSigner": false, + "desc": "Edition account", + "optional": true + }, + { + "name": "payer", + "isMut": true, + "isSigner": true, + "desc": "Payer" + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "desc": "System program" + }, + { + "name": "sysvarInstructions", + "isMut": false, + "isSigner": false, + "desc": "System program" + }, + { + "name": "authorizationRulesProgram", + "isMut": false, + "isSigner": false, + "desc": "Token Authorization Rules Program", + "optional": true + }, + { + "name": "authorizationRules", + "isMut": false, + "isSigner": false, + "desc": "Token Authorization Rules account", + "optional": true + } + ], + "args": [ + { + "name": "updateArgs", + "type": { + "defined": "UpdateArgs" + } + } + ], + "defaultOptionalAccounts": true, + "discriminant": { + "type": "u8", + "value": 50 + } + }, + { + "name": "Use", + "accounts": [ + { + "name": "authority", + "isMut": false, + "isSigner": true, + "desc": "Token owner or delegate" + }, + { + "name": "delegateRecord", + "isMut": true, + "isSigner": false, + "desc": "Delegate record PDA", + "optional": true + }, + { + "name": "token", + "isMut": true, + "isSigner": false, + "desc": "Token account", + "optional": true + }, + { + "name": "mint", + "isMut": false, + "isSigner": false, + "desc": "Mint account" + }, + { + "name": "metadata", + "isMut": true, + "isSigner": false, + "desc": "Metadata account" + }, + { + "name": "edition", + "isMut": true, + "isSigner": false, + "desc": "Edition account", + "optional": true + }, + { + "name": "payer", + "isMut": false, + "isSigner": true, + "desc": "Payer" + }, + { + "name": "systemProgram", + "isMut": false, + "isSigner": false, + "desc": "System program" + }, + { + "name": "sysvarInstructions", + "isMut": false, + "isSigner": false, + "desc": "System program" + }, + { + "name": "splTokenProgram", + "isMut": false, + "isSigner": false, + "desc": "SPL Token Program", + "optional": true + }, + { + "name": "authorizationRulesProgram", + "isMut": false, + "isSigner": false, + "desc": "Token Authorization Rules Program", + "optional": true + }, + { + "name": "authorizationRules", + "isMut": false, + "isSigner": false, + "desc": "Token Authorization Rules account", + "optional": true + } + ], + "args": [ + { + "name": "useArgs", + "type": { + "defined": "UseArgs" + } + } + ], + "defaultOptionalAccounts": true, + "discriminant": { + "type": "u8", + "value": 51 + } + }, + { + "name": "Verify", + "accounts": [ + { + "name": "metadata", + "isMut": true, + "isSigner": false, + "desc": "Metadata account" + }, + { + "name": "collectionAuthority", + "isMut": true, + "isSigner": true, + "desc": "Collection Update authority" + }, + { + "name": "payer", + "isMut": true, + "isSigner": true, + "desc": "payer" + }, + { + "name": "authorizationRules", + "isMut": false, + "isSigner": false, + "desc": "Token Authorization Rules account", + "optional": true + }, + { + "name": "authorizationRulesProgram", + "isMut": false, + "isSigner": false, + "desc": "Token Authorization Rules Program", + "optional": true + } + ], + "args": [ + { + "name": "verifyArgs", + "type": { + "defined": "VerifyArgs" + } + } + ], + "defaultOptionalAccounts": true, + "discriminant": { + "type": "u8", + "value": 52 + } + } + ], + "accounts": [ + { + "name": "CollectionAuthorityRecord", + "type": { + "kind": "struct", + "fields": [ + { + "name": "key", + "type": { + "defined": "Key" + } + }, + { + "name": "bump", + "type": "u8" + }, + { + "name": "updateAuthority", + "type": { + "option": "publicKey" + } + } + ] + } + }, + { + "name": "MetadataDelegateRecord", + "type": { + "kind": "struct", + "fields": [ + { + "name": "key", + "type": { + "defined": "Key" + } + }, + { + "name": "bump", + "type": "u8" + }, + { + "name": "mint", + "type": "publicKey" + }, + { + "name": "delegate", + "type": "publicKey" + }, + { + "name": "updateAuthority", + "type": "publicKey" + } + ] + } + }, + { + "name": "Edition", + "type": { + "kind": "struct", + "fields": [ + { + "name": "key", + "type": { + "defined": "Key" + } + }, + { + "name": "parent", + "type": "publicKey" + }, + { + "name": "edition", + "type": "u64" + } + ] + } + }, + { + "name": "EditionMarker", + "type": { + "kind": "struct", + "fields": [ + { + "name": "key", + "type": { + "defined": "Key" + } + }, + { + "name": "ledger", + "type": { + "array": [ + "u8", + 31 + ] + } + } + ] + } + }, + { + "name": "TokenOwnedEscrow", + "type": { + "kind": "struct", + "fields": [ + { + "name": "key", + "type": { + "defined": "Key" + } + }, + { + "name": "baseToken", + "type": "publicKey" + }, + { + "name": "authority", + "type": { + "defined": "EscrowAuthority" + } + }, + { + "name": "bump", + "type": "u8" + } + ] + } + }, + { + "name": "MasterEditionV2", + "type": { + "kind": "struct", + "fields": [ + { + "name": "key", + "type": { + "defined": "Key" + } + }, + { + "name": "supply", + "type": "u64" + }, + { + "name": "maxSupply", + "type": { + "option": "u64" + } + } + ] + } + }, + { + "name": "MasterEditionV1", + "type": { + "kind": "struct", + "fields": [ + { + "name": "key", + "type": { + "defined": "Key" + } + }, + { + "name": "supply", + "type": "u64" + }, + { + "name": "maxSupply", + "type": { + "option": "u64" + } + }, + { + "name": "printingMint", + "type": "publicKey" + }, + { + "name": "oneTimePrintingAuthorizationMint", + "type": "publicKey" + } + ] + } + }, + { + "name": "Metadata", + "type": { + "kind": "struct", + "fields": [ + { + "name": "key", + "type": { + "defined": "Key" + } + }, + { + "name": "updateAuthority", + "type": "publicKey" + }, + { + "name": "mint", + "type": "publicKey" + }, + { + "name": "data", + "type": { + "defined": "Data" + } + }, + { + "name": "primarySaleHappened", + "type": "bool" + }, + { + "name": "isMutable", + "type": "bool" + }, + { + "name": "editionNonce", + "type": { + "option": "u8" + } + }, + { + "name": "tokenStandard", + "type": { + "option": { + "defined": "TokenStandard" + } + } + }, + { + "name": "collection", + "type": { + "option": { + "defined": "Collection" + } + } + }, + { + "name": "uses", + "type": { + "option": { + "defined": "Uses" + } + } + }, + { + "name": "collectionDetails", + "type": { + "option": { + "defined": "CollectionDetails" + } + } + }, + { + "name": "programmableConfig", + "type": { + "option": { + "defined": "ProgrammableConfig" + } + } + } + ] + } + }, + { + "name": "TokenRecord", + "type": { + "kind": "struct", + "fields": [ + { + "name": "key", + "type": { + "defined": "Key" + } + }, + { + "name": "bump", + "type": "u8" + }, + { + "name": "state", + "type": { + "defined": "TokenState" + } + }, + { + "name": "ruleSetRevision", + "type": { + "option": "u64" + } + }, + { + "name": "delegate", + "type": { + "option": "publicKey" + } + }, + { + "name": "delegateRole", + "type": { + "option": { + "defined": "TokenDelegateRole" + } + } + } + ] + } + }, + { + "name": "ReservationListV2", + "type": { + "kind": "struct", + "fields": [ + { + "name": "key", + "type": { + "defined": "Key" + } + }, + { + "name": "masterEdition", + "type": "publicKey" + }, + { + "name": "supplySnapshot", + "type": { + "option": "u64" + } + }, + { + "name": "reservations", + "type": { + "vec": { + "defined": "Reservation" + } + } + }, + { + "name": "totalReservationSpots", + "type": "u64" + }, + { + "name": "currentReservationSpots", + "type": "u64" + } + ] + } + }, + { + "name": "ReservationListV1", + "type": { + "kind": "struct", + "fields": [ + { + "name": "key", + "type": { + "defined": "Key" + } + }, + { + "name": "masterEdition", + "type": "publicKey" + }, + { + "name": "supplySnapshot", + "type": { + "option": "u64" + } + }, + { + "name": "reservations", + "type": { + "vec": { + "defined": "ReservationV1" + } + } + } + ] + } + }, + { + "name": "UseAuthorityRecord", + "type": { + "kind": "struct", + "fields": [ + { + "name": "key", + "type": { + "defined": "Key" + } + }, + { + "name": "allowedUses", + "type": "u64" + }, + { + "name": "bump", + "type": "u8" + } + ] + } } ], - "accounts": [ + "types": [ + { + "name": "SetCollectionSizeArgs", + "type": { + "kind": "struct", + "fields": [ + { + "name": "size", + "type": "u64" + } + ] + } + }, + { + "name": "CreateMetadataAccountArgsV2", + "type": { + "kind": "struct", + "fields": [ + { + "name": "data", + "type": { + "defined": "DataV2" + } + }, + { + "name": "isMutable", + "type": "bool" + } + ] + } + }, + { + "name": "CreateMetadataAccountArgs", + "type": { + "kind": "struct", + "fields": [ + { + "name": "data", + "type": { + "defined": "Data" + } + }, + { + "name": "isMutable", + "type": "bool" + } + ] + } + }, + { + "name": "UpdateMetadataAccountArgs", + "type": { + "kind": "struct", + "fields": [ + { + "name": "data", + "type": { + "option": { + "defined": "Data" + } + } + }, + { + "name": "updateAuthority", + "type": { + "option": "publicKey" + } + }, + { + "name": "primarySaleHappened", + "type": { + "option": "bool" + } + } + ] + } + }, + { + "name": "MintPrintingTokensViaTokenArgs", + "type": { + "kind": "struct", + "fields": [ + { + "name": "supply", + "type": "u64" + } + ] + } + }, { - "name": "CollectionAuthorityRecord", + "name": "SetReservationListArgs", "type": { "kind": "struct", "fields": [ { - "name": "key", + "name": "reservations", "type": { - "defined": "Key" + "vec": { + "defined": "Reservation" + } } }, { - "name": "bump", - "type": "u8" - }, - { - "name": "updateAuthority", + "name": "totalReservationSpots", "type": { - "option": "publicKey" + "option": "u64" } + }, + { + "name": "offset", + "type": "u64" + }, + { + "name": "totalSpotOffset", + "type": "u64" } ] } }, { - "name": "Edition", + "name": "CreateMasterEditionArgs", "type": { "kind": "struct", "fields": [ { - "name": "key", + "name": "maxSupply", "type": { - "defined": "Key" + "option": "u64" } - }, - { - "name": "parent", - "type": "publicKey" - }, + } + ] + } + }, + { + "name": "MintNewEditionFromMasterEditionViaTokenArgs", + "type": { + "kind": "struct", + "fields": [ { "name": "edition", "type": "u64" @@ -2391,135 +4048,144 @@ } }, { - "name": "EditionMarker", + "name": "TransferOutOfEscrowArgs", "type": { "kind": "struct", "fields": [ { - "name": "key", - "type": { - "defined": "Key" - } - }, - { - "name": "ledger", - "type": { - "array": [ - "u8", - 31 - ] - } + "name": "amount", + "type": "u64" } ] } }, { - "name": "TokenOwnedEscrow", + "name": "CreateMetadataAccountArgsV3", "type": { "kind": "struct", "fields": [ { - "name": "key", + "name": "data", "type": { - "defined": "Key" + "defined": "DataV2" } }, { - "name": "baseToken", - "type": "publicKey" + "name": "isMutable", + "type": "bool" }, { - "name": "authority", + "name": "collectionDetails", "type": { - "defined": "EscrowAuthority" + "option": { + "defined": "CollectionDetails" + } } - }, - { - "name": "bump", - "type": "u8" } ] } }, { - "name": "MasterEditionV2", + "name": "UpdateMetadataAccountArgsV2", "type": { "kind": "struct", "fields": [ { - "name": "key", + "name": "data", "type": { - "defined": "Key" + "option": { + "defined": "DataV2" + } } }, { - "name": "supply", - "type": "u64" + "name": "updateAuthority", + "type": { + "option": "publicKey" + } }, { - "name": "maxSupply", + "name": "primarySaleHappened", "type": { - "option": "u64" + "option": "bool" + } + }, + { + "name": "isMutable", + "type": { + "option": "bool" } } ] } }, { - "name": "MasterEditionV1", + "name": "ApproveUseAuthorityArgs", "type": { "kind": "struct", "fields": [ { - "name": "key", - "type": { - "defined": "Key" - } - }, + "name": "numberOfUses", + "type": "u64" + } + ] + } + }, + { + "name": "UtilizeArgs", + "type": { + "kind": "struct", + "fields": [ { - "name": "supply", + "name": "numberOfUses", "type": "u64" - }, + } + ] + } + }, + { + "name": "AuthorizationData", + "type": { + "kind": "struct", + "fields": [ { - "name": "maxSupply", + "name": "payload", "type": { - "option": "u64" + "defined": "Payload" } - }, - { - "name": "printingMint", - "type": "publicKey" - }, - { - "name": "oneTimePrintingAuthorizationMint", - "type": "publicKey" } ] } }, { - "name": "Metadata", + "name": "AssetData", "type": { "kind": "struct", "fields": [ { - "name": "key", - "type": { - "defined": "Key" - } + "name": "name", + "type": "string" }, { - "name": "updateAuthority", - "type": "publicKey" + "name": "symbol", + "type": "string" }, { - "name": "mint", - "type": "publicKey" + "name": "uri", + "type": "string" }, { - "name": "data", + "name": "sellerFeeBasisPoints", + "type": "u16" + }, + { + "name": "creators", "type": { - "defined": "Data" + "option": { + "vec": { + "defined": "Creator" + } + } } }, { @@ -2531,40 +4197,108 @@ "type": "bool" }, { - "name": "editionNonce", + "name": "tokenStandard", "type": { - "option": "u8" + "defined": "TokenStandard" } }, { - "name": "tokenStandard", + "name": "collection", "type": { "option": { - "defined": "TokenStandard" + "defined": "Collection" } } }, { - "name": "collection", + "name": "uses", "type": { "option": { - "defined": "Collection" + "defined": "Uses" } } }, { - "name": "uses", + "name": "collectionDetails", "type": { "option": { - "defined": "Uses" + "defined": "CollectionDetails" } } }, { - "name": "collectionDetails", + "name": "ruleSet", + "type": { + "option": "publicKey" + } + } + ] + } + }, + { + "name": "Collection", + "type": { + "kind": "struct", + "fields": [ + { + "name": "verified", + "type": "bool" + }, + { + "name": "key", + "type": "publicKey" + } + ] + } + }, + { + "name": "Creator", + "type": { + "kind": "struct", + "fields": [ + { + "name": "address", + "type": "publicKey" + }, + { + "name": "verified", + "type": "bool" + }, + { + "name": "share", + "type": "u8" + } + ] + } + }, + { + "name": "Data", + "type": { + "kind": "struct", + "fields": [ + { + "name": "name", + "type": "string" + }, + { + "name": "symbol", + "type": "string" + }, + { + "name": "uri", + "type": "string" + }, + { + "name": "sellerFeeBasisPoints", + "type": "u16" + }, + { + "name": "creators", "type": { "option": { - "defined": "CollectionDetails" + "vec": { + "defined": "Creator" + } } } } @@ -2572,71 +4306,49 @@ } }, { - "name": "ReservationListV2", + "name": "DataV2", "type": { "kind": "struct", "fields": [ { - "name": "key", - "type": { - "defined": "Key" - } - }, - { - "name": "masterEdition", - "type": "publicKey" + "name": "name", + "type": "string" }, { - "name": "supplySnapshot", - "type": { - "option": "u64" - } + "name": "symbol", + "type": "string" }, { - "name": "reservations", - "type": { - "vec": { - "defined": "Reservation" - } - } + "name": "uri", + "type": "string" }, { - "name": "totalReservationSpots", - "type": "u64" + "name": "sellerFeeBasisPoints", + "type": "u16" }, { - "name": "currentReservationSpots", - "type": "u64" - } - ] - } - }, - { - "name": "ReservationListV1", - "type": { - "kind": "struct", - "fields": [ - { - "name": "key", + "name": "creators", "type": { - "defined": "Key" + "option": { + "vec": { + "defined": "Creator" + } + } } }, { - "name": "masterEdition", - "type": "publicKey" - }, - { - "name": "supplySnapshot", + "name": "collection", "type": { - "option": "u64" + "option": { + "defined": "Collection" + } } }, { - "name": "reservations", + "name": "uses", "type": { - "vec": { - "defined": "ReservationV1" + "option": { + "defined": "Uses" } } } @@ -2644,447 +4356,656 @@ } }, { - "name": "UseAuthorityRecord", + "name": "Reservation", "type": { "kind": "struct", "fields": [ { - "name": "key", - "type": { - "defined": "Key" - } + "name": "address", + "type": "publicKey" }, { - "name": "allowedUses", + "name": "spotsRemaining", "type": "u64" }, { - "name": "bump", - "type": "u8" - } - ] - } - } - ], - "types": [ - { - "name": "SetCollectionSizeArgs", - "type": { - "kind": "struct", - "fields": [ - { - "name": "size", + "name": "totalSpots", "type": "u64" } ] } }, { - "name": "CreateMetadataAccountArgsV2", + "name": "ReservationV1", "type": { "kind": "struct", "fields": [ { - "name": "data", - "type": { - "defined": "DataV2" - } + "name": "address", + "type": "publicKey" }, { - "name": "isMutable", - "type": "bool" + "name": "spotsRemaining", + "type": "u8" + }, + { + "name": "totalSpots", + "type": "u8" } ] } }, { - "name": "CreateMetadataAccountArgs", + "name": "SeedsVec", "type": { "kind": "struct", "fields": [ { - "name": "data", + "name": "seeds", "type": { - "defined": "Data" + "vec": "bytes" } - }, - { - "name": "isMutable", - "type": "bool" } ] } }, { - "name": "UpdateMetadataAccountArgs", + "name": "LeafInfo", "type": { "kind": "struct", "fields": [ { - "name": "data", - "type": { - "option": { - "defined": "Data" - } - } - }, - { - "name": "updateAuthority", + "name": "leaf", "type": { - "option": "publicKey" + "array": [ + "u8", + 32 + ] } }, { - "name": "primarySaleHappened", + "name": "proof", "type": { - "option": "bool" + "vec": { + "array": [ + "u8", + 32 + ] + } } } ] } }, { - "name": "MintPrintingTokensViaTokenArgs", + "name": "Payload", "type": { "kind": "struct", "fields": [ { - "name": "supply", - "type": "u64" + "name": "map", + "type": { + "hashMap": [ + { + "defined": "PayloadKey" + }, + { + "defined": "PayloadType" + } + ] + } } ] } }, { - "name": "SetReservationListArgs", + "name": "Uses", "type": { "kind": "struct", "fields": [ { - "name": "reservations", - "type": { - "vec": { - "defined": "Reservation" - } - } - }, - { - "name": "totalReservationSpots", + "name": "useMethod", "type": { - "option": "u64" + "defined": "UseMethod" } }, { - "name": "offset", + "name": "remaining", "type": "u64" }, { - "name": "totalSpotOffset", + "name": "total", "type": "u64" } ] } }, { - "name": "CreateMasterEditionArgs", + "name": "BurnArgs", "type": { - "kind": "struct", - "fields": [ + "kind": "enum", + "variants": [ { - "name": "maxSupply", - "type": { - "option": "u64" - } + "name": "V1", + "fields": [ + { + "name": "authorization_data", + "type": { + "option": { + "defined": "AuthorizationData" + } + } + } + ] } ] } }, { - "name": "MintNewEditionFromMasterEditionViaTokenArgs", + "name": "VerifyArgs", "type": { - "kind": "struct", - "fields": [ + "kind": "enum", + "variants": [ { - "name": "edition", - "type": "u64" + "name": "V1", + "fields": [ + { + "name": "authorization_data", + "type": { + "option": { + "defined": "AuthorizationData" + } + } + } + ] } ] } }, { - "name": "TransferOutOfEscrowArgs", + "name": "DelegateArgs", "type": { - "kind": "struct", - "fields": [ + "kind": "enum", + "variants": [ { - "name": "amount", - "type": "u64" + "name": "CollectionV1", + "fields": [ + { + "name": "authorization_data", + "type": { + "option": { + "defined": "AuthorizationData" + } + } + } + ] + }, + { + "name": "SaleV1", + "fields": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "authorization_data", + "type": { + "option": { + "defined": "AuthorizationData" + } + } + } + ] + }, + { + "name": "TransferV1", + "fields": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "authorization_data", + "type": { + "option": { + "defined": "AuthorizationData" + } + } + } + ] + }, + { + "name": "UpdateV1", + "fields": [ + { + "name": "authorization_data", + "type": { + "option": { + "defined": "AuthorizationData" + } + } + } + ] + }, + { + "name": "UtilityV1", + "fields": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "authorization_data", + "type": { + "option": { + "defined": "AuthorizationData" + } + } + } + ] + }, + { + "name": "StakingV1", + "fields": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "authorization_data", + "type": { + "option": { + "defined": "AuthorizationData" + } + } + } + ] + }, + { + "name": "StandardV1", + "fields": [ + { + "name": "amount", + "type": "u64" + } + ] } ] } }, { - "name": "CreateMetadataAccountArgsV3", + "name": "RevokeArgs", "type": { - "kind": "struct", - "fields": [ + "kind": "enum", + "variants": [ { - "name": "data", - "type": { - "defined": "DataV2" - } + "name": "CollectionV1" }, { - "name": "isMutable", - "type": "bool" + "name": "SaleV1" }, { - "name": "collectionDetails", - "type": { - "option": { - "defined": "CollectionDetails" - } - } + "name": "TransferV1" + }, + { + "name": "UpdateV1" + }, + { + "name": "UtilityV1" + }, + { + "name": "StakingV1" + }, + { + "name": "StandardV1" } ] } }, { - "name": "UpdateMetadataAccountArgsV2", + "name": "MetadataDelegateRole", "type": { - "kind": "struct", - "fields": [ + "kind": "enum", + "variants": [ { - "name": "data", - "type": { - "option": { - "defined": "DataV2" - } - } + "name": "Authority" }, { - "name": "updateAuthority", - "type": { - "option": "publicKey" - } + "name": "Collection" }, { - "name": "primarySaleHappened", - "type": { - "option": "bool" - } + "name": "Use" }, { - "name": "isMutable", - "type": { - "option": "bool" - } + "name": "Update" } ] } }, { - "name": "ApproveUseAuthorityArgs", + "name": "CreateArgs", "type": { - "kind": "struct", - "fields": [ + "kind": "enum", + "variants": [ { - "name": "numberOfUses", - "type": "u64" + "name": "V1", + "fields": [ + { + "name": "asset_data", + "type": { + "defined": "AssetData" + } + }, + { + "name": "decimals", + "type": { + "option": "u8" + } + }, + { + "name": "print_supply", + "type": { + "option": { + "defined": "PrintSupply" + } + } + } + ] } ] } }, { - "name": "UtilizeArgs", + "name": "MintArgs", "type": { - "kind": "struct", - "fields": [ + "kind": "enum", + "variants": [ { - "name": "numberOfUses", - "type": "u64" + "name": "V1", + "fields": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "authorization_data", + "type": { + "option": { + "defined": "AuthorizationData" + } + } + } + ] } ] } }, { - "name": "Collection", + "name": "TransferArgs", "type": { - "kind": "struct", - "fields": [ + "kind": "enum", + "variants": [ { - "name": "verified", - "type": "bool" - }, + "name": "V1", + "fields": [ + { + "name": "amount", + "type": "u64" + }, + { + "name": "authorization_data", + "type": { + "option": { + "defined": "AuthorizationData" + } + } + } + ] + } + ] + } + }, + { + "name": "UpdateArgs", + "type": { + "kind": "enum", + "variants": [ { - "name": "key", - "type": "publicKey" + "name": "V1", + "fields": [ + { + "name": "new_update_authority", + "type": { + "option": "publicKey" + } + }, + { + "name": "data", + "type": { + "option": { + "defined": "Data" + } + } + }, + { + "name": "primary_sale_happened", + "type": { + "option": "bool" + } + }, + { + "name": "is_mutable", + "type": { + "option": "bool" + } + }, + { + "name": "collection", + "type": { + "defined": "CollectionToggle" + } + }, + { + "name": "collection_details", + "type": { + "defined": "CollectionDetailsToggle" + } + }, + { + "name": "uses", + "type": { + "defined": "UsesToggle" + } + }, + { + "name": "rule_set", + "type": { + "defined": "RuleSetToggle" + } + }, + { + "name": "authorization_data", + "type": { + "option": { + "defined": "AuthorizationData" + } + } + } + ] } ] } }, { - "name": "Creator", + "name": "CollectionToggle", "type": { - "kind": "struct", - "fields": [ + "kind": "enum", + "variants": [ { - "name": "address", - "type": "publicKey" + "name": "None" }, { - "name": "verified", - "type": "bool" + "name": "Clear" }, { - "name": "share", - "type": "u8" + "name": "Set", + "fields": [ + { + "defined": "Collection" + } + ] } ] } }, { - "name": "Data", + "name": "UsesToggle", "type": { - "kind": "struct", - "fields": [ - { - "name": "name", - "type": "string" - }, - { - "name": "symbol", - "type": "string" - }, + "kind": "enum", + "variants": [ { - "name": "uri", - "type": "string" + "name": "None" }, { - "name": "sellerFeeBasisPoints", - "type": "u16" + "name": "Clear" }, { - "name": "creators", - "type": { - "option": { - "vec": { - "defined": "Creator" - } + "name": "Set", + "fields": [ + { + "defined": "Uses" } - } + ] } ] } }, { - "name": "DataV2", + "name": "CollectionDetailsToggle", "type": { - "kind": "struct", - "fields": [ + "kind": "enum", + "variants": [ { - "name": "name", - "type": "string" + "name": "None" }, { - "name": "symbol", - "type": "string" + "name": "Clear" }, { - "name": "uri", - "type": "string" - }, + "name": "Set", + "fields": [ + { + "defined": "CollectionDetails" + } + ] + } + ] + } + }, + { + "name": "RuleSetToggle", + "type": { + "kind": "enum", + "variants": [ { - "name": "sellerFeeBasisPoints", - "type": "u16" + "name": "None" }, { - "name": "creators", - "type": { - "option": { - "vec": { - "defined": "Creator" - } - } - } + "name": "Clear" }, { - "name": "collection", - "type": { - "option": { - "defined": "Collection" - } - } - }, + "name": "Set", + "fields": [ + "publicKey" + ] + } + ] + } + }, + { + "name": "MigrateArgs", + "type": { + "kind": "enum", + "variants": [ { - "name": "uses", - "type": { - "option": { - "defined": "Uses" + "name": "V1", + "fields": [ + { + "name": "migration_type", + "type": { + "defined": "MigrationType" + } + }, + { + "name": "rule_set", + "type": { + "option": "publicKey" + } } - } + ] } ] } }, { - "name": "Reservation", + "name": "LockArgs", "type": { - "kind": "struct", - "fields": [ - { - "name": "address", - "type": "publicKey" - }, - { - "name": "spotsRemaining", - "type": "u64" - }, + "kind": "enum", + "variants": [ { - "name": "totalSpots", - "type": "u64" + "name": "V1", + "fields": [ + { + "name": "authorization_data", + "type": { + "option": { + "defined": "AuthorizationData" + } + } + } + ] } ] } }, { - "name": "ReservationV1", + "name": "UnlockArgs", "type": { - "kind": "struct", - "fields": [ - { - "name": "address", - "type": "publicKey" - }, - { - "name": "spotsRemaining", - "type": "u8" - }, + "kind": "enum", + "variants": [ { - "name": "totalSpots", - "type": "u8" + "name": "V1", + "fields": [ + { + "name": "authorization_data", + "type": { + "option": { + "defined": "AuthorizationData" + } + } + } + ] } ] } }, { - "name": "Uses", + "name": "UseArgs", "type": { - "kind": "struct", - "fields": [ - { - "name": "useMethod", - "type": { - "defined": "UseMethod" - } - }, - { - "name": "remaining", - "type": "u64" - }, + "kind": "enum", + "variants": [ { - "name": "total", - "type": "u64" + "name": "V1", + "fields": [ + { + "name": "authorization_data", + "type": { + "option": { + "defined": "AuthorizationData" + } + } + } + ] } ] } @@ -3105,6 +5026,9 @@ }, { "name": "NonFungibleEdition" + }, + { + "name": "ProgrammableNonFungible" } ] } @@ -3146,6 +5070,12 @@ }, { "name": "TokenOwnedEscrow" + }, + { + "name": "TokenRecord" + }, + { + "name": "MetadataDelegate" } ] } @@ -3184,6 +5114,178 @@ ] } }, + { + "name": "PrintSupply", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Zero" + }, + { + "name": "Limited", + "fields": [ + "u64" + ] + }, + { + "name": "Unlimited" + } + ] + } + }, + { + "name": "ProgrammableConfig", + "type": { + "kind": "enum", + "variants": [ + { + "name": "V1", + "fields": [ + { + "name": "rule_set", + "type": { + "option": "publicKey" + } + } + ] + } + ] + } + }, + { + "name": "MigrationType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "CollectionV1" + }, + { + "name": "ProgrammableV1" + } + ] + } + }, + { + "name": "TokenState", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Unlocked" + }, + { + "name": "Locked" + }, + { + "name": "Listed" + } + ] + } + }, + { + "name": "TokenDelegateRole", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Sale" + }, + { + "name": "Transfer" + }, + { + "name": "Utility" + }, + { + "name": "Staking" + }, + { + "name": "Standard" + }, + { + "name": "Migration" + } + ] + } + }, + { + "name": "AuthorityType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "None" + }, + { + "name": "Metadata" + }, + { + "name": "Delegate" + }, + { + "name": "Holder" + } + ] + } + }, + { + "name": "PayloadType", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Pubkey", + "fields": [ + "publicKey" + ] + }, + { + "name": "Seeds", + "fields": [ + { + "defined": "SeedsVec" + } + ] + }, + { + "name": "MerkleProof", + "fields": [ + { + "defined": "LeafInfo" + } + ] + }, + { + "name": "Number", + "fields": [ + "u64" + ] + } + ] + } + }, + { + "name": "PayloadKey", + "type": { + "kind": "enum", + "variants": [ + { + "name": "Target" + }, + { + "name": "Holder" + }, + { + "name": "Authority" + }, + { + "name": "Amount" + } + ] + } + }, { "name": "UseMethod", "type": { @@ -3576,7 +5678,7 @@ { "code": 74, "name": "CollectionCannotBeVerifiedInThisInstruction", - "msg": "Cannont Verify Collection in this Instruction" + "msg": "Collection cannot be verified in this instruction" }, { "code": 75, @@ -3766,7 +5868,7 @@ { "code": 112, "name": "CannotUpdateVerifiedCollection", - "msg": "Cannot update a verified colleciton in this command" + "msg": "Cannot update a verified collection in this command" }, { "code": 113, @@ -3867,6 +5969,186 @@ "code": 132, "name": "InvalidBubblegumSigner", "msg": "Invalid bubblegum signer" + }, + { + "code": 133, + "name": "EscrowParentHasDelegate", + "msg": "Escrow parent cannot have a delegate" + }, + { + "code": 134, + "name": "MintIsNotSigner", + "msg": "Mint needs to be signer to initialize the account" + }, + { + "code": 135, + "name": "InvalidTokenStandard", + "msg": "Invalid token standard" + }, + { + "code": 136, + "name": "InvalidMintForTokenStandard", + "msg": "Invalid mint account for specified token standard" + }, + { + "code": 137, + "name": "InvalidAuthorizationRules", + "msg": "Invalid authorization rules account" + }, + { + "code": 138, + "name": "MissingAuthorizationRules", + "msg": "Missing authorization rules account" + }, + { + "code": 139, + "name": "MissingProgrammableConfig", + "msg": "Missing programmable configuration" + }, + { + "code": 140, + "name": "InvalidProgrammableConfig", + "msg": "Invalid programmable configuration" + }, + { + "code": 141, + "name": "DelegateAlreadyExists", + "msg": "Delegate already exists" + }, + { + "code": 142, + "name": "DelegateNotFound", + "msg": "Delegate not found" + }, + { + "code": 143, + "name": "MissingAccountInBuilder", + "msg": "Required account not set in instruction builder" + }, + { + "code": 144, + "name": "MissingArgumentInBuilder", + "msg": "Required argument not set in instruction builder" + }, + { + "code": 145, + "name": "FeatureNotSupported", + "msg": "Feature not supported currently" + }, + { + "code": 146, + "name": "InvalidSystemWallet", + "msg": "Invalid system wallet" + }, + { + "code": 147, + "name": "OnlySaleDelegateCanTransfer", + "msg": "Only the sale delegate can transfer while its set" + }, + { + "code": 148, + "name": "MissingTokenAccount", + "msg": "Missing token account" + }, + { + "code": 149, + "name": "MissingSplTokenProgram", + "msg": "Missing SPL token program" + }, + { + "code": 150, + "name": "MissingAuthorizationRulesProgram", + "msg": "Missing authorization rules program" + }, + { + "code": 151, + "name": "InvalidDelegateRoleForTransfer", + "msg": "Invalid delegate role for transfer" + }, + { + "code": 152, + "name": "InvalidTransferAuthority", + "msg": "Invalid transfer authority" + }, + { + "code": 153, + "name": "InstructionNotSupported", + "msg": "Instruction not supported for ProgrammableNonFungible assets" + }, + { + "code": 154, + "name": "KeyMismatch", + "msg": "Public key does not match expected value" + }, + { + "code": 155, + "name": "LockedToken", + "msg": "Token is locked" + }, + { + "code": 156, + "name": "UnlockedToken", + "msg": "Token is unlocked" + }, + { + "code": 157, + "name": "MissingDelegateRole", + "msg": "Missing delegate role" + }, + { + "code": 158, + "name": "InvalidAuthorityType", + "msg": "Invalid authority type" + }, + { + "code": 159, + "name": "MissingTokenRecord", + "msg": "Missing token record account" + }, + { + "code": 160, + "name": "MintSupplyMustBeZero", + "msg": "Mint supply must be zero for programmable assets" + }, + { + "code": 161, + "name": "DataIsEmptyOrZeroed", + "msg": "Data is empty or zeroed" + }, + { + "code": 162, + "name": "MissingTokenOwnerAccount", + "msg": "Missing token owner" + }, + { + "code": 163, + "name": "InvalidMasterEditionAccountLength", + "msg": "Master edition account has an invalid length" + }, + { + "code": 164, + "name": "IncorrectTokenState", + "msg": "Incorrect token state" + }, + { + "code": 165, + "name": "InvalidDelegateRole", + "msg": "Invalid delegate role" + }, + { + "code": 166, + "name": "MissingPrintSupply", + "msg": "Print supply is required for non-fungibles" + }, + { + "code": 167, + "name": "MissingMasterEditionAccount", + "msg": "Missing master edition account" + }, + { + "code": 168, + "name": "AmountMustBeGreaterThanZero", + "msg": "Amount must be greater than zero" } ], "metadata": { diff --git a/token-metadata/js/package.json b/token-metadata/js/package.json index 97d5692cec..5e634d2c4d 100644 --- a/token-metadata/js/package.json +++ b/token-metadata/js/package.json @@ -1,6 +1,6 @@ { "name": "@metaplex-foundation/mpl-token-metadata", - "version": "2.5.4", + "version": "2.7.0-beta.2", "contractVersion": "1.6.3", "description": "MPL Token Metadata JavaScript API.", "main": "dist/src/mpl-token-metadata.js", @@ -14,12 +14,14 @@ "build:docs": "typedoc", "build": "rimraf dist && tsc -p tsconfig.json", "api:gen": "DEBUG='(solita|rustbin):(info|error)' solita", - "test": "cpr test/fixtures dist/test/fixtures && tape dist/test/*.js", + "test": "cpr test/fixtures dist/test/fixtures -o && tape dist/test/*.js", "lint": "eslint \"{src,test}/**/*.ts\" --format stylish", "fix:lint": "yarn lint --fix", "prettier": "prettier \"{src,test}/**/*.ts\" --check", "fix:prettier": "prettier --write src/", - "fix": "yarn fix:lint && yarn fix:prettier" + "fix": "yarn fix:lint && yarn fix:prettier", + "amman:start": "amman start", + "amman:stop": "amman stop" }, "files": [ "/dist/src" @@ -48,20 +50,28 @@ "debug": "^4.3.4" }, "devDependencies": { - "@metaplex-foundation/solita": "^0.19.1", + "@metaplex-foundation/amman": "^0.12.0", + "@metaplex-foundation/amman-client": "^0.2.2", + "@metaplex-foundation/solita": "^0.19.3", + "@metaplex-foundation/mpl-token-auth-rules": "^0.2.7", + "@msgpack/msgpack": "^2.8.0", "@types/bn.js": "^5.1.1", "@types/debug": "^4.1.7", "@types/tape": "^4.13.2", "@typescript-eslint/eslint-plugin": "^5.44.0", "@typescript-eslint/parser": "^5.44.0", "cpr": "^3.0.1", + "esbuild": "0.15.12", + "esbuild-runner": "^2.2.1", "eslint": "^8.3.0", "eslint-config-prettier": "^8.5.0", "eslint-plugin-prettier": "^4.2.1", + "prettier": "^2.5.1", "rimraf": "^3.0.2", "spok": "^1.4.3", "supports-color": "^9.2.3", "tape": "^5.5.3", + "typedoc": "^0.23.16", "typescript": "^4.9.3" } } diff --git a/token-metadata/js/run-test.sh b/token-metadata/js/run-test.sh new file mode 100755 index 0000000000..4d16e9a71e --- /dev/null +++ b/token-metadata/js/run-test.sh @@ -0,0 +1,40 @@ +#!/bin/bash +# +# To run this script, you need: +# - npm install -g esbuild-runner +# - npm install -g tap-spec + +# error output colour +RED() { echo $'\e[1;31m'$1$'\e[0m'; } +RUN_ALL=0 + +# check whether we are running all test files or not + +while getopts a-: optchar; do + case "${optchar}" in + a) + RUN_ALL=1 ;; + -) + case "${OPTARG}" in + all) RUN_ALL=1 ;; + *) ;; + esac ;; + *) ;; + esac +done + +# runs single or multiple tests + +if [ $RUN_ALL -eq 1 ]; then + for file in `ls test/*.test.ts` + do + esr $file | tap-spec + done +else + if [ ! -z "$1" ] && [[ -f "$1" ]]; then + esr $1 | tap-spec + else + echo "$(RED "Error: ")Please specify a test file or [-a | --all] to run all tests" + exit 1 + fi +fi diff --git a/token-metadata/js/src/custom/metadata-deserializer.ts b/token-metadata/js/src/custom/metadata-deserializer.ts index 0d44e13bc3..67edd54a07 100644 --- a/token-metadata/js/src/custom/metadata-deserializer.ts +++ b/token-metadata/js/src/custom/metadata-deserializer.ts @@ -3,6 +3,7 @@ import * as beetSolana from '@metaplex-foundation/beet-solana'; import { Metadata } from '../generated/accounts/Metadata'; import { collectionBeet } from '../generated/types/Collection'; import { collectionDetailsBeet } from '../generated/types/CollectionDetails'; +import { programmableConfigBeet } from '../generated/types/ProgrammableConfig'; import { dataBeet } from '../generated/types/Data'; import { keyBeet } from '../generated/types/Key'; import { tokenStandardBeet } from '../generated/types/TokenStandard'; @@ -87,8 +88,19 @@ export function deserialize(buf: Buffer, offset = 0): [Metadata, number] { : tryReadOption(beet.coption(collectionDetailsBeet), buf, cursor); cursor += collectionDetailsDelta; + // programmable_config + const [programmableConfig, programmableConfigDelta, programmableConfigCorrupted] = + tokenCorrupted || collectionCorrupted || usesCorrupted + ? [null, NONE_BYTE_SIZE, true] + : tryReadOption(beet.coption(programmableConfigBeet), buf, cursor); + cursor += programmableConfigDelta; + const anyCorrupted = - tokenCorrupted || collectionCorrupted || usesCorrupted || collectionDetailsCorrupted; + tokenCorrupted || + collectionCorrupted || + usesCorrupted || + collectionDetailsCorrupted || + programmableConfigCorrupted; const args = { key, @@ -102,6 +114,7 @@ export function deserialize(buf: Buffer, offset = 0): [Metadata, number] { collection: anyCorrupted ? null : collection, uses: anyCorrupted ? null : uses, collectionDetails: anyCorrupted ? null : collectionDetails, + programmableConfig: anyCorrupted ? null : programmableConfig, }; return [Metadata.fromArgs(args), cursor]; diff --git a/token-metadata/js/src/generated/accounts/Metadata.ts b/token-metadata/js/src/generated/accounts/Metadata.ts index ff53bc61ce..fa4824e90a 100644 --- a/token-metadata/js/src/generated/accounts/Metadata.ts +++ b/token-metadata/js/src/generated/accounts/Metadata.ts @@ -14,6 +14,7 @@ import { TokenStandard, tokenStandardBeet } from '../types/TokenStandard'; import { Collection, collectionBeet } from '../types/Collection'; import { Uses, usesBeet } from '../types/Uses'; import { CollectionDetails, collectionDetailsBeet } from '../types/CollectionDetails'; +import { ProgrammableConfig, programmableConfigBeet } from '../types/ProgrammableConfig'; import * as customSerializer from '../../custom/metadata-deserializer'; /** @@ -33,6 +34,7 @@ export type MetadataArgs = { collection: beet.COption; uses: beet.COption; collectionDetails: beet.COption; + programmableConfig: beet.COption; }; /** * Holds the data for the {@link Metadata} Account and provides de/serialization @@ -54,6 +56,7 @@ export class Metadata implements MetadataArgs { readonly collection: beet.COption, readonly uses: beet.COption, readonly collectionDetails: beet.COption, + readonly programmableConfig: beet.COption, ) {} /** @@ -72,6 +75,7 @@ export class Metadata implements MetadataArgs { args.collection, args.uses, args.collectionDetails, + args.programmableConfig, ); } @@ -174,6 +178,7 @@ export class Metadata implements MetadataArgs { collection: this.collection, uses: this.uses, collectionDetails: this.collectionDetails, + programmableConfig: this.programmableConfig, }; } } @@ -195,6 +200,7 @@ export const metadataBeet = new beet.FixableBeetStruct( ['collection', beet.coption(collectionBeet)], ['uses', beet.coption(usesBeet)], ['collectionDetails', beet.coption(collectionDetailsBeet)], + ['programmableConfig', beet.coption(programmableConfigBeet)], ], Metadata.fromArgs, 'Metadata', diff --git a/token-metadata/js/src/generated/accounts/MetadataDelegateRecord.ts b/token-metadata/js/src/generated/accounts/MetadataDelegateRecord.ts new file mode 100644 index 0000000000..39bd7c077e --- /dev/null +++ b/token-metadata/js/src/generated/accounts/MetadataDelegateRecord.ts @@ -0,0 +1,175 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as web3 from '@solana/web3.js'; +import * as beet from '@metaplex-foundation/beet'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; +import { Key, keyBeet } from '../types/Key'; + +/** + * Arguments used to create {@link MetadataDelegateRecord} + * @category Accounts + * @category generated + */ +export type MetadataDelegateRecordArgs = { + key: Key; + bump: number; + mint: web3.PublicKey; + delegate: web3.PublicKey; + updateAuthority: web3.PublicKey; +}; +/** + * Holds the data for the {@link MetadataDelegateRecord} Account and provides de/serialization + * functionality for that data + * + * @category Accounts + * @category generated + */ +export class MetadataDelegateRecord implements MetadataDelegateRecordArgs { + private constructor( + readonly key: Key, + readonly bump: number, + readonly mint: web3.PublicKey, + readonly delegate: web3.PublicKey, + readonly updateAuthority: web3.PublicKey, + ) {} + + /** + * Creates a {@link MetadataDelegateRecord} instance from the provided args. + */ + static fromArgs(args: MetadataDelegateRecordArgs) { + return new MetadataDelegateRecord( + args.key, + args.bump, + args.mint, + args.delegate, + args.updateAuthority, + ); + } + + /** + * Deserializes the {@link MetadataDelegateRecord} from the data of the provided {@link web3.AccountInfo}. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static fromAccountInfo( + accountInfo: web3.AccountInfo, + offset = 0, + ): [MetadataDelegateRecord, number] { + return MetadataDelegateRecord.deserialize(accountInfo.data, offset); + } + + /** + * Retrieves the account info from the provided address and deserializes + * the {@link MetadataDelegateRecord} from its data. + * + * @throws Error if no account info is found at the address or if deserialization fails + */ + static async fromAccountAddress( + connection: web3.Connection, + address: web3.PublicKey, + commitmentOrConfig?: web3.Commitment | web3.GetAccountInfoConfig, + ): Promise { + const accountInfo = await connection.getAccountInfo(address, commitmentOrConfig); + if (accountInfo == null) { + throw new Error(`Unable to find MetadataDelegateRecord account at ${address}`); + } + return MetadataDelegateRecord.fromAccountInfo(accountInfo, 0)[0]; + } + + /** + * Provides a {@link web3.Connection.getProgramAccounts} config builder, + * to fetch accounts matching filters that can be specified via that builder. + * + * @param programId - the program that owns the accounts we are filtering + */ + static gpaBuilder( + programId: web3.PublicKey = new web3.PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'), + ) { + return beetSolana.GpaBuilder.fromStruct(programId, metadataDelegateRecordBeet); + } + + /** + * Deserializes the {@link MetadataDelegateRecord} from the provided data Buffer. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static deserialize(buf: Buffer, offset = 0): [MetadataDelegateRecord, number] { + return metadataDelegateRecordBeet.deserialize(buf, offset); + } + + /** + * Serializes the {@link MetadataDelegateRecord} into a Buffer. + * @returns a tuple of the created Buffer and the offset up to which the buffer was written to store it. + */ + serialize(): [Buffer, number] { + return metadataDelegateRecordBeet.serialize(this); + } + + /** + * Returns the byteSize of a {@link Buffer} holding the serialized data of + * {@link MetadataDelegateRecord} + */ + static get byteSize() { + return metadataDelegateRecordBeet.byteSize; + } + + /** + * Fetches the minimum balance needed to exempt an account holding + * {@link MetadataDelegateRecord} data from rent + * + * @param connection used to retrieve the rent exemption information + */ + static async getMinimumBalanceForRentExemption( + connection: web3.Connection, + commitment?: web3.Commitment, + ): Promise { + return connection.getMinimumBalanceForRentExemption( + MetadataDelegateRecord.byteSize, + commitment, + ); + } + + /** + * Determines if the provided {@link Buffer} has the correct byte size to + * hold {@link MetadataDelegateRecord} data. + */ + static hasCorrectByteSize(buf: Buffer, offset = 0) { + return buf.byteLength - offset === MetadataDelegateRecord.byteSize; + } + + /** + * Returns a readable version of {@link MetadataDelegateRecord} properties + * and can be used to convert to JSON and/or logging + */ + pretty() { + return { + key: 'Key.' + Key[this.key], + bump: this.bump, + mint: this.mint.toBase58(), + delegate: this.delegate.toBase58(), + updateAuthority: this.updateAuthority.toBase58(), + }; + } +} + +/** + * @category Accounts + * @category generated + */ +export const metadataDelegateRecordBeet = new beet.BeetStruct< + MetadataDelegateRecord, + MetadataDelegateRecordArgs +>( + [ + ['key', keyBeet], + ['bump', beet.u8], + ['mint', beetSolana.publicKey], + ['delegate', beetSolana.publicKey], + ['updateAuthority', beetSolana.publicKey], + ], + MetadataDelegateRecord.fromArgs, + 'MetadataDelegateRecord', +); diff --git a/token-metadata/js/src/generated/accounts/TokenRecord.ts b/token-metadata/js/src/generated/accounts/TokenRecord.ts new file mode 100644 index 0000000000..b276c4c2fd --- /dev/null +++ b/token-metadata/js/src/generated/accounts/TokenRecord.ts @@ -0,0 +1,172 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; +import { Key, keyBeet } from '../types/Key'; +import { TokenState, tokenStateBeet } from '../types/TokenState'; +import { TokenDelegateRole, tokenDelegateRoleBeet } from '../types/TokenDelegateRole'; + +/** + * Arguments used to create {@link TokenRecord} + * @category Accounts + * @category generated + */ +export type TokenRecordArgs = { + key: Key; + bump: number; + state: TokenState; + ruleSetRevision: beet.COption; + delegate: beet.COption; + delegateRole: beet.COption; +}; +/** + * Holds the data for the {@link TokenRecord} Account and provides de/serialization + * functionality for that data + * + * @category Accounts + * @category generated + */ +export class TokenRecord implements TokenRecordArgs { + private constructor( + readonly key: Key, + readonly bump: number, + readonly state: TokenState, + readonly ruleSetRevision: beet.COption, + readonly delegate: beet.COption, + readonly delegateRole: beet.COption, + ) {} + + /** + * Creates a {@link TokenRecord} instance from the provided args. + */ + static fromArgs(args: TokenRecordArgs) { + return new TokenRecord( + args.key, + args.bump, + args.state, + args.ruleSetRevision, + args.delegate, + args.delegateRole, + ); + } + + /** + * Deserializes the {@link TokenRecord} from the data of the provided {@link web3.AccountInfo}. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static fromAccountInfo(accountInfo: web3.AccountInfo, offset = 0): [TokenRecord, number] { + return TokenRecord.deserialize(accountInfo.data, offset); + } + + /** + * Retrieves the account info from the provided address and deserializes + * the {@link TokenRecord} from its data. + * + * @throws Error if no account info is found at the address or if deserialization fails + */ + static async fromAccountAddress( + connection: web3.Connection, + address: web3.PublicKey, + commitmentOrConfig?: web3.Commitment | web3.GetAccountInfoConfig, + ): Promise { + const accountInfo = await connection.getAccountInfo(address, commitmentOrConfig); + if (accountInfo == null) { + throw new Error(`Unable to find TokenRecord account at ${address}`); + } + return TokenRecord.fromAccountInfo(accountInfo, 0)[0]; + } + + /** + * Provides a {@link web3.Connection.getProgramAccounts} config builder, + * to fetch accounts matching filters that can be specified via that builder. + * + * @param programId - the program that owns the accounts we are filtering + */ + static gpaBuilder( + programId: web3.PublicKey = new web3.PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'), + ) { + return beetSolana.GpaBuilder.fromStruct(programId, tokenRecordBeet); + } + + /** + * Deserializes the {@link TokenRecord} from the provided data Buffer. + * @returns a tuple of the account data and the offset up to which the buffer was read to obtain it. + */ + static deserialize(buf: Buffer, offset = 0): [TokenRecord, number] { + return tokenRecordBeet.deserialize(buf, offset); + } + + /** + * Serializes the {@link TokenRecord} into a Buffer. + * @returns a tuple of the created Buffer and the offset up to which the buffer was written to store it. + */ + serialize(): [Buffer, number] { + return tokenRecordBeet.serialize(this); + } + + /** + * Returns the byteSize of a {@link Buffer} holding the serialized data of + * {@link TokenRecord} for the provided args. + * + * @param args need to be provided since the byte size for this account + * depends on them + */ + static byteSize(args: TokenRecordArgs) { + const instance = TokenRecord.fromArgs(args); + return tokenRecordBeet.toFixedFromValue(instance).byteSize; + } + + /** + * Fetches the minimum balance needed to exempt an account holding + * {@link TokenRecord} data from rent + * + * @param args need to be provided since the byte size for this account + * depends on them + * @param connection used to retrieve the rent exemption information + */ + static async getMinimumBalanceForRentExemption( + args: TokenRecordArgs, + connection: web3.Connection, + commitment?: web3.Commitment, + ): Promise { + return connection.getMinimumBalanceForRentExemption(TokenRecord.byteSize(args), commitment); + } + + /** + * Returns a readable version of {@link TokenRecord} properties + * and can be used to convert to JSON and/or logging + */ + pretty() { + return { + key: 'Key.' + Key[this.key], + bump: this.bump, + state: 'TokenState.' + TokenState[this.state], + ruleSetRevision: this.ruleSetRevision, + delegate: this.delegate, + delegateRole: this.delegateRole, + }; + } +} + +/** + * @category Accounts + * @category generated + */ +export const tokenRecordBeet = new beet.FixableBeetStruct( + [ + ['key', keyBeet], + ['bump', beet.u8], + ['state', tokenStateBeet], + ['ruleSetRevision', beet.coption(beet.u64)], + ['delegate', beet.coption(beetSolana.publicKey)], + ['delegateRole', beet.coption(tokenDelegateRoleBeet)], + ], + TokenRecord.fromArgs, + 'TokenRecord', +); diff --git a/token-metadata/js/src/generated/accounts/index.ts b/token-metadata/js/src/generated/accounts/index.ts index 007e882244..b686943284 100644 --- a/token-metadata/js/src/generated/accounts/index.ts +++ b/token-metadata/js/src/generated/accounts/index.ts @@ -4,30 +4,36 @@ export * from './EditionMarker'; export * from './MasterEditionV1'; export * from './MasterEditionV2'; export * from './Metadata'; +export * from './MetadataDelegateRecord'; export * from './ReservationListV1'; export * from './ReservationListV2'; export * from './TokenOwnedEscrow'; +export * from './TokenRecord'; export * from './UseAuthorityRecord'; import { CollectionAuthorityRecord } from './CollectionAuthorityRecord'; +import { MetadataDelegateRecord } from './MetadataDelegateRecord'; import { Edition } from './Edition'; import { EditionMarker } from './EditionMarker'; import { TokenOwnedEscrow } from './TokenOwnedEscrow'; import { MasterEditionV2 } from './MasterEditionV2'; import { MasterEditionV1 } from './MasterEditionV1'; import { Metadata } from './Metadata'; +import { TokenRecord } from './TokenRecord'; import { ReservationListV2 } from './ReservationListV2'; import { ReservationListV1 } from './ReservationListV1'; import { UseAuthorityRecord } from './UseAuthorityRecord'; export const accountProviders = { CollectionAuthorityRecord, + MetadataDelegateRecord, Edition, EditionMarker, TokenOwnedEscrow, MasterEditionV2, MasterEditionV1, Metadata, + TokenRecord, ReservationListV2, ReservationListV1, UseAuthorityRecord, diff --git a/token-metadata/js/src/generated/errors/index.ts b/token-metadata/js/src/generated/errors/index.ts index 83e1b5779a..5cdf6b5549 100644 --- a/token-metadata/js/src/generated/errors/index.ts +++ b/token-metadata/js/src/generated/errors/index.ts @@ -1594,7 +1594,7 @@ createErrorFromNameLookup.set( ); /** - * CollectionCannotBeVerifiedInThisInstruction: 'Cannont Verify Collection in this Instruction' + * CollectionCannotBeVerifiedInThisInstruction: 'Collection cannot be verified in this instruction' * * @category Errors * @category generated @@ -1603,7 +1603,7 @@ export class CollectionCannotBeVerifiedInThisInstructionError extends Error { readonly code: number = 0x4a; readonly name: string = 'CollectionCannotBeVerifiedInThisInstruction'; constructor() { - super('Cannont Verify Collection in this Instruction'); + super('Collection cannot be verified in this instruction'); if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, CollectionCannotBeVerifiedInThisInstructionError); } @@ -2417,7 +2417,7 @@ createErrorFromNameLookup.set( ); /** - * CannotUpdateVerifiedCollection: 'Cannot update a verified colleciton in this command' + * CannotUpdateVerifiedCollection: 'Cannot update a verified collection in this command' * * @category Errors * @category generated @@ -2426,7 +2426,7 @@ export class CannotUpdateVerifiedCollectionError extends Error { readonly code: number = 0x70; readonly name: string = 'CannotUpdateVerifiedCollection'; constructor() { - super('Cannot update a verified colleciton in this command'); + super('Cannot update a verified collection in this command'); if (typeof Error.captureStackTrace === 'function') { Error.captureStackTrace(this, CannotUpdateVerifiedCollectionError); } @@ -2854,6 +2854,768 @@ export class InvalidBubblegumSignerError extends Error { createErrorFromCodeLookup.set(0x84, () => new InvalidBubblegumSignerError()); createErrorFromNameLookup.set('InvalidBubblegumSigner', () => new InvalidBubblegumSignerError()); +/** + * EscrowParentHasDelegate: 'Escrow parent cannot have a delegate' + * + * @category Errors + * @category generated + */ +export class EscrowParentHasDelegateError extends Error { + readonly code: number = 0x85; + readonly name: string = 'EscrowParentHasDelegate'; + constructor() { + super('Escrow parent cannot have a delegate'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, EscrowParentHasDelegateError); + } + } +} + +createErrorFromCodeLookup.set(0x85, () => new EscrowParentHasDelegateError()); +createErrorFromNameLookup.set('EscrowParentHasDelegate', () => new EscrowParentHasDelegateError()); + +/** + * MintIsNotSigner: 'Mint needs to be signer to initialize the account' + * + * @category Errors + * @category generated + */ +export class MintIsNotSignerError extends Error { + readonly code: number = 0x86; + readonly name: string = 'MintIsNotSigner'; + constructor() { + super('Mint needs to be signer to initialize the account'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, MintIsNotSignerError); + } + } +} + +createErrorFromCodeLookup.set(0x86, () => new MintIsNotSignerError()); +createErrorFromNameLookup.set('MintIsNotSigner', () => new MintIsNotSignerError()); + +/** + * InvalidTokenStandard: 'Invalid token standard' + * + * @category Errors + * @category generated + */ +export class InvalidTokenStandardError extends Error { + readonly code: number = 0x87; + readonly name: string = 'InvalidTokenStandard'; + constructor() { + super('Invalid token standard'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, InvalidTokenStandardError); + } + } +} + +createErrorFromCodeLookup.set(0x87, () => new InvalidTokenStandardError()); +createErrorFromNameLookup.set('InvalidTokenStandard', () => new InvalidTokenStandardError()); + +/** + * InvalidMintForTokenStandard: 'Invalid mint account for specified token standard' + * + * @category Errors + * @category generated + */ +export class InvalidMintForTokenStandardError extends Error { + readonly code: number = 0x88; + readonly name: string = 'InvalidMintForTokenStandard'; + constructor() { + super('Invalid mint account for specified token standard'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, InvalidMintForTokenStandardError); + } + } +} + +createErrorFromCodeLookup.set(0x88, () => new InvalidMintForTokenStandardError()); +createErrorFromNameLookup.set( + 'InvalidMintForTokenStandard', + () => new InvalidMintForTokenStandardError(), +); + +/** + * InvalidAuthorizationRules: 'Invalid authorization rules account' + * + * @category Errors + * @category generated + */ +export class InvalidAuthorizationRulesError extends Error { + readonly code: number = 0x89; + readonly name: string = 'InvalidAuthorizationRules'; + constructor() { + super('Invalid authorization rules account'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, InvalidAuthorizationRulesError); + } + } +} + +createErrorFromCodeLookup.set(0x89, () => new InvalidAuthorizationRulesError()); +createErrorFromNameLookup.set( + 'InvalidAuthorizationRules', + () => new InvalidAuthorizationRulesError(), +); + +/** + * MissingAuthorizationRules: 'Missing authorization rules account' + * + * @category Errors + * @category generated + */ +export class MissingAuthorizationRulesError extends Error { + readonly code: number = 0x8a; + readonly name: string = 'MissingAuthorizationRules'; + constructor() { + super('Missing authorization rules account'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, MissingAuthorizationRulesError); + } + } +} + +createErrorFromCodeLookup.set(0x8a, () => new MissingAuthorizationRulesError()); +createErrorFromNameLookup.set( + 'MissingAuthorizationRules', + () => new MissingAuthorizationRulesError(), +); + +/** + * MissingProgrammableConfig: 'Missing programmable configuration' + * + * @category Errors + * @category generated + */ +export class MissingProgrammableConfigError extends Error { + readonly code: number = 0x8b; + readonly name: string = 'MissingProgrammableConfig'; + constructor() { + super('Missing programmable configuration'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, MissingProgrammableConfigError); + } + } +} + +createErrorFromCodeLookup.set(0x8b, () => new MissingProgrammableConfigError()); +createErrorFromNameLookup.set( + 'MissingProgrammableConfig', + () => new MissingProgrammableConfigError(), +); + +/** + * InvalidProgrammableConfig: 'Invalid programmable configuration' + * + * @category Errors + * @category generated + */ +export class InvalidProgrammableConfigError extends Error { + readonly code: number = 0x8c; + readonly name: string = 'InvalidProgrammableConfig'; + constructor() { + super('Invalid programmable configuration'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, InvalidProgrammableConfigError); + } + } +} + +createErrorFromCodeLookup.set(0x8c, () => new InvalidProgrammableConfigError()); +createErrorFromNameLookup.set( + 'InvalidProgrammableConfig', + () => new InvalidProgrammableConfigError(), +); + +/** + * DelegateAlreadyExists: 'Delegate already exists' + * + * @category Errors + * @category generated + */ +export class DelegateAlreadyExistsError extends Error { + readonly code: number = 0x8d; + readonly name: string = 'DelegateAlreadyExists'; + constructor() { + super('Delegate already exists'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, DelegateAlreadyExistsError); + } + } +} + +createErrorFromCodeLookup.set(0x8d, () => new DelegateAlreadyExistsError()); +createErrorFromNameLookup.set('DelegateAlreadyExists', () => new DelegateAlreadyExistsError()); + +/** + * DelegateNotFound: 'Delegate not found' + * + * @category Errors + * @category generated + */ +export class DelegateNotFoundError extends Error { + readonly code: number = 0x8e; + readonly name: string = 'DelegateNotFound'; + constructor() { + super('Delegate not found'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, DelegateNotFoundError); + } + } +} + +createErrorFromCodeLookup.set(0x8e, () => new DelegateNotFoundError()); +createErrorFromNameLookup.set('DelegateNotFound', () => new DelegateNotFoundError()); + +/** + * MissingAccountInBuilder: 'Required account not set in instruction builder' + * + * @category Errors + * @category generated + */ +export class MissingAccountInBuilderError extends Error { + readonly code: number = 0x8f; + readonly name: string = 'MissingAccountInBuilder'; + constructor() { + super('Required account not set in instruction builder'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, MissingAccountInBuilderError); + } + } +} + +createErrorFromCodeLookup.set(0x8f, () => new MissingAccountInBuilderError()); +createErrorFromNameLookup.set('MissingAccountInBuilder', () => new MissingAccountInBuilderError()); + +/** + * MissingArgumentInBuilder: 'Required argument not set in instruction builder' + * + * @category Errors + * @category generated + */ +export class MissingArgumentInBuilderError extends Error { + readonly code: number = 0x90; + readonly name: string = 'MissingArgumentInBuilder'; + constructor() { + super('Required argument not set in instruction builder'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, MissingArgumentInBuilderError); + } + } +} + +createErrorFromCodeLookup.set(0x90, () => new MissingArgumentInBuilderError()); +createErrorFromNameLookup.set( + 'MissingArgumentInBuilder', + () => new MissingArgumentInBuilderError(), +); + +/** + * FeatureNotSupported: 'Feature not supported currently' + * + * @category Errors + * @category generated + */ +export class FeatureNotSupportedError extends Error { + readonly code: number = 0x91; + readonly name: string = 'FeatureNotSupported'; + constructor() { + super('Feature not supported currently'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, FeatureNotSupportedError); + } + } +} + +createErrorFromCodeLookup.set(0x91, () => new FeatureNotSupportedError()); +createErrorFromNameLookup.set('FeatureNotSupported', () => new FeatureNotSupportedError()); + +/** + * InvalidSystemWallet: 'Invalid system wallet' + * + * @category Errors + * @category generated + */ +export class InvalidSystemWalletError extends Error { + readonly code: number = 0x92; + readonly name: string = 'InvalidSystemWallet'; + constructor() { + super('Invalid system wallet'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, InvalidSystemWalletError); + } + } +} + +createErrorFromCodeLookup.set(0x92, () => new InvalidSystemWalletError()); +createErrorFromNameLookup.set('InvalidSystemWallet', () => new InvalidSystemWalletError()); + +/** + * OnlySaleDelegateCanTransfer: 'Only the sale delegate can transfer while its set' + * + * @category Errors + * @category generated + */ +export class OnlySaleDelegateCanTransferError extends Error { + readonly code: number = 0x93; + readonly name: string = 'OnlySaleDelegateCanTransfer'; + constructor() { + super('Only the sale delegate can transfer while its set'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, OnlySaleDelegateCanTransferError); + } + } +} + +createErrorFromCodeLookup.set(0x93, () => new OnlySaleDelegateCanTransferError()); +createErrorFromNameLookup.set( + 'OnlySaleDelegateCanTransfer', + () => new OnlySaleDelegateCanTransferError(), +); + +/** + * MissingTokenAccount: 'Missing token account' + * + * @category Errors + * @category generated + */ +export class MissingTokenAccountError extends Error { + readonly code: number = 0x94; + readonly name: string = 'MissingTokenAccount'; + constructor() { + super('Missing token account'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, MissingTokenAccountError); + } + } +} + +createErrorFromCodeLookup.set(0x94, () => new MissingTokenAccountError()); +createErrorFromNameLookup.set('MissingTokenAccount', () => new MissingTokenAccountError()); + +/** + * MissingSplTokenProgram: 'Missing SPL token program' + * + * @category Errors + * @category generated + */ +export class MissingSplTokenProgramError extends Error { + readonly code: number = 0x95; + readonly name: string = 'MissingSplTokenProgram'; + constructor() { + super('Missing SPL token program'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, MissingSplTokenProgramError); + } + } +} + +createErrorFromCodeLookup.set(0x95, () => new MissingSplTokenProgramError()); +createErrorFromNameLookup.set('MissingSplTokenProgram', () => new MissingSplTokenProgramError()); + +/** + * MissingAuthorizationRulesProgram: 'Missing authorization rules program' + * + * @category Errors + * @category generated + */ +export class MissingAuthorizationRulesProgramError extends Error { + readonly code: number = 0x96; + readonly name: string = 'MissingAuthorizationRulesProgram'; + constructor() { + super('Missing authorization rules program'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, MissingAuthorizationRulesProgramError); + } + } +} + +createErrorFromCodeLookup.set(0x96, () => new MissingAuthorizationRulesProgramError()); +createErrorFromNameLookup.set( + 'MissingAuthorizationRulesProgram', + () => new MissingAuthorizationRulesProgramError(), +); + +/** + * InvalidDelegateRoleForTransfer: 'Invalid delegate role for transfer' + * + * @category Errors + * @category generated + */ +export class InvalidDelegateRoleForTransferError extends Error { + readonly code: number = 0x97; + readonly name: string = 'InvalidDelegateRoleForTransfer'; + constructor() { + super('Invalid delegate role for transfer'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, InvalidDelegateRoleForTransferError); + } + } +} + +createErrorFromCodeLookup.set(0x97, () => new InvalidDelegateRoleForTransferError()); +createErrorFromNameLookup.set( + 'InvalidDelegateRoleForTransfer', + () => new InvalidDelegateRoleForTransferError(), +); + +/** + * InvalidTransferAuthority: 'Invalid transfer authority' + * + * @category Errors + * @category generated + */ +export class InvalidTransferAuthorityError extends Error { + readonly code: number = 0x98; + readonly name: string = 'InvalidTransferAuthority'; + constructor() { + super('Invalid transfer authority'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, InvalidTransferAuthorityError); + } + } +} + +createErrorFromCodeLookup.set(0x98, () => new InvalidTransferAuthorityError()); +createErrorFromNameLookup.set( + 'InvalidTransferAuthority', + () => new InvalidTransferAuthorityError(), +); + +/** + * InstructionNotSupported: 'Instruction not supported for ProgrammableNonFungible assets' + * + * @category Errors + * @category generated + */ +export class InstructionNotSupportedError extends Error { + readonly code: number = 0x99; + readonly name: string = 'InstructionNotSupported'; + constructor() { + super('Instruction not supported for ProgrammableNonFungible assets'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, InstructionNotSupportedError); + } + } +} + +createErrorFromCodeLookup.set(0x99, () => new InstructionNotSupportedError()); +createErrorFromNameLookup.set('InstructionNotSupported', () => new InstructionNotSupportedError()); + +/** + * KeyMismatch: 'Public key does not match expected value' + * + * @category Errors + * @category generated + */ +export class KeyMismatchError extends Error { + readonly code: number = 0x9a; + readonly name: string = 'KeyMismatch'; + constructor() { + super('Public key does not match expected value'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, KeyMismatchError); + } + } +} + +createErrorFromCodeLookup.set(0x9a, () => new KeyMismatchError()); +createErrorFromNameLookup.set('KeyMismatch', () => new KeyMismatchError()); + +/** + * LockedToken: 'Token is locked' + * + * @category Errors + * @category generated + */ +export class LockedTokenError extends Error { + readonly code: number = 0x9b; + readonly name: string = 'LockedToken'; + constructor() { + super('Token is locked'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, LockedTokenError); + } + } +} + +createErrorFromCodeLookup.set(0x9b, () => new LockedTokenError()); +createErrorFromNameLookup.set('LockedToken', () => new LockedTokenError()); + +/** + * UnlockedToken: 'Token is unlocked' + * + * @category Errors + * @category generated + */ +export class UnlockedTokenError extends Error { + readonly code: number = 0x9c; + readonly name: string = 'UnlockedToken'; + constructor() { + super('Token is unlocked'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, UnlockedTokenError); + } + } +} + +createErrorFromCodeLookup.set(0x9c, () => new UnlockedTokenError()); +createErrorFromNameLookup.set('UnlockedToken', () => new UnlockedTokenError()); + +/** + * MissingDelegateRole: 'Missing delegate role' + * + * @category Errors + * @category generated + */ +export class MissingDelegateRoleError extends Error { + readonly code: number = 0x9d; + readonly name: string = 'MissingDelegateRole'; + constructor() { + super('Missing delegate role'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, MissingDelegateRoleError); + } + } +} + +createErrorFromCodeLookup.set(0x9d, () => new MissingDelegateRoleError()); +createErrorFromNameLookup.set('MissingDelegateRole', () => new MissingDelegateRoleError()); + +/** + * InvalidAuthorityType: 'Invalid authority type' + * + * @category Errors + * @category generated + */ +export class InvalidAuthorityTypeError extends Error { + readonly code: number = 0x9e; + readonly name: string = 'InvalidAuthorityType'; + constructor() { + super('Invalid authority type'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, InvalidAuthorityTypeError); + } + } +} + +createErrorFromCodeLookup.set(0x9e, () => new InvalidAuthorityTypeError()); +createErrorFromNameLookup.set('InvalidAuthorityType', () => new InvalidAuthorityTypeError()); + +/** + * MissingTokenRecord: 'Missing token record account' + * + * @category Errors + * @category generated + */ +export class MissingTokenRecordError extends Error { + readonly code: number = 0x9f; + readonly name: string = 'MissingTokenRecord'; + constructor() { + super('Missing token record account'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, MissingTokenRecordError); + } + } +} + +createErrorFromCodeLookup.set(0x9f, () => new MissingTokenRecordError()); +createErrorFromNameLookup.set('MissingTokenRecord', () => new MissingTokenRecordError()); + +/** + * MintSupplyMustBeZero: 'Mint supply must be zero for programmable assets' + * + * @category Errors + * @category generated + */ +export class MintSupplyMustBeZeroError extends Error { + readonly code: number = 0xa0; + readonly name: string = 'MintSupplyMustBeZero'; + constructor() { + super('Mint supply must be zero for programmable assets'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, MintSupplyMustBeZeroError); + } + } +} + +createErrorFromCodeLookup.set(0xa0, () => new MintSupplyMustBeZeroError()); +createErrorFromNameLookup.set('MintSupplyMustBeZero', () => new MintSupplyMustBeZeroError()); + +/** + * DataIsEmptyOrZeroed: 'Data is empty or zeroed' + * + * @category Errors + * @category generated + */ +export class DataIsEmptyOrZeroedError extends Error { + readonly code: number = 0xa1; + readonly name: string = 'DataIsEmptyOrZeroed'; + constructor() { + super('Data is empty or zeroed'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, DataIsEmptyOrZeroedError); + } + } +} + +createErrorFromCodeLookup.set(0xa1, () => new DataIsEmptyOrZeroedError()); +createErrorFromNameLookup.set('DataIsEmptyOrZeroed', () => new DataIsEmptyOrZeroedError()); + +/** + * MissingTokenOwnerAccount: 'Missing token owner' + * + * @category Errors + * @category generated + */ +export class MissingTokenOwnerAccountError extends Error { + readonly code: number = 0xa2; + readonly name: string = 'MissingTokenOwnerAccount'; + constructor() { + super('Missing token owner'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, MissingTokenOwnerAccountError); + } + } +} + +createErrorFromCodeLookup.set(0xa2, () => new MissingTokenOwnerAccountError()); +createErrorFromNameLookup.set( + 'MissingTokenOwnerAccount', + () => new MissingTokenOwnerAccountError(), +); + +/** + * InvalidMasterEditionAccountLength: 'Master edition account has an invalid length' + * + * @category Errors + * @category generated + */ +export class InvalidMasterEditionAccountLengthError extends Error { + readonly code: number = 0xa3; + readonly name: string = 'InvalidMasterEditionAccountLength'; + constructor() { + super('Master edition account has an invalid length'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, InvalidMasterEditionAccountLengthError); + } + } +} + +createErrorFromCodeLookup.set(0xa3, () => new InvalidMasterEditionAccountLengthError()); +createErrorFromNameLookup.set( + 'InvalidMasterEditionAccountLength', + () => new InvalidMasterEditionAccountLengthError(), +); + +/** + * IncorrectTokenState: 'Incorrect token state' + * + * @category Errors + * @category generated + */ +export class IncorrectTokenStateError extends Error { + readonly code: number = 0xa4; + readonly name: string = 'IncorrectTokenState'; + constructor() { + super('Incorrect token state'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, IncorrectTokenStateError); + } + } +} + +createErrorFromCodeLookup.set(0xa4, () => new IncorrectTokenStateError()); +createErrorFromNameLookup.set('IncorrectTokenState', () => new IncorrectTokenStateError()); + +/** + * InvalidDelegateRole: 'Invalid delegate role' + * + * @category Errors + * @category generated + */ +export class InvalidDelegateRoleError extends Error { + readonly code: number = 0xa5; + readonly name: string = 'InvalidDelegateRole'; + constructor() { + super('Invalid delegate role'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, InvalidDelegateRoleError); + } + } +} + +createErrorFromCodeLookup.set(0xa5, () => new InvalidDelegateRoleError()); +createErrorFromNameLookup.set('InvalidDelegateRole', () => new InvalidDelegateRoleError()); + +/** + * MissingPrintSupply: 'Print supply is required for non-fungibles' + * + * @category Errors + * @category generated + */ +export class MissingPrintSupplyError extends Error { + readonly code: number = 0xa6; + readonly name: string = 'MissingPrintSupply'; + constructor() { + super('Print supply is required for non-fungibles'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, MissingPrintSupplyError); + } + } +} + +createErrorFromCodeLookup.set(0xa6, () => new MissingPrintSupplyError()); +createErrorFromNameLookup.set('MissingPrintSupply', () => new MissingPrintSupplyError()); + +/** + * MissingMasterEditionAccount: 'Missing master edition account' + * + * @category Errors + * @category generated + */ +export class MissingMasterEditionAccountError extends Error { + readonly code: number = 0xa7; + readonly name: string = 'MissingMasterEditionAccount'; + constructor() { + super('Missing master edition account'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, MissingMasterEditionAccountError); + } + } +} + +createErrorFromCodeLookup.set(0xa7, () => new MissingMasterEditionAccountError()); +createErrorFromNameLookup.set( + 'MissingMasterEditionAccount', + () => new MissingMasterEditionAccountError(), +); + +/** + * AmountMustBeGreaterThanZero: 'Amount must be greater than zero' + * + * @category Errors + * @category generated + */ +export class AmountMustBeGreaterThanZeroError extends Error { + readonly code: number = 0xa8; + readonly name: string = 'AmountMustBeGreaterThanZero'; + constructor() { + super('Amount must be greater than zero'); + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, AmountMustBeGreaterThanZeroError); + } + } +} + +createErrorFromCodeLookup.set(0xa8, () => new AmountMustBeGreaterThanZeroError()); +createErrorFromNameLookup.set( + 'AmountMustBeGreaterThanZero', + () => new AmountMustBeGreaterThanZeroError(), +); + /** * Attempts to resolve a custom program error from the provided error code. * @category Errors diff --git a/token-metadata/js/src/generated/instructions/Burn.ts b/token-metadata/js/src/generated/instructions/Burn.ts new file mode 100644 index 0000000000..b334af2578 --- /dev/null +++ b/token-metadata/js/src/generated/instructions/Burn.ts @@ -0,0 +1,142 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import { BurnArgs, burnArgsBeet } from '../types/BurnArgs'; + +/** + * @category Instructions + * @category Burn + * @category generated + */ +export type BurnInstructionArgs = { + burnArgs: BurnArgs; +}; +/** + * @category Instructions + * @category Burn + * @category generated + */ +export const BurnStruct = new beet.FixableBeetArgsStruct< + BurnInstructionArgs & { + instructionDiscriminator: number; + } +>( + [ + ['instructionDiscriminator', beet.u8], + ['burnArgs', burnArgsBeet], + ], + 'BurnInstructionArgs', +); +/** + * Accounts required by the _Burn_ instruction + * + * @property [_writable_] metadata Metadata (pda of ['metadata', program id, mint id]) + * @property [_writable_, **signer**] owner Asset owner + * @property [_writable_] mint Mint of token asset + * @property [_writable_] tokenAccount Token account to close + * @property [_writable_] masterEditionAccount MasterEdition of the asset + * @property [] splTokenProgram SPL Token Program + * @property [_writable_] collectionMetadata (optional) Metadata of the Collection + * @property [] authorizationRules (optional) Token Authorization Rules account + * @property [] authorizationRulesProgram (optional) Token Authorization Rules Program + * @category Instructions + * @category Burn + * @category generated + */ +export type BurnInstructionAccounts = { + metadata: web3.PublicKey; + owner: web3.PublicKey; + mint: web3.PublicKey; + tokenAccount: web3.PublicKey; + masterEditionAccount: web3.PublicKey; + splTokenProgram: web3.PublicKey; + collectionMetadata?: web3.PublicKey; + authorizationRules?: web3.PublicKey; + authorizationRulesProgram?: web3.PublicKey; +}; + +export const burnInstructionDiscriminator = 41; + +/** + * Creates a _Burn_ instruction. + * + * Optional accounts that are not provided default to the program ID since + * this was indicated in the IDL from which this instruction was generated. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Burn + * @category generated + */ +export function createBurnInstruction( + accounts: BurnInstructionAccounts, + args: BurnInstructionArgs, + programId = new web3.PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'), +) { + const [data] = BurnStruct.serialize({ + instructionDiscriminator: burnInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.metadata, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.owner, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.mint, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.tokenAccount, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.masterEditionAccount, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.splTokenProgram, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.collectionMetadata ?? programId, + isWritable: accounts.collectionMetadata != null, + isSigner: false, + }, + { + pubkey: accounts.authorizationRules ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.authorizationRulesProgram ?? programId, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/token-metadata/js/src/generated/instructions/Create.ts b/token-metadata/js/src/generated/instructions/Create.ts new file mode 100644 index 0000000000..cec6b3f470 --- /dev/null +++ b/token-metadata/js/src/generated/instructions/Create.ts @@ -0,0 +1,141 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import { CreateArgs, createArgsBeet } from '../types/CreateArgs'; + +/** + * @category Instructions + * @category Create + * @category generated + */ +export type CreateInstructionArgs = { + createArgs: CreateArgs; +}; +/** + * @category Instructions + * @category Create + * @category generated + */ +export const CreateStruct = new beet.FixableBeetArgsStruct< + CreateInstructionArgs & { + instructionDiscriminator: number; + } +>( + [ + ['instructionDiscriminator', beet.u8], + ['createArgs', createArgsBeet], + ], + 'CreateInstructionArgs', +); +/** + * Accounts required by the _Create_ instruction + * + * @property [_writable_] metadata Unallocated metadata account with address as pda of ['metadata', program id, mint id] + * @property [_writable_] masterEdition (optional) Unallocated edition account with address as pda of ['metadata', program id, mint, 'edition'] + * @property [_writable_] mint Mint of token asset + * @property [**signer**] authority Mint authority + * @property [_writable_, **signer**] payer Payer + * @property [] updateAuthority Update authority for the metadata account + * @property [] sysvarInstructions Instructions sysvar account + * @property [] splTokenProgram SPL Token program + * @category Instructions + * @category Create + * @category generated + */ +export type CreateInstructionAccounts = { + metadata: web3.PublicKey; + masterEdition?: web3.PublicKey; + mint: web3.PublicKey; + authority: web3.PublicKey; + payer: web3.PublicKey; + updateAuthority: web3.PublicKey; + systemProgram?: web3.PublicKey; + sysvarInstructions: web3.PublicKey; + splTokenProgram: web3.PublicKey; +}; + +export const createInstructionDiscriminator = 42; + +/** + * Creates a _Create_ instruction. + * + * Optional accounts that are not provided default to the program ID since + * this was indicated in the IDL from which this instruction was generated. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Create + * @category generated + */ +export function createCreateInstruction( + accounts: CreateInstructionAccounts, + args: CreateInstructionArgs, + programId = new web3.PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'), +) { + const [data] = CreateStruct.serialize({ + instructionDiscriminator: createInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.metadata, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.masterEdition ?? programId, + isWritable: accounts.masterEdition != null, + isSigner: false, + }, + { + pubkey: accounts.mint, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.authority, + isWritable: false, + isSigner: true, + }, + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.updateAuthority, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.systemProgram ?? web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.sysvarInstructions, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.splTokenProgram, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/token-metadata/js/src/generated/instructions/Delegate.ts b/token-metadata/js/src/generated/instructions/Delegate.ts new file mode 100644 index 0000000000..494e2a87d1 --- /dev/null +++ b/token-metadata/js/src/generated/instructions/Delegate.ts @@ -0,0 +1,176 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import { DelegateArgs, delegateArgsBeet } from '../types/DelegateArgs'; + +/** + * @category Instructions + * @category Delegate + * @category generated + */ +export type DelegateInstructionArgs = { + delegateArgs: DelegateArgs; +}; +/** + * @category Instructions + * @category Delegate + * @category generated + */ +export const DelegateStruct = new beet.FixableBeetArgsStruct< + DelegateInstructionArgs & { + instructionDiscriminator: number; + } +>( + [ + ['instructionDiscriminator', beet.u8], + ['delegateArgs', delegateArgsBeet], + ], + 'DelegateInstructionArgs', +); +/** + * Accounts required by the _Delegate_ instruction + * + * @property [_writable_] delegateRecord (optional) Delegate record account + * @property [] delegate Owner of the delegated account + * @property [_writable_] metadata Metadata account + * @property [] masterEdition (optional) Master Edition account + * @property [_writable_] tokenRecord (optional) Token record account + * @property [] mint Mint of metadata + * @property [_writable_] token (optional) Token account of mint + * @property [**signer**] authority Update authority or token owner + * @property [_writable_, **signer**] payer Payer + * @property [] sysvarInstructions Instructions sysvar account + * @property [] splTokenProgram (optional) SPL Token Program + * @property [] authorizationRulesProgram (optional) Token Authorization Rules Program + * @property [] authorizationRules (optional) Token Authorization Rules account + * @category Instructions + * @category Delegate + * @category generated + */ +export type DelegateInstructionAccounts = { + delegateRecord?: web3.PublicKey; + delegate: web3.PublicKey; + metadata: web3.PublicKey; + masterEdition?: web3.PublicKey; + tokenRecord?: web3.PublicKey; + mint: web3.PublicKey; + token?: web3.PublicKey; + authority: web3.PublicKey; + payer: web3.PublicKey; + systemProgram?: web3.PublicKey; + sysvarInstructions: web3.PublicKey; + splTokenProgram?: web3.PublicKey; + authorizationRulesProgram?: web3.PublicKey; + authorizationRules?: web3.PublicKey; +}; + +export const delegateInstructionDiscriminator = 44; + +/** + * Creates a _Delegate_ instruction. + * + * Optional accounts that are not provided default to the program ID since + * this was indicated in the IDL from which this instruction was generated. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Delegate + * @category generated + */ +export function createDelegateInstruction( + accounts: DelegateInstructionAccounts, + args: DelegateInstructionArgs, + programId = new web3.PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'), +) { + const [data] = DelegateStruct.serialize({ + instructionDiscriminator: delegateInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.delegateRecord ?? programId, + isWritable: accounts.delegateRecord != null, + isSigner: false, + }, + { + pubkey: accounts.delegate, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.metadata, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.masterEdition ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.tokenRecord ?? programId, + isWritable: accounts.tokenRecord != null, + isSigner: false, + }, + { + pubkey: accounts.mint, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.token ?? programId, + isWritable: accounts.token != null, + isSigner: false, + }, + { + pubkey: accounts.authority, + isWritable: false, + isSigner: true, + }, + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.systemProgram ?? web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.sysvarInstructions, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.splTokenProgram ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.authorizationRulesProgram ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.authorizationRules ?? programId, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/token-metadata/js/src/generated/instructions/Lock.ts b/token-metadata/js/src/generated/instructions/Lock.ts new file mode 100644 index 0000000000..b4da2a5ee3 --- /dev/null +++ b/token-metadata/js/src/generated/instructions/Lock.ts @@ -0,0 +1,169 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import { LockArgs, lockArgsBeet } from '../types/LockArgs'; + +/** + * @category Instructions + * @category Lock + * @category generated + */ +export type LockInstructionArgs = { + lockArgs: LockArgs; +}; +/** + * @category Instructions + * @category Lock + * @category generated + */ +export const LockStruct = new beet.FixableBeetArgsStruct< + LockInstructionArgs & { + instructionDiscriminator: number; + } +>( + [ + ['instructionDiscriminator', beet.u8], + ['lockArgs', lockArgsBeet], + ], + 'LockInstructionArgs', +); +/** + * Accounts required by the _Lock_ instruction + * + * @property [**signer**] authority Delegate account + * @property [] tokenOwner (optional) Token owner account + * @property [_writable_] token Token account + * @property [] mint Mint account + * @property [_writable_] metadata Metadata account + * @property [] edition (optional) Edition account + * @property [_writable_] tokenRecord (optional) Token record account + * @property [_writable_, **signer**] payer Payer + * @property [] sysvarInstructions System program + * @property [] splTokenProgram (optional) SPL Token Program + * @property [] authorizationRulesProgram (optional) Token Authorization Rules Program + * @property [] authorizationRules (optional) Token Authorization Rules account + * @category Instructions + * @category Lock + * @category generated + */ +export type LockInstructionAccounts = { + authority: web3.PublicKey; + tokenOwner?: web3.PublicKey; + token: web3.PublicKey; + mint: web3.PublicKey; + metadata: web3.PublicKey; + edition?: web3.PublicKey; + tokenRecord?: web3.PublicKey; + payer: web3.PublicKey; + systemProgram?: web3.PublicKey; + sysvarInstructions: web3.PublicKey; + splTokenProgram?: web3.PublicKey; + authorizationRulesProgram?: web3.PublicKey; + authorizationRules?: web3.PublicKey; +}; + +export const lockInstructionDiscriminator = 46; + +/** + * Creates a _Lock_ instruction. + * + * Optional accounts that are not provided default to the program ID since + * this was indicated in the IDL from which this instruction was generated. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Lock + * @category generated + */ +export function createLockInstruction( + accounts: LockInstructionAccounts, + args: LockInstructionArgs, + programId = new web3.PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'), +) { + const [data] = LockStruct.serialize({ + instructionDiscriminator: lockInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.authority, + isWritable: false, + isSigner: true, + }, + { + pubkey: accounts.tokenOwner ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.token, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.mint, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.metadata, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.edition ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.tokenRecord ?? programId, + isWritable: accounts.tokenRecord != null, + isSigner: false, + }, + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.systemProgram ?? web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.sysvarInstructions, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.splTokenProgram ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.authorizationRulesProgram ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.authorizationRules ?? programId, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/token-metadata/js/src/generated/instructions/Migrate.ts b/token-metadata/js/src/generated/instructions/Migrate.ts new file mode 100644 index 0000000000..0c85077683 --- /dev/null +++ b/token-metadata/js/src/generated/instructions/Migrate.ts @@ -0,0 +1,183 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import { MigrateArgs, migrateArgsBeet } from '../types/MigrateArgs'; + +/** + * @category Instructions + * @category Migrate + * @category generated + */ +export type MigrateInstructionArgs = { + migrateArgs: MigrateArgs; +}; +/** + * @category Instructions + * @category Migrate + * @category generated + */ +export const MigrateStruct = new beet.FixableBeetArgsStruct< + MigrateInstructionArgs & { + instructionDiscriminator: number; + } +>( + [ + ['instructionDiscriminator', beet.u8], + ['migrateArgs', migrateArgsBeet], + ], + 'MigrateInstructionArgs', +); +/** + * Accounts required by the _Migrate_ instruction + * + * @property [_writable_] metadata Metadata account + * @property [_writable_] edition Edition account + * @property [_writable_] token Token account + * @property [] tokenOwner Token account owner + * @property [] mint Mint account + * @property [_writable_, **signer**] payer Update authority + * @property [**signer**] authority Update authority + * @property [] collectionMetadata Collection metadata account + * @property [] delegateRecord Delegate record account + * @property [] tokenRecord Token record account + * @property [] sysvarInstructions Instruction sysvar account + * @property [] splTokenProgram Token Program + * @property [] authorizationRulesProgram (optional) Token Authorization Rules Program + * @property [] authorizationRules (optional) Token Authorization Rules account + * @category Instructions + * @category Migrate + * @category generated + */ +export type MigrateInstructionAccounts = { + metadata: web3.PublicKey; + edition: web3.PublicKey; + token: web3.PublicKey; + tokenOwner: web3.PublicKey; + mint: web3.PublicKey; + payer: web3.PublicKey; + authority: web3.PublicKey; + collectionMetadata: web3.PublicKey; + delegateRecord: web3.PublicKey; + tokenRecord: web3.PublicKey; + systemProgram?: web3.PublicKey; + sysvarInstructions: web3.PublicKey; + splTokenProgram: web3.PublicKey; + authorizationRulesProgram?: web3.PublicKey; + authorizationRules?: web3.PublicKey; +}; + +export const migrateInstructionDiscriminator = 48; + +/** + * Creates a _Migrate_ instruction. + * + * Optional accounts that are not provided default to the program ID since + * this was indicated in the IDL from which this instruction was generated. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Migrate + * @category generated + */ +export function createMigrateInstruction( + accounts: MigrateInstructionAccounts, + args: MigrateInstructionArgs, + programId = new web3.PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'), +) { + const [data] = MigrateStruct.serialize({ + instructionDiscriminator: migrateInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.metadata, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.edition, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.token, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.tokenOwner, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.mint, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.authority, + isWritable: false, + isSigner: true, + }, + { + pubkey: accounts.collectionMetadata, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.delegateRecord, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.tokenRecord, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.systemProgram ?? web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.sysvarInstructions, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.splTokenProgram, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.authorizationRulesProgram ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.authorizationRules ?? programId, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/token-metadata/js/src/generated/instructions/Mint.ts b/token-metadata/js/src/generated/instructions/Mint.ts new file mode 100644 index 0000000000..7bc9087887 --- /dev/null +++ b/token-metadata/js/src/generated/instructions/Mint.ts @@ -0,0 +1,183 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import { MintArgs, mintArgsBeet } from '../types/MintArgs'; + +/** + * @category Instructions + * @category Mint + * @category generated + */ +export type MintInstructionArgs = { + mintArgs: MintArgs; +}; +/** + * @category Instructions + * @category Mint + * @category generated + */ +export const MintStruct = new beet.FixableBeetArgsStruct< + MintInstructionArgs & { + instructionDiscriminator: number; + } +>( + [ + ['instructionDiscriminator', beet.u8], + ['mintArgs', mintArgsBeet], + ], + 'MintInstructionArgs', +); +/** + * Accounts required by the _Mint_ instruction + * + * @property [_writable_] token Token or Associated Token account + * @property [] tokenOwner (optional) Owner of the token account + * @property [] metadata Metadata account (pda of ['metadata', program id, mint id]) + * @property [] masterEdition (optional) Master Edition account + * @property [_writable_] tokenRecord (optional) Token record account + * @property [_writable_] mint Mint of token asset + * @property [**signer**] authority (Mint or Update) authority + * @property [] delegateRecord (optional) Metadata delegate record + * @property [_writable_, **signer**] payer Payer + * @property [] sysvarInstructions Instructions sysvar account + * @property [] splTokenProgram SPL Token program + * @property [] splAtaProgram SPL Associated Token Account program + * @property [] authorizationRulesProgram (optional) Token Authorization Rules program + * @property [] authorizationRules (optional) Token Authorization Rules account + * @category Instructions + * @category Mint + * @category generated + */ +export type MintInstructionAccounts = { + token: web3.PublicKey; + tokenOwner?: web3.PublicKey; + metadata: web3.PublicKey; + masterEdition?: web3.PublicKey; + tokenRecord?: web3.PublicKey; + mint: web3.PublicKey; + authority: web3.PublicKey; + delegateRecord?: web3.PublicKey; + payer: web3.PublicKey; + systemProgram?: web3.PublicKey; + sysvarInstructions: web3.PublicKey; + splTokenProgram: web3.PublicKey; + splAtaProgram: web3.PublicKey; + authorizationRulesProgram?: web3.PublicKey; + authorizationRules?: web3.PublicKey; +}; + +export const mintInstructionDiscriminator = 43; + +/** + * Creates a _Mint_ instruction. + * + * Optional accounts that are not provided default to the program ID since + * this was indicated in the IDL from which this instruction was generated. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Mint + * @category generated + */ +export function createMintInstruction( + accounts: MintInstructionAccounts, + args: MintInstructionArgs, + programId = new web3.PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'), +) { + const [data] = MintStruct.serialize({ + instructionDiscriminator: mintInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.token, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.tokenOwner ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.metadata, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.masterEdition ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.tokenRecord ?? programId, + isWritable: accounts.tokenRecord != null, + isSigner: false, + }, + { + pubkey: accounts.mint, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.authority, + isWritable: false, + isSigner: true, + }, + { + pubkey: accounts.delegateRecord ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.systemProgram ?? web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.sysvarInstructions, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.splTokenProgram, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.splAtaProgram, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.authorizationRulesProgram ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.authorizationRules ?? programId, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/token-metadata/js/src/generated/instructions/Revoke.ts b/token-metadata/js/src/generated/instructions/Revoke.ts new file mode 100644 index 0000000000..e37a03f16a --- /dev/null +++ b/token-metadata/js/src/generated/instructions/Revoke.ts @@ -0,0 +1,176 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import { RevokeArgs, revokeArgsBeet } from '../types/RevokeArgs'; + +/** + * @category Instructions + * @category Revoke + * @category generated + */ +export type RevokeInstructionArgs = { + revokeArgs: RevokeArgs; +}; +/** + * @category Instructions + * @category Revoke + * @category generated + */ +export const RevokeStruct = new beet.BeetArgsStruct< + RevokeInstructionArgs & { + instructionDiscriminator: number; + } +>( + [ + ['instructionDiscriminator', beet.u8], + ['revokeArgs', revokeArgsBeet], + ], + 'RevokeInstructionArgs', +); +/** + * Accounts required by the _Revoke_ instruction + * + * @property [_writable_] delegateRecord (optional) Delegate record account + * @property [] delegate Owner of the delegated account + * @property [_writable_] metadata Metadata account + * @property [] masterEdition (optional) Master Edition account + * @property [_writable_] tokenRecord (optional) Token record account + * @property [] mint Mint of metadata + * @property [_writable_] token (optional) Token account of mint + * @property [**signer**] authority Update authority or token owner + * @property [_writable_, **signer**] payer Payer + * @property [] sysvarInstructions Instructions sysvar account + * @property [] splTokenProgram (optional) SPL Token Program + * @property [] authorizationRulesProgram (optional) Token Authorization Rules Program + * @property [] authorizationRules (optional) Token Authorization Rules account + * @category Instructions + * @category Revoke + * @category generated + */ +export type RevokeInstructionAccounts = { + delegateRecord?: web3.PublicKey; + delegate: web3.PublicKey; + metadata: web3.PublicKey; + masterEdition?: web3.PublicKey; + tokenRecord?: web3.PublicKey; + mint: web3.PublicKey; + token?: web3.PublicKey; + authority: web3.PublicKey; + payer: web3.PublicKey; + systemProgram?: web3.PublicKey; + sysvarInstructions: web3.PublicKey; + splTokenProgram?: web3.PublicKey; + authorizationRulesProgram?: web3.PublicKey; + authorizationRules?: web3.PublicKey; +}; + +export const revokeInstructionDiscriminator = 45; + +/** + * Creates a _Revoke_ instruction. + * + * Optional accounts that are not provided default to the program ID since + * this was indicated in the IDL from which this instruction was generated. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Revoke + * @category generated + */ +export function createRevokeInstruction( + accounts: RevokeInstructionAccounts, + args: RevokeInstructionArgs, + programId = new web3.PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'), +) { + const [data] = RevokeStruct.serialize({ + instructionDiscriminator: revokeInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.delegateRecord ?? programId, + isWritable: accounts.delegateRecord != null, + isSigner: false, + }, + { + pubkey: accounts.delegate, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.metadata, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.masterEdition ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.tokenRecord ?? programId, + isWritable: accounts.tokenRecord != null, + isSigner: false, + }, + { + pubkey: accounts.mint, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.token ?? programId, + isWritable: accounts.token != null, + isSigner: false, + }, + { + pubkey: accounts.authority, + isWritable: false, + isSigner: true, + }, + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.systemProgram ?? web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.sysvarInstructions, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.splTokenProgram ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.authorizationRulesProgram ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.authorizationRules ?? programId, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/token-metadata/js/src/generated/instructions/Transfer.ts b/token-metadata/js/src/generated/instructions/Transfer.ts new file mode 100644 index 0000000000..0b32e320cc --- /dev/null +++ b/token-metadata/js/src/generated/instructions/Transfer.ts @@ -0,0 +1,197 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import { TransferArgs, transferArgsBeet } from '../types/TransferArgs'; + +/** + * @category Instructions + * @category Transfer + * @category generated + */ +export type TransferInstructionArgs = { + transferArgs: TransferArgs; +}; +/** + * @category Instructions + * @category Transfer + * @category generated + */ +export const TransferStruct = new beet.FixableBeetArgsStruct< + TransferInstructionArgs & { + instructionDiscriminator: number; + } +>( + [ + ['instructionDiscriminator', beet.u8], + ['transferArgs', transferArgsBeet], + ], + 'TransferInstructionArgs', +); +/** + * Accounts required by the _Transfer_ instruction + * + * @property [_writable_] token Token account + * @property [] tokenOwner Token account owner + * @property [_writable_] destination Destination token account + * @property [] destinationOwner Destination token account owner + * @property [] mint Mint of token asset + * @property [_writable_] metadata Metadata (pda of ['metadata', program id, mint id]) + * @property [] edition (optional) Edition of token asset + * @property [_writable_] ownerTokenRecord (optional) Token record account + * @property [_writable_] destinationTokenRecord (optional) Token record account + * @property [**signer**] authority Transfer authority (token or delegate owner) + * @property [_writable_, **signer**] payer Payer + * @property [] sysvarInstructions Instructions sysvar account + * @property [] splTokenProgram SPL Token Program + * @property [] splAtaProgram SPL Associated Token Account program + * @property [] authorizationRulesProgram (optional) Token Authorization Rules Program + * @property [] authorizationRules (optional) Token Authorization Rules account + * @category Instructions + * @category Transfer + * @category generated + */ +export type TransferInstructionAccounts = { + token: web3.PublicKey; + tokenOwner: web3.PublicKey; + destination: web3.PublicKey; + destinationOwner: web3.PublicKey; + mint: web3.PublicKey; + metadata: web3.PublicKey; + edition?: web3.PublicKey; + ownerTokenRecord?: web3.PublicKey; + destinationTokenRecord?: web3.PublicKey; + authority: web3.PublicKey; + payer: web3.PublicKey; + systemProgram?: web3.PublicKey; + sysvarInstructions: web3.PublicKey; + splTokenProgram: web3.PublicKey; + splAtaProgram: web3.PublicKey; + authorizationRulesProgram?: web3.PublicKey; + authorizationRules?: web3.PublicKey; +}; + +export const transferInstructionDiscriminator = 49; + +/** + * Creates a _Transfer_ instruction. + * + * Optional accounts that are not provided default to the program ID since + * this was indicated in the IDL from which this instruction was generated. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Transfer + * @category generated + */ +export function createTransferInstruction( + accounts: TransferInstructionAccounts, + args: TransferInstructionArgs, + programId = new web3.PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'), +) { + const [data] = TransferStruct.serialize({ + instructionDiscriminator: transferInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.token, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.tokenOwner, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.destination, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.destinationOwner, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.mint, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.metadata, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.edition ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.ownerTokenRecord ?? programId, + isWritable: accounts.ownerTokenRecord != null, + isSigner: false, + }, + { + pubkey: accounts.destinationTokenRecord ?? programId, + isWritable: accounts.destinationTokenRecord != null, + isSigner: false, + }, + { + pubkey: accounts.authority, + isWritable: false, + isSigner: true, + }, + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.systemProgram ?? web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.sysvarInstructions, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.splTokenProgram, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.splAtaProgram, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.authorizationRulesProgram ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.authorizationRules ?? programId, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/token-metadata/js/src/generated/instructions/Unlock.ts b/token-metadata/js/src/generated/instructions/Unlock.ts new file mode 100644 index 0000000000..b695a6e1e2 --- /dev/null +++ b/token-metadata/js/src/generated/instructions/Unlock.ts @@ -0,0 +1,169 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import { UnlockArgs, unlockArgsBeet } from '../types/UnlockArgs'; + +/** + * @category Instructions + * @category Unlock + * @category generated + */ +export type UnlockInstructionArgs = { + unlockArgs: UnlockArgs; +}; +/** + * @category Instructions + * @category Unlock + * @category generated + */ +export const UnlockStruct = new beet.FixableBeetArgsStruct< + UnlockInstructionArgs & { + instructionDiscriminator: number; + } +>( + [ + ['instructionDiscriminator', beet.u8], + ['unlockArgs', unlockArgsBeet], + ], + 'UnlockInstructionArgs', +); +/** + * Accounts required by the _Unlock_ instruction + * + * @property [**signer**] authority Delegate account + * @property [] tokenOwner (optional) Token owner account + * @property [_writable_] token Token account + * @property [] mint Mint account + * @property [_writable_] metadata Metadata account + * @property [] edition (optional) Edition account + * @property [_writable_] tokenRecord (optional) Token record account + * @property [_writable_, **signer**] payer Payer + * @property [] sysvarInstructions System program + * @property [] splTokenProgram (optional) SPL Token Program + * @property [] authorizationRulesProgram (optional) Token Authorization Rules Program + * @property [] authorizationRules (optional) Token Authorization Rules account + * @category Instructions + * @category Unlock + * @category generated + */ +export type UnlockInstructionAccounts = { + authority: web3.PublicKey; + tokenOwner?: web3.PublicKey; + token: web3.PublicKey; + mint: web3.PublicKey; + metadata: web3.PublicKey; + edition?: web3.PublicKey; + tokenRecord?: web3.PublicKey; + payer: web3.PublicKey; + systemProgram?: web3.PublicKey; + sysvarInstructions: web3.PublicKey; + splTokenProgram?: web3.PublicKey; + authorizationRulesProgram?: web3.PublicKey; + authorizationRules?: web3.PublicKey; +}; + +export const unlockInstructionDiscriminator = 47; + +/** + * Creates a _Unlock_ instruction. + * + * Optional accounts that are not provided default to the program ID since + * this was indicated in the IDL from which this instruction was generated. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Unlock + * @category generated + */ +export function createUnlockInstruction( + accounts: UnlockInstructionAccounts, + args: UnlockInstructionArgs, + programId = new web3.PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'), +) { + const [data] = UnlockStruct.serialize({ + instructionDiscriminator: unlockInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.authority, + isWritable: false, + isSigner: true, + }, + { + pubkey: accounts.tokenOwner ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.token, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.mint, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.metadata, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.edition ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.tokenRecord ?? programId, + isWritable: accounts.tokenRecord != null, + isSigner: false, + }, + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.systemProgram ?? web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.sysvarInstructions, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.splTokenProgram ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.authorizationRulesProgram ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.authorizationRules ?? programId, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/token-metadata/js/src/generated/instructions/Update.ts b/token-metadata/js/src/generated/instructions/Update.ts new file mode 100644 index 0000000000..0a01bef877 --- /dev/null +++ b/token-metadata/js/src/generated/instructions/Update.ts @@ -0,0 +1,155 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import { UpdateArgs, updateArgsBeet } from '../types/UpdateArgs'; + +/** + * @category Instructions + * @category Update + * @category generated + */ +export type UpdateInstructionArgs = { + updateArgs: UpdateArgs; +}; +/** + * @category Instructions + * @category Update + * @category generated + */ +export const UpdateStruct = new beet.FixableBeetArgsStruct< + UpdateInstructionArgs & { + instructionDiscriminator: number; + } +>( + [ + ['instructionDiscriminator', beet.u8], + ['updateArgs', updateArgsBeet], + ], + 'UpdateInstructionArgs', +); +/** + * Accounts required by the _Update_ instruction + * + * @property [**signer**] authority Update authority or delegate + * @property [] delegateRecord (optional) Delegate record PDA + * @property [] token (optional) Token account + * @property [] mint Mint account + * @property [_writable_] metadata Metadata account + * @property [_writable_] edition (optional) Edition account + * @property [_writable_, **signer**] payer Payer + * @property [] sysvarInstructions System program + * @property [] authorizationRulesProgram (optional) Token Authorization Rules Program + * @property [] authorizationRules (optional) Token Authorization Rules account + * @category Instructions + * @category Update + * @category generated + */ +export type UpdateInstructionAccounts = { + authority: web3.PublicKey; + delegateRecord?: web3.PublicKey; + token?: web3.PublicKey; + mint: web3.PublicKey; + metadata: web3.PublicKey; + edition?: web3.PublicKey; + payer: web3.PublicKey; + systemProgram?: web3.PublicKey; + sysvarInstructions: web3.PublicKey; + authorizationRulesProgram?: web3.PublicKey; + authorizationRules?: web3.PublicKey; +}; + +export const updateInstructionDiscriminator = 50; + +/** + * Creates a _Update_ instruction. + * + * Optional accounts that are not provided default to the program ID since + * this was indicated in the IDL from which this instruction was generated. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Update + * @category generated + */ +export function createUpdateInstruction( + accounts: UpdateInstructionAccounts, + args: UpdateInstructionArgs, + programId = new web3.PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'), +) { + const [data] = UpdateStruct.serialize({ + instructionDiscriminator: updateInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.authority, + isWritable: false, + isSigner: true, + }, + { + pubkey: accounts.delegateRecord ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.token ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.mint, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.metadata, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.edition ?? programId, + isWritable: accounts.edition != null, + isSigner: false, + }, + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.systemProgram ?? web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.sysvarInstructions, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.authorizationRulesProgram ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.authorizationRules ?? programId, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/token-metadata/js/src/generated/instructions/Use.ts b/token-metadata/js/src/generated/instructions/Use.ts new file mode 100644 index 0000000000..7e653cd1d0 --- /dev/null +++ b/token-metadata/js/src/generated/instructions/Use.ts @@ -0,0 +1,162 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import { UseArgs, useArgsBeet } from '../types/UseArgs'; + +/** + * @category Instructions + * @category Use + * @category generated + */ +export type UseInstructionArgs = { + useArgs: UseArgs; +}; +/** + * @category Instructions + * @category Use + * @category generated + */ +export const UseStruct = new beet.FixableBeetArgsStruct< + UseInstructionArgs & { + instructionDiscriminator: number; + } +>( + [ + ['instructionDiscriminator', beet.u8], + ['useArgs', useArgsBeet], + ], + 'UseInstructionArgs', +); +/** + * Accounts required by the _Use_ instruction + * + * @property [**signer**] authority Token owner or delegate + * @property [_writable_] delegateRecord (optional) Delegate record PDA + * @property [_writable_] token (optional) Token account + * @property [] mint Mint account + * @property [_writable_] metadata Metadata account + * @property [_writable_] edition (optional) Edition account + * @property [**signer**] payer Payer + * @property [] sysvarInstructions System program + * @property [] splTokenProgram (optional) SPL Token Program + * @property [] authorizationRulesProgram (optional) Token Authorization Rules Program + * @property [] authorizationRules (optional) Token Authorization Rules account + * @category Instructions + * @category Use + * @category generated + */ +export type UseInstructionAccounts = { + authority: web3.PublicKey; + delegateRecord?: web3.PublicKey; + token?: web3.PublicKey; + mint: web3.PublicKey; + metadata: web3.PublicKey; + edition?: web3.PublicKey; + payer: web3.PublicKey; + systemProgram?: web3.PublicKey; + sysvarInstructions: web3.PublicKey; + splTokenProgram?: web3.PublicKey; + authorizationRulesProgram?: web3.PublicKey; + authorizationRules?: web3.PublicKey; +}; + +export const useInstructionDiscriminator = 51; + +/** + * Creates a _Use_ instruction. + * + * Optional accounts that are not provided default to the program ID since + * this was indicated in the IDL from which this instruction was generated. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Use + * @category generated + */ +export function createUseInstruction( + accounts: UseInstructionAccounts, + args: UseInstructionArgs, + programId = new web3.PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'), +) { + const [data] = UseStruct.serialize({ + instructionDiscriminator: useInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.authority, + isWritable: false, + isSigner: true, + }, + { + pubkey: accounts.delegateRecord ?? programId, + isWritable: accounts.delegateRecord != null, + isSigner: false, + }, + { + pubkey: accounts.token ?? programId, + isWritable: accounts.token != null, + isSigner: false, + }, + { + pubkey: accounts.mint, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.metadata, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.edition ?? programId, + isWritable: accounts.edition != null, + isSigner: false, + }, + { + pubkey: accounts.payer, + isWritable: false, + isSigner: true, + }, + { + pubkey: accounts.systemProgram ?? web3.SystemProgram.programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.sysvarInstructions, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.splTokenProgram ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.authorizationRulesProgram ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.authorizationRules ?? programId, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/token-metadata/js/src/generated/instructions/Utilize.ts b/token-metadata/js/src/generated/instructions/Utilize.ts index 36beb67666..075066ed25 100644 --- a/token-metadata/js/src/generated/instructions/Utilize.ts +++ b/token-metadata/js/src/generated/instructions/Utilize.ts @@ -143,7 +143,6 @@ export function createUtilizeInstruction( isSigner: false, }); } - if (accounts.burner != null) { if (accounts.useAuthorityRecord == null) { throw new Error( diff --git a/token-metadata/js/src/generated/instructions/Verify.ts b/token-metadata/js/src/generated/instructions/Verify.ts new file mode 100644 index 0000000000..3f5b43e9ab --- /dev/null +++ b/token-metadata/js/src/generated/instructions/Verify.ts @@ -0,0 +1,114 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import { VerifyArgs, verifyArgsBeet } from '../types/VerifyArgs'; + +/** + * @category Instructions + * @category Verify + * @category generated + */ +export type VerifyInstructionArgs = { + verifyArgs: VerifyArgs; +}; +/** + * @category Instructions + * @category Verify + * @category generated + */ +export const VerifyStruct = new beet.FixableBeetArgsStruct< + VerifyInstructionArgs & { + instructionDiscriminator: number; + } +>( + [ + ['instructionDiscriminator', beet.u8], + ['verifyArgs', verifyArgsBeet], + ], + 'VerifyInstructionArgs', +); +/** + * Accounts required by the _Verify_ instruction + * + * @property [_writable_] metadata Metadata account + * @property [_writable_, **signer**] collectionAuthority Collection Update authority + * @property [_writable_, **signer**] payer payer + * @property [] authorizationRules (optional) Token Authorization Rules account + * @property [] authorizationRulesProgram (optional) Token Authorization Rules Program + * @category Instructions + * @category Verify + * @category generated + */ +export type VerifyInstructionAccounts = { + metadata: web3.PublicKey; + collectionAuthority: web3.PublicKey; + payer: web3.PublicKey; + authorizationRules?: web3.PublicKey; + authorizationRulesProgram?: web3.PublicKey; +}; + +export const verifyInstructionDiscriminator = 52; + +/** + * Creates a _Verify_ instruction. + * + * Optional accounts that are not provided default to the program ID since + * this was indicated in the IDL from which this instruction was generated. + * + * @param accounts that will be accessed while the instruction is processed + * @param args to provide as instruction data to the program + * + * @category Instructions + * @category Verify + * @category generated + */ +export function createVerifyInstruction( + accounts: VerifyInstructionAccounts, + args: VerifyInstructionArgs, + programId = new web3.PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'), +) { + const [data] = VerifyStruct.serialize({ + instructionDiscriminator: verifyInstructionDiscriminator, + ...args, + }); + const keys: web3.AccountMeta[] = [ + { + pubkey: accounts.metadata, + isWritable: true, + isSigner: false, + }, + { + pubkey: accounts.collectionAuthority, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.payer, + isWritable: true, + isSigner: true, + }, + { + pubkey: accounts.authorizationRules ?? programId, + isWritable: false, + isSigner: false, + }, + { + pubkey: accounts.authorizationRulesProgram ?? programId, + isWritable: false, + isSigner: false, + }, + ]; + + const ix = new web3.TransactionInstruction({ + programId, + keys, + data, + }); + return ix; +} diff --git a/token-metadata/js/src/generated/instructions/index.ts b/token-metadata/js/src/generated/instructions/index.ts index 6015343540..f973780e2c 100644 --- a/token-metadata/js/src/generated/instructions/index.ts +++ b/token-metadata/js/src/generated/instructions/index.ts @@ -1,16 +1,19 @@ export * from './ApproveCollectionAuthority'; export * from './ApproveUseAuthority'; export * from './BubblegumSetCollectionSize'; +export * from './Burn'; export * from './BurnEditionNft'; export * from './BurnNft'; export * from './CloseEscrowAccount'; export * from './ConvertMasterEditionV1ToV2'; +export * from './Create'; export * from './CreateEscrowAccount'; export * from './CreateMasterEdition'; export * from './CreateMasterEditionV3'; export * from './CreateMetadataAccount'; export * from './CreateMetadataAccountV2'; export * from './CreateMetadataAccountV3'; +export * from './Delegate'; export * from './DeprecatedCreateMasterEdition'; export * from './DeprecatedCreateReservationList'; export * from './DeprecatedMintNewEditionFromMasterEditionViaPrintingToken'; @@ -18,10 +21,14 @@ export * from './DeprecatedMintPrintingTokens'; export * from './DeprecatedMintPrintingTokensViaToken'; export * from './DeprecatedSetReservationList'; export * from './FreezeDelegatedAccount'; +export * from './Lock'; +export * from './Migrate'; +export * from './Mint'; export * from './MintNewEditionFromMasterEditionViaToken'; export * from './MintNewEditionFromMasterEditionViaVaultProxy'; export * from './PuffMetadata'; export * from './RemoveCreatorVerification'; +export * from './Revoke'; export * from './RevokeCollectionAuthority'; export * from './RevokeUseAuthority'; export * from './SetAndVerifyCollection'; @@ -30,12 +37,17 @@ export * from './SetCollectionSize'; export * from './SetTokenStandard'; export * from './SignMetadata'; export * from './ThawDelegatedAccount'; +export * from './Transfer'; export * from './TransferOutOfEscrow'; +export * from './Unlock'; export * from './UnverifyCollection'; export * from './UnverifySizedCollectionItem'; +export * from './Update'; export * from './UpdateMetadataAccount'; export * from './UpdateMetadataAccountV2'; export * from './UpdatePrimarySaleHappenedViaToken'; +export * from './Use'; export * from './Utilize'; +export * from './Verify'; export * from './VerifyCollection'; export * from './VerifySizedCollectionItem'; diff --git a/token-metadata/js/src/generated/types/AssetData.ts b/token-metadata/js/src/generated/types/AssetData.ts new file mode 100644 index 0000000000..fbb391235d --- /dev/null +++ b/token-metadata/js/src/generated/types/AssetData.ts @@ -0,0 +1,51 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import * as web3 from '@solana/web3.js'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; +import { Creator, creatorBeet } from './Creator'; +import { TokenStandard, tokenStandardBeet } from './TokenStandard'; +import { Collection, collectionBeet } from './Collection'; +import { Uses, usesBeet } from './Uses'; +import { CollectionDetails, collectionDetailsBeet } from './CollectionDetails'; +export type AssetData = { + name: string; + symbol: string; + uri: string; + sellerFeeBasisPoints: number; + creators: beet.COption; + primarySaleHappened: boolean; + isMutable: boolean; + tokenStandard: TokenStandard; + collection: beet.COption; + uses: beet.COption; + collectionDetails: beet.COption; + ruleSet: beet.COption; +}; + +/** + * @category userTypes + * @category generated + */ +export const assetDataBeet = new beet.FixableBeetArgsStruct( + [ + ['name', beet.utf8String], + ['symbol', beet.utf8String], + ['uri', beet.utf8String], + ['sellerFeeBasisPoints', beet.u16], + ['creators', beet.coption(beet.array(creatorBeet))], + ['primarySaleHappened', beet.bool], + ['isMutable', beet.bool], + ['tokenStandard', tokenStandardBeet], + ['collection', beet.coption(collectionBeet)], + ['uses', beet.coption(usesBeet)], + ['collectionDetails', beet.coption(collectionDetailsBeet)], + ['ruleSet', beet.coption(beetSolana.publicKey)], + ], + 'AssetData', +); diff --git a/token-metadata/js/src/generated/types/AuthorityType.ts b/token-metadata/js/src/generated/types/AuthorityType.ts new file mode 100644 index 0000000000..ac29e89e12 --- /dev/null +++ b/token-metadata/js/src/generated/types/AuthorityType.ts @@ -0,0 +1,27 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +/** + * @category enums + * @category generated + */ +export enum AuthorityType { + None, + Metadata, + Delegate, + Holder, +} + +/** + * @category userTypes + * @category generated + */ +export const authorityTypeBeet = beet.fixedScalarEnum(AuthorityType) as beet.FixedSizeBeet< + AuthorityType, + AuthorityType +>; diff --git a/token-metadata/js/src/generated/types/AuthorizationData.ts b/token-metadata/js/src/generated/types/AuthorizationData.ts new file mode 100644 index 0000000000..85abbf70aa --- /dev/null +++ b/token-metadata/js/src/generated/types/AuthorizationData.ts @@ -0,0 +1,21 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import { Payload, payloadBeet } from './Payload'; +export type AuthorizationData = { + payload: Payload; +}; + +/** + * @category userTypes + * @category generated + */ +export const authorizationDataBeet = new beet.FixableBeetArgsStruct( + [['payload', payloadBeet]], + 'AuthorizationData', +); diff --git a/token-metadata/js/src/generated/types/BurnArgs.ts b/token-metadata/js/src/generated/types/BurnArgs.ts new file mode 100644 index 0000000000..24f792b716 --- /dev/null +++ b/token-metadata/js/src/generated/types/BurnArgs.ts @@ -0,0 +1,50 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import { AuthorizationData, authorizationDataBeet } from './AuthorizationData'; +/** + * This type is used to derive the {@link BurnArgs} type as well as the de/serializer. + * However don't refer to it in your code but use the {@link BurnArgs} type instead. + * + * @category userTypes + * @category enums + * @category generated + * @private + */ +export type BurnArgsRecord = { + V1: { authorizationData: beet.COption }; +}; + +/** + * Union type respresenting the BurnArgs data enum defined in Rust. + * + * NOTE: that it includes a `__kind` property which allows to narrow types in + * switch/if statements. + * Additionally `isBurnArgs*` type guards are exposed below to narrow to a specific variant. + * + * @category userTypes + * @category enums + * @category generated + */ +export type BurnArgs = beet.DataEnumKeyAsKind; + +export const isBurnArgsV1 = (x: BurnArgs): x is BurnArgs & { __kind: 'V1' } => x.__kind === 'V1'; + +/** + * @category userTypes + * @category generated + */ +export const burnArgsBeet = beet.dataEnum([ + [ + 'V1', + new beet.FixableBeetArgsStruct( + [['authorizationData', beet.coption(authorizationDataBeet)]], + 'BurnArgsRecord["V1"]', + ), + ], +]) as beet.FixableBeet; diff --git a/token-metadata/js/src/generated/types/CollectionDetails.ts b/token-metadata/js/src/generated/types/CollectionDetails.ts index b6be469632..10427a39e9 100644 --- a/token-metadata/js/src/generated/types/CollectionDetails.ts +++ b/token-metadata/js/src/generated/types/CollectionDetails.ts @@ -48,4 +48,4 @@ export const collectionDetailsBeet = beet.dataEnum([ 'CollectionDetailsRecord["V1"]', ), ], -]) as beet.FixableBeet; +]) as beet.FixableBeet; diff --git a/token-metadata/js/src/generated/types/CollectionDetailsToggle.ts b/token-metadata/js/src/generated/types/CollectionDetailsToggle.ts new file mode 100644 index 0000000000..d01335aa76 --- /dev/null +++ b/token-metadata/js/src/generated/types/CollectionDetailsToggle.ts @@ -0,0 +1,62 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import { CollectionDetails, collectionDetailsBeet } from './CollectionDetails'; +/** + * This type is used to derive the {@link CollectionDetailsToggle} type as well as the de/serializer. + * However don't refer to it in your code but use the {@link CollectionDetailsToggle} type instead. + * + * @category userTypes + * @category enums + * @category generated + * @private + */ +export type CollectionDetailsToggleRecord = { + None: void /* scalar variant */; + Clear: void /* scalar variant */; + Set: { fields: [CollectionDetails] }; +}; + +/** + * Union type respresenting the CollectionDetailsToggle data enum defined in Rust. + * + * NOTE: that it includes a `__kind` property which allows to narrow types in + * switch/if statements. + * Additionally `isCollectionDetailsToggle*` type guards are exposed below to narrow to a specific variant. + * + * @category userTypes + * @category enums + * @category generated + */ +export type CollectionDetailsToggle = beet.DataEnumKeyAsKind; + +export const isCollectionDetailsToggleNone = ( + x: CollectionDetailsToggle, +): x is CollectionDetailsToggle & { __kind: 'None' } => x.__kind === 'None'; +export const isCollectionDetailsToggleClear = ( + x: CollectionDetailsToggle, +): x is CollectionDetailsToggle & { __kind: 'Clear' } => x.__kind === 'Clear'; +export const isCollectionDetailsToggleSet = ( + x: CollectionDetailsToggle, +): x is CollectionDetailsToggle & { __kind: 'Set' } => x.__kind === 'Set'; + +/** + * @category userTypes + * @category generated + */ +export const collectionDetailsToggleBeet = beet.dataEnum([ + ['None', beet.unit], + ['Clear', beet.unit], + [ + 'Set', + new beet.FixableBeetArgsStruct( + [['fields', beet.tuple([collectionDetailsBeet])]], + 'CollectionDetailsToggleRecord["Set"]', + ), + ], +]) as beet.FixableBeet; diff --git a/token-metadata/js/src/generated/types/CollectionToggle.ts b/token-metadata/js/src/generated/types/CollectionToggle.ts new file mode 100644 index 0000000000..a3dcd54b3a --- /dev/null +++ b/token-metadata/js/src/generated/types/CollectionToggle.ts @@ -0,0 +1,62 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import { Collection, collectionBeet } from './Collection'; +/** + * This type is used to derive the {@link CollectionToggle} type as well as the de/serializer. + * However don't refer to it in your code but use the {@link CollectionToggle} type instead. + * + * @category userTypes + * @category enums + * @category generated + * @private + */ +export type CollectionToggleRecord = { + None: void /* scalar variant */; + Clear: void /* scalar variant */; + Set: { fields: [Collection] }; +}; + +/** + * Union type respresenting the CollectionToggle data enum defined in Rust. + * + * NOTE: that it includes a `__kind` property which allows to narrow types in + * switch/if statements. + * Additionally `isCollectionToggle*` type guards are exposed below to narrow to a specific variant. + * + * @category userTypes + * @category enums + * @category generated + */ +export type CollectionToggle = beet.DataEnumKeyAsKind; + +export const isCollectionToggleNone = ( + x: CollectionToggle, +): x is CollectionToggle & { __kind: 'None' } => x.__kind === 'None'; +export const isCollectionToggleClear = ( + x: CollectionToggle, +): x is CollectionToggle & { __kind: 'Clear' } => x.__kind === 'Clear'; +export const isCollectionToggleSet = ( + x: CollectionToggle, +): x is CollectionToggle & { __kind: 'Set' } => x.__kind === 'Set'; + +/** + * @category userTypes + * @category generated + */ +export const collectionToggleBeet = beet.dataEnum([ + ['None', beet.unit], + ['Clear', beet.unit], + [ + 'Set', + new beet.BeetArgsStruct( + [['fields', beet.fixedSizeTuple([collectionBeet])]], + 'CollectionToggleRecord["Set"]', + ), + ], +]) as beet.FixableBeet; diff --git a/token-metadata/js/src/generated/types/CreateArgs.ts b/token-metadata/js/src/generated/types/CreateArgs.ts new file mode 100644 index 0000000000..e2b47981f7 --- /dev/null +++ b/token-metadata/js/src/generated/types/CreateArgs.ts @@ -0,0 +1,60 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import { AssetData, assetDataBeet } from './AssetData'; +import { PrintSupply, printSupplyBeet } from './PrintSupply'; +/** + * This type is used to derive the {@link CreateArgs} type as well as the de/serializer. + * However don't refer to it in your code but use the {@link CreateArgs} type instead. + * + * @category userTypes + * @category enums + * @category generated + * @private + */ +export type CreateArgsRecord = { + V1: { + assetData: AssetData; + decimals: beet.COption; + printSupply: beet.COption; + }; +}; + +/** + * Union type respresenting the CreateArgs data enum defined in Rust. + * + * NOTE: that it includes a `__kind` property which allows to narrow types in + * switch/if statements. + * Additionally `isCreateArgs*` type guards are exposed below to narrow to a specific variant. + * + * @category userTypes + * @category enums + * @category generated + */ +export type CreateArgs = beet.DataEnumKeyAsKind; + +export const isCreateArgsV1 = (x: CreateArgs): x is CreateArgs & { __kind: 'V1' } => + x.__kind === 'V1'; + +/** + * @category userTypes + * @category generated + */ +export const createArgsBeet = beet.dataEnum([ + [ + 'V1', + new beet.FixableBeetArgsStruct( + [ + ['assetData', assetDataBeet], + ['decimals', beet.coption(beet.u8)], + ['printSupply', beet.coption(printSupplyBeet)], + ], + 'CreateArgsRecord["V1"]', + ), + ], +]) as beet.FixableBeet; diff --git a/token-metadata/js/src/generated/types/DelegateArgs.ts b/token-metadata/js/src/generated/types/DelegateArgs.ts new file mode 100644 index 0000000000..41e7f0b4f6 --- /dev/null +++ b/token-metadata/js/src/generated/types/DelegateArgs.ts @@ -0,0 +1,135 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import { AuthorizationData, authorizationDataBeet } from './AuthorizationData'; +/** + * This type is used to derive the {@link DelegateArgs} type as well as the de/serializer. + * However don't refer to it in your code but use the {@link DelegateArgs} type instead. + * + * @category userTypes + * @category enums + * @category generated + * @private + */ +export type DelegateArgsRecord = { + CollectionV1: { authorizationData: beet.COption }; + SaleV1: { amount: beet.bignum; authorizationData: beet.COption }; + TransferV1: { amount: beet.bignum; authorizationData: beet.COption }; + UpdateV1: { authorizationData: beet.COption }; + UtilityV1: { amount: beet.bignum; authorizationData: beet.COption }; + StakingV1: { amount: beet.bignum; authorizationData: beet.COption }; + StandardV1: { amount: beet.bignum }; +}; + +/** + * Union type respresenting the DelegateArgs data enum defined in Rust. + * + * NOTE: that it includes a `__kind` property which allows to narrow types in + * switch/if statements. + * Additionally `isDelegateArgs*` type guards are exposed below to narrow to a specific variant. + * + * @category userTypes + * @category enums + * @category generated + */ +export type DelegateArgs = beet.DataEnumKeyAsKind; + +export const isDelegateArgsCollectionV1 = ( + x: DelegateArgs, +): x is DelegateArgs & { __kind: 'CollectionV1' } => x.__kind === 'CollectionV1'; +export const isDelegateArgsSaleV1 = (x: DelegateArgs): x is DelegateArgs & { __kind: 'SaleV1' } => + x.__kind === 'SaleV1'; +export const isDelegateArgsTransferV1 = ( + x: DelegateArgs, +): x is DelegateArgs & { __kind: 'TransferV1' } => x.__kind === 'TransferV1'; +export const isDelegateArgsUpdateV1 = ( + x: DelegateArgs, +): x is DelegateArgs & { __kind: 'UpdateV1' } => x.__kind === 'UpdateV1'; +export const isDelegateArgsUtilityV1 = ( + x: DelegateArgs, +): x is DelegateArgs & { __kind: 'UtilityV1' } => x.__kind === 'UtilityV1'; +export const isDelegateArgsStakingV1 = ( + x: DelegateArgs, +): x is DelegateArgs & { __kind: 'StakingV1' } => x.__kind === 'StakingV1'; +export const isDelegateArgsStandardV1 = ( + x: DelegateArgs, +): x is DelegateArgs & { __kind: 'StandardV1' } => x.__kind === 'StandardV1'; + +/** + * @category userTypes + * @category generated + */ +export const delegateArgsBeet = beet.dataEnum([ + [ + 'CollectionV1', + new beet.FixableBeetArgsStruct( + [['authorizationData', beet.coption(authorizationDataBeet)]], + 'DelegateArgsRecord["CollectionV1"]', + ), + ], + + [ + 'SaleV1', + new beet.FixableBeetArgsStruct( + [ + ['amount', beet.u64], + ['authorizationData', beet.coption(authorizationDataBeet)], + ], + 'DelegateArgsRecord["SaleV1"]', + ), + ], + + [ + 'TransferV1', + new beet.FixableBeetArgsStruct( + [ + ['amount', beet.u64], + ['authorizationData', beet.coption(authorizationDataBeet)], + ], + 'DelegateArgsRecord["TransferV1"]', + ), + ], + + [ + 'UpdateV1', + new beet.FixableBeetArgsStruct( + [['authorizationData', beet.coption(authorizationDataBeet)]], + 'DelegateArgsRecord["UpdateV1"]', + ), + ], + + [ + 'UtilityV1', + new beet.FixableBeetArgsStruct( + [ + ['amount', beet.u64], + ['authorizationData', beet.coption(authorizationDataBeet)], + ], + 'DelegateArgsRecord["UtilityV1"]', + ), + ], + + [ + 'StakingV1', + new beet.FixableBeetArgsStruct( + [ + ['amount', beet.u64], + ['authorizationData', beet.coption(authorizationDataBeet)], + ], + 'DelegateArgsRecord["StakingV1"]', + ), + ], + + [ + 'StandardV1', + new beet.BeetArgsStruct( + [['amount', beet.u64]], + 'DelegateArgsRecord["StandardV1"]', + ), + ], +]) as beet.FixableBeet; diff --git a/token-metadata/js/src/generated/types/EscrowAuthority.ts b/token-metadata/js/src/generated/types/EscrowAuthority.ts index aafb1a575f..d98cfd9f1f 100644 --- a/token-metadata/js/src/generated/types/EscrowAuthority.ts +++ b/token-metadata/js/src/generated/types/EscrowAuthority.ts @@ -55,4 +55,4 @@ export const escrowAuthorityBeet = beet.dataEnum([ 'EscrowAuthorityRecord["Creator"]', ), ], -]) as beet.FixableBeet; +]) as beet.FixableBeet; diff --git a/token-metadata/js/src/generated/types/Key.ts b/token-metadata/js/src/generated/types/Key.ts index b0d07d0416..46d2470c30 100644 --- a/token-metadata/js/src/generated/types/Key.ts +++ b/token-metadata/js/src/generated/types/Key.ts @@ -22,6 +22,8 @@ export enum Key { UseAuthorityRecord, CollectionAuthorityRecord, TokenOwnedEscrow, + TokenRecord, + MetadataDelegate, } /** diff --git a/token-metadata/js/src/generated/types/LeafInfo.ts b/token-metadata/js/src/generated/types/LeafInfo.ts new file mode 100644 index 0000000000..5efec2a271 --- /dev/null +++ b/token-metadata/js/src/generated/types/LeafInfo.ts @@ -0,0 +1,24 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +export type LeafInfo = { + leaf: number[] /* size: 32 */; + proof: number[] /* size: 32 */[]; +}; + +/** + * @category userTypes + * @category generated + */ +export const leafInfoBeet = new beet.FixableBeetArgsStruct( + [ + ['leaf', beet.uniformFixedSizeArray(beet.u8, 32)], + ['proof', beet.array(beet.uniformFixedSizeArray(beet.u8, 32))], + ], + 'LeafInfo', +); diff --git a/token-metadata/js/src/generated/types/LockArgs.ts b/token-metadata/js/src/generated/types/LockArgs.ts new file mode 100644 index 0000000000..bbcd8a0e03 --- /dev/null +++ b/token-metadata/js/src/generated/types/LockArgs.ts @@ -0,0 +1,50 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import { AuthorizationData, authorizationDataBeet } from './AuthorizationData'; +/** + * This type is used to derive the {@link LockArgs} type as well as the de/serializer. + * However don't refer to it in your code but use the {@link LockArgs} type instead. + * + * @category userTypes + * @category enums + * @category generated + * @private + */ +export type LockArgsRecord = { + V1: { authorizationData: beet.COption }; +}; + +/** + * Union type respresenting the LockArgs data enum defined in Rust. + * + * NOTE: that it includes a `__kind` property which allows to narrow types in + * switch/if statements. + * Additionally `isLockArgs*` type guards are exposed below to narrow to a specific variant. + * + * @category userTypes + * @category enums + * @category generated + */ +export type LockArgs = beet.DataEnumKeyAsKind; + +export const isLockArgsV1 = (x: LockArgs): x is LockArgs & { __kind: 'V1' } => x.__kind === 'V1'; + +/** + * @category userTypes + * @category generated + */ +export const lockArgsBeet = beet.dataEnum([ + [ + 'V1', + new beet.FixableBeetArgsStruct( + [['authorizationData', beet.coption(authorizationDataBeet)]], + 'LockArgsRecord["V1"]', + ), + ], +]) as beet.FixableBeet; diff --git a/token-metadata/js/src/generated/types/MetadataDelegateRole.ts b/token-metadata/js/src/generated/types/MetadataDelegateRole.ts new file mode 100644 index 0000000000..c0bfca191f --- /dev/null +++ b/token-metadata/js/src/generated/types/MetadataDelegateRole.ts @@ -0,0 +1,26 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +/** + * @category enums + * @category generated + */ +export enum MetadataDelegateRole { + Authority, + Collection, + Use, + Update, +} + +/** + * @category userTypes + * @category generated + */ +export const metadataDelegateRoleBeet = beet.fixedScalarEnum( + MetadataDelegateRole, +) as beet.FixedSizeBeet; diff --git a/token-metadata/js/src/generated/types/MigrateArgs.ts b/token-metadata/js/src/generated/types/MigrateArgs.ts new file mode 100644 index 0000000000..dd017fb22b --- /dev/null +++ b/token-metadata/js/src/generated/types/MigrateArgs.ts @@ -0,0 +1,56 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as web3 from '@solana/web3.js'; +import * as beet from '@metaplex-foundation/beet'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; +import { MigrationType, migrationTypeBeet } from './MigrationType'; +/** + * This type is used to derive the {@link MigrateArgs} type as well as the de/serializer. + * However don't refer to it in your code but use the {@link MigrateArgs} type instead. + * + * @category userTypes + * @category enums + * @category generated + * @private + */ +export type MigrateArgsRecord = { + V1: { migrationType: MigrationType; ruleSet: beet.COption }; +}; + +/** + * Union type respresenting the MigrateArgs data enum defined in Rust. + * + * NOTE: that it includes a `__kind` property which allows to narrow types in + * switch/if statements. + * Additionally `isMigrateArgs*` type guards are exposed below to narrow to a specific variant. + * + * @category userTypes + * @category enums + * @category generated + */ +export type MigrateArgs = beet.DataEnumKeyAsKind; + +export const isMigrateArgsV1 = (x: MigrateArgs): x is MigrateArgs & { __kind: 'V1' } => + x.__kind === 'V1'; + +/** + * @category userTypes + * @category generated + */ +export const migrateArgsBeet = beet.dataEnum([ + [ + 'V1', + new beet.FixableBeetArgsStruct( + [ + ['migrationType', migrationTypeBeet], + ['ruleSet', beet.coption(beetSolana.publicKey)], + ], + 'MigrateArgsRecord["V1"]', + ), + ], +]) as beet.FixableBeet; diff --git a/token-metadata/js/src/generated/types/MigrationType.ts b/token-metadata/js/src/generated/types/MigrationType.ts new file mode 100644 index 0000000000..755822de1a --- /dev/null +++ b/token-metadata/js/src/generated/types/MigrationType.ts @@ -0,0 +1,25 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +/** + * @category enums + * @category generated + */ +export enum MigrationType { + CollectionV1, + ProgrammableV1, +} + +/** + * @category userTypes + * @category generated + */ +export const migrationTypeBeet = beet.fixedScalarEnum(MigrationType) as beet.FixedSizeBeet< + MigrationType, + MigrationType +>; diff --git a/token-metadata/js/src/generated/types/MintArgs.ts b/token-metadata/js/src/generated/types/MintArgs.ts new file mode 100644 index 0000000000..7eadd0e96a --- /dev/null +++ b/token-metadata/js/src/generated/types/MintArgs.ts @@ -0,0 +1,53 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import { AuthorizationData, authorizationDataBeet } from './AuthorizationData'; +/** + * This type is used to derive the {@link MintArgs} type as well as the de/serializer. + * However don't refer to it in your code but use the {@link MintArgs} type instead. + * + * @category userTypes + * @category enums + * @category generated + * @private + */ +export type MintArgsRecord = { + V1: { amount: beet.bignum; authorizationData: beet.COption }; +}; + +/** + * Union type respresenting the MintArgs data enum defined in Rust. + * + * NOTE: that it includes a `__kind` property which allows to narrow types in + * switch/if statements. + * Additionally `isMintArgs*` type guards are exposed below to narrow to a specific variant. + * + * @category userTypes + * @category enums + * @category generated + */ +export type MintArgs = beet.DataEnumKeyAsKind; + +export const isMintArgsV1 = (x: MintArgs): x is MintArgs & { __kind: 'V1' } => x.__kind === 'V1'; + +/** + * @category userTypes + * @category generated + */ +export const mintArgsBeet = beet.dataEnum([ + [ + 'V1', + new beet.FixableBeetArgsStruct( + [ + ['amount', beet.u64], + ['authorizationData', beet.coption(authorizationDataBeet)], + ], + 'MintArgsRecord["V1"]', + ), + ], +]) as beet.FixableBeet; diff --git a/token-metadata/js/src/generated/types/Payload.ts b/token-metadata/js/src/generated/types/Payload.ts new file mode 100644 index 0000000000..30542562f5 --- /dev/null +++ b/token-metadata/js/src/generated/types/Payload.ts @@ -0,0 +1,22 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import { PayloadKey, payloadKeyBeet } from './PayloadKey'; +import { PayloadType, payloadTypeBeet } from './PayloadType'; +export type Payload = { + map: Map; +}; + +/** + * @category userTypes + * @category generated + */ +export const payloadBeet = new beet.FixableBeetArgsStruct( + [['map', beet.map(payloadKeyBeet, payloadTypeBeet)]], + 'Payload', +); diff --git a/token-metadata/js/src/generated/types/PayloadKey.ts b/token-metadata/js/src/generated/types/PayloadKey.ts new file mode 100644 index 0000000000..c152a2b890 --- /dev/null +++ b/token-metadata/js/src/generated/types/PayloadKey.ts @@ -0,0 +1,27 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +/** + * @category enums + * @category generated + */ +export enum PayloadKey { + Target, + Holder, + Authority, + Amount, +} + +/** + * @category userTypes + * @category generated + */ +export const payloadKeyBeet = beet.fixedScalarEnum(PayloadKey) as beet.FixedSizeBeet< + PayloadKey, + PayloadKey +>; diff --git a/token-metadata/js/src/generated/types/PayloadType.ts b/token-metadata/js/src/generated/types/PayloadType.ts new file mode 100644 index 0000000000..7110ec54f2 --- /dev/null +++ b/token-metadata/js/src/generated/types/PayloadType.ts @@ -0,0 +1,85 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as web3 from '@solana/web3.js'; +import * as beet from '@metaplex-foundation/beet'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; +import { SeedsVec, seedsVecBeet } from './SeedsVec'; +import { LeafInfo, leafInfoBeet } from './LeafInfo'; +/** + * This type is used to derive the {@link PayloadType} type as well as the de/serializer. + * However don't refer to it in your code but use the {@link PayloadType} type instead. + * + * @category userTypes + * @category enums + * @category generated + * @private + */ +export type PayloadTypeRecord = { + Pubkey: { fields: [web3.PublicKey] }; + Seeds: { fields: [SeedsVec] }; + MerkleProof: { fields: [LeafInfo] }; + Number: { fields: [beet.bignum] }; +}; + +/** + * Union type respresenting the PayloadType data enum defined in Rust. + * + * NOTE: that it includes a `__kind` property which allows to narrow types in + * switch/if statements. + * Additionally `isPayloadType*` type guards are exposed below to narrow to a specific variant. + * + * @category userTypes + * @category enums + * @category generated + */ +export type PayloadType = beet.DataEnumKeyAsKind; + +export const isPayloadTypePubkey = (x: PayloadType): x is PayloadType & { __kind: 'Pubkey' } => + x.__kind === 'Pubkey'; +export const isPayloadTypeSeeds = (x: PayloadType): x is PayloadType & { __kind: 'Seeds' } => + x.__kind === 'Seeds'; +export const isPayloadTypeMerkleProof = ( + x: PayloadType, +): x is PayloadType & { __kind: 'MerkleProof' } => x.__kind === 'MerkleProof'; +export const isPayloadTypeNumber = (x: PayloadType): x is PayloadType & { __kind: 'Number' } => + x.__kind === 'Number'; + +/** + * @category userTypes + * @category generated + */ +export const payloadTypeBeet = beet.dataEnum([ + [ + 'Pubkey', + new beet.BeetArgsStruct( + [['fields', beet.fixedSizeTuple([beetSolana.publicKey])]], + 'PayloadTypeRecord["Pubkey"]', + ), + ], + [ + 'Seeds', + new beet.FixableBeetArgsStruct( + [['fields', beet.tuple([seedsVecBeet])]], + 'PayloadTypeRecord["Seeds"]', + ), + ], + [ + 'MerkleProof', + new beet.FixableBeetArgsStruct( + [['fields', beet.tuple([leafInfoBeet])]], + 'PayloadTypeRecord["MerkleProof"]', + ), + ], + [ + 'Number', + new beet.BeetArgsStruct( + [['fields', beet.fixedSizeTuple([beet.u64])]], + 'PayloadTypeRecord["Number"]', + ), + ], +]) as beet.FixableBeet; diff --git a/token-metadata/js/src/generated/types/PrintSupply.ts b/token-metadata/js/src/generated/types/PrintSupply.ts new file mode 100644 index 0000000000..f20a8db8d4 --- /dev/null +++ b/token-metadata/js/src/generated/types/PrintSupply.ts @@ -0,0 +1,59 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +/** + * This type is used to derive the {@link PrintSupply} type as well as the de/serializer. + * However don't refer to it in your code but use the {@link PrintSupply} type instead. + * + * @category userTypes + * @category enums + * @category generated + * @private + */ +export type PrintSupplyRecord = { + Zero: void /* scalar variant */; + Limited: { fields: [beet.bignum] }; + Unlimited: void /* scalar variant */; +}; + +/** + * Union type respresenting the PrintSupply data enum defined in Rust. + * + * NOTE: that it includes a `__kind` property which allows to narrow types in + * switch/if statements. + * Additionally `isPrintSupply*` type guards are exposed below to narrow to a specific variant. + * + * @category userTypes + * @category enums + * @category generated + */ +export type PrintSupply = beet.DataEnumKeyAsKind; + +export const isPrintSupplyZero = (x: PrintSupply): x is PrintSupply & { __kind: 'Zero' } => + x.__kind === 'Zero'; +export const isPrintSupplyLimited = (x: PrintSupply): x is PrintSupply & { __kind: 'Limited' } => + x.__kind === 'Limited'; +export const isPrintSupplyUnlimited = ( + x: PrintSupply, +): x is PrintSupply & { __kind: 'Unlimited' } => x.__kind === 'Unlimited'; + +/** + * @category userTypes + * @category generated + */ +export const printSupplyBeet = beet.dataEnum([ + ['Zero', beet.unit], + [ + 'Limited', + new beet.BeetArgsStruct( + [['fields', beet.fixedSizeTuple([beet.u64])]], + 'PrintSupplyRecord["Limited"]', + ), + ], + ['Unlimited', beet.unit], +]) as beet.FixableBeet; diff --git a/token-metadata/js/src/generated/types/ProgrammableConfig.ts b/token-metadata/js/src/generated/types/ProgrammableConfig.ts new file mode 100644 index 0000000000..d7c5b52a05 --- /dev/null +++ b/token-metadata/js/src/generated/types/ProgrammableConfig.ts @@ -0,0 +1,53 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as web3 from '@solana/web3.js'; +import * as beet from '@metaplex-foundation/beet'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; +/** + * This type is used to derive the {@link ProgrammableConfig} type as well as the de/serializer. + * However don't refer to it in your code but use the {@link ProgrammableConfig} type instead. + * + * @category userTypes + * @category enums + * @category generated + * @private + */ +export type ProgrammableConfigRecord = { + V1: { ruleSet: beet.COption }; +}; + +/** + * Union type respresenting the ProgrammableConfig data enum defined in Rust. + * + * NOTE: that it includes a `__kind` property which allows to narrow types in + * switch/if statements. + * Additionally `isProgrammableConfig*` type guards are exposed below to narrow to a specific variant. + * + * @category userTypes + * @category enums + * @category generated + */ +export type ProgrammableConfig = beet.DataEnumKeyAsKind; + +export const isProgrammableConfigV1 = ( + x: ProgrammableConfig, +): x is ProgrammableConfig & { __kind: 'V1' } => x.__kind === 'V1'; + +/** + * @category userTypes + * @category generated + */ +export const programmableConfigBeet = beet.dataEnum([ + [ + 'V1', + new beet.FixableBeetArgsStruct( + [['ruleSet', beet.coption(beetSolana.publicKey)]], + 'ProgrammableConfigRecord["V1"]', + ), + ], +]) as beet.FixableBeet; diff --git a/token-metadata/js/src/generated/types/RevokeArgs.ts b/token-metadata/js/src/generated/types/RevokeArgs.ts new file mode 100644 index 0000000000..a19db2f470 --- /dev/null +++ b/token-metadata/js/src/generated/types/RevokeArgs.ts @@ -0,0 +1,30 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +/** + * @category enums + * @category generated + */ +export enum RevokeArgs { + CollectionV1, + SaleV1, + TransferV1, + UpdateV1, + UtilityV1, + StakingV1, + StandardV1, +} + +/** + * @category userTypes + * @category generated + */ +export const revokeArgsBeet = beet.fixedScalarEnum(RevokeArgs) as beet.FixedSizeBeet< + RevokeArgs, + RevokeArgs +>; diff --git a/token-metadata/js/src/generated/types/RuleSetToggle.ts b/token-metadata/js/src/generated/types/RuleSetToggle.ts new file mode 100644 index 0000000000..6cd5e45e17 --- /dev/null +++ b/token-metadata/js/src/generated/types/RuleSetToggle.ts @@ -0,0 +1,60 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as web3 from '@solana/web3.js'; +import * as beet from '@metaplex-foundation/beet'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; +/** + * This type is used to derive the {@link RuleSetToggle} type as well as the de/serializer. + * However don't refer to it in your code but use the {@link RuleSetToggle} type instead. + * + * @category userTypes + * @category enums + * @category generated + * @private + */ +export type RuleSetToggleRecord = { + None: void /* scalar variant */; + Clear: void /* scalar variant */; + Set: { fields: [web3.PublicKey] }; +}; + +/** + * Union type respresenting the RuleSetToggle data enum defined in Rust. + * + * NOTE: that it includes a `__kind` property which allows to narrow types in + * switch/if statements. + * Additionally `isRuleSetToggle*` type guards are exposed below to narrow to a specific variant. + * + * @category userTypes + * @category enums + * @category generated + */ +export type RuleSetToggle = beet.DataEnumKeyAsKind; + +export const isRuleSetToggleNone = (x: RuleSetToggle): x is RuleSetToggle & { __kind: 'None' } => + x.__kind === 'None'; +export const isRuleSetToggleClear = (x: RuleSetToggle): x is RuleSetToggle & { __kind: 'Clear' } => + x.__kind === 'Clear'; +export const isRuleSetToggleSet = (x: RuleSetToggle): x is RuleSetToggle & { __kind: 'Set' } => + x.__kind === 'Set'; + +/** + * @category userTypes + * @category generated + */ +export const ruleSetToggleBeet = beet.dataEnum([ + ['None', beet.unit], + ['Clear', beet.unit], + [ + 'Set', + new beet.BeetArgsStruct( + [['fields', beet.fixedSizeTuple([beetSolana.publicKey])]], + 'RuleSetToggleRecord["Set"]', + ), + ], +]) as beet.FixableBeet; diff --git a/token-metadata/js/src/generated/types/SeedsVec.ts b/token-metadata/js/src/generated/types/SeedsVec.ts new file mode 100644 index 0000000000..929befdb39 --- /dev/null +++ b/token-metadata/js/src/generated/types/SeedsVec.ts @@ -0,0 +1,20 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +export type SeedsVec = { + seeds: Uint8Array[]; +}; + +/** + * @category userTypes + * @category generated + */ +export const seedsVecBeet = new beet.FixableBeetArgsStruct( + [['seeds', beet.array(beet.bytes)]], + 'SeedsVec', +); diff --git a/token-metadata/js/src/generated/types/TokenDelegateRole.ts b/token-metadata/js/src/generated/types/TokenDelegateRole.ts new file mode 100644 index 0000000000..02b4dcbbd4 --- /dev/null +++ b/token-metadata/js/src/generated/types/TokenDelegateRole.ts @@ -0,0 +1,29 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +/** + * @category enums + * @category generated + */ +export enum TokenDelegateRole { + Sale, + Transfer, + Utility, + Staking, + Standard, + Migration, +} + +/** + * @category userTypes + * @category generated + */ +export const tokenDelegateRoleBeet = beet.fixedScalarEnum(TokenDelegateRole) as beet.FixedSizeBeet< + TokenDelegateRole, + TokenDelegateRole +>; diff --git a/token-metadata/js/src/generated/types/TokenStandard.ts b/token-metadata/js/src/generated/types/TokenStandard.ts index dc449df040..e4122006a5 100644 --- a/token-metadata/js/src/generated/types/TokenStandard.ts +++ b/token-metadata/js/src/generated/types/TokenStandard.ts @@ -15,6 +15,7 @@ export enum TokenStandard { FungibleAsset, Fungible, NonFungibleEdition, + ProgrammableNonFungible, } /** diff --git a/token-metadata/js/src/generated/types/TokenState.ts b/token-metadata/js/src/generated/types/TokenState.ts new file mode 100644 index 0000000000..afc24f5c1c --- /dev/null +++ b/token-metadata/js/src/generated/types/TokenState.ts @@ -0,0 +1,26 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +/** + * @category enums + * @category generated + */ +export enum TokenState { + Unlocked, + Locked, + Listed, +} + +/** + * @category userTypes + * @category generated + */ +export const tokenStateBeet = beet.fixedScalarEnum(TokenState) as beet.FixedSizeBeet< + TokenState, + TokenState +>; diff --git a/token-metadata/js/src/generated/types/TransferArgs.ts b/token-metadata/js/src/generated/types/TransferArgs.ts new file mode 100644 index 0000000000..c6a2cffb68 --- /dev/null +++ b/token-metadata/js/src/generated/types/TransferArgs.ts @@ -0,0 +1,54 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import { AuthorizationData, authorizationDataBeet } from './AuthorizationData'; +/** + * This type is used to derive the {@link TransferArgs} type as well as the de/serializer. + * However don't refer to it in your code but use the {@link TransferArgs} type instead. + * + * @category userTypes + * @category enums + * @category generated + * @private + */ +export type TransferArgsRecord = { + V1: { amount: beet.bignum; authorizationData: beet.COption }; +}; + +/** + * Union type respresenting the TransferArgs data enum defined in Rust. + * + * NOTE: that it includes a `__kind` property which allows to narrow types in + * switch/if statements. + * Additionally `isTransferArgs*` type guards are exposed below to narrow to a specific variant. + * + * @category userTypes + * @category enums + * @category generated + */ +export type TransferArgs = beet.DataEnumKeyAsKind; + +export const isTransferArgsV1 = (x: TransferArgs): x is TransferArgs & { __kind: 'V1' } => + x.__kind === 'V1'; + +/** + * @category userTypes + * @category generated + */ +export const transferArgsBeet = beet.dataEnum([ + [ + 'V1', + new beet.FixableBeetArgsStruct( + [ + ['amount', beet.u64], + ['authorizationData', beet.coption(authorizationDataBeet)], + ], + 'TransferArgsRecord["V1"]', + ), + ], +]) as beet.FixableBeet; diff --git a/token-metadata/js/src/generated/types/UnlockArgs.ts b/token-metadata/js/src/generated/types/UnlockArgs.ts new file mode 100644 index 0000000000..b5598b293c --- /dev/null +++ b/token-metadata/js/src/generated/types/UnlockArgs.ts @@ -0,0 +1,51 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import { AuthorizationData, authorizationDataBeet } from './AuthorizationData'; +/** + * This type is used to derive the {@link UnlockArgs} type as well as the de/serializer. + * However don't refer to it in your code but use the {@link UnlockArgs} type instead. + * + * @category userTypes + * @category enums + * @category generated + * @private + */ +export type UnlockArgsRecord = { + V1: { authorizationData: beet.COption }; +}; + +/** + * Union type respresenting the UnlockArgs data enum defined in Rust. + * + * NOTE: that it includes a `__kind` property which allows to narrow types in + * switch/if statements. + * Additionally `isUnlockArgs*` type guards are exposed below to narrow to a specific variant. + * + * @category userTypes + * @category enums + * @category generated + */ +export type UnlockArgs = beet.DataEnumKeyAsKind; + +export const isUnlockArgsV1 = (x: UnlockArgs): x is UnlockArgs & { __kind: 'V1' } => + x.__kind === 'V1'; + +/** + * @category userTypes + * @category generated + */ +export const unlockArgsBeet = beet.dataEnum([ + [ + 'V1', + new beet.FixableBeetArgsStruct( + [['authorizationData', beet.coption(authorizationDataBeet)]], + 'UnlockArgsRecord["V1"]', + ), + ], +]) as beet.FixableBeet; diff --git a/token-metadata/js/src/generated/types/UpdateArgs.ts b/token-metadata/js/src/generated/types/UpdateArgs.ts new file mode 100644 index 0000000000..7b731d9f0c --- /dev/null +++ b/token-metadata/js/src/generated/types/UpdateArgs.ts @@ -0,0 +1,78 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as web3 from '@solana/web3.js'; +import * as beet from '@metaplex-foundation/beet'; +import * as beetSolana from '@metaplex-foundation/beet-solana'; +import { Data, dataBeet } from './Data'; +import { CollectionToggle, collectionToggleBeet } from './CollectionToggle'; +import { CollectionDetailsToggle, collectionDetailsToggleBeet } from './CollectionDetailsToggle'; +import { UsesToggle, usesToggleBeet } from './UsesToggle'; +import { RuleSetToggle, ruleSetToggleBeet } from './RuleSetToggle'; +import { AuthorizationData, authorizationDataBeet } from './AuthorizationData'; +/** + * This type is used to derive the {@link UpdateArgs} type as well as the de/serializer. + * However don't refer to it in your code but use the {@link UpdateArgs} type instead. + * + * @category userTypes + * @category enums + * @category generated + * @private + */ +export type UpdateArgsRecord = { + V1: { + newUpdateAuthority: beet.COption; + data: beet.COption; + primarySaleHappened: beet.COption; + isMutable: beet.COption; + collection: CollectionToggle; + collectionDetails: CollectionDetailsToggle; + uses: UsesToggle; + ruleSet: RuleSetToggle; + authorizationData: beet.COption; + }; +}; + +/** + * Union type respresenting the UpdateArgs data enum defined in Rust. + * + * NOTE: that it includes a `__kind` property which allows to narrow types in + * switch/if statements. + * Additionally `isUpdateArgs*` type guards are exposed below to narrow to a specific variant. + * + * @category userTypes + * @category enums + * @category generated + */ +export type UpdateArgs = beet.DataEnumKeyAsKind; + +export const isUpdateArgsV1 = (x: UpdateArgs): x is UpdateArgs & { __kind: 'V1' } => + x.__kind === 'V1'; + +/** + * @category userTypes + * @category generated + */ +export const updateArgsBeet = beet.dataEnum([ + [ + 'V1', + new beet.FixableBeetArgsStruct( + [ + ['newUpdateAuthority', beet.coption(beetSolana.publicKey)], + ['data', beet.coption(dataBeet)], + ['primarySaleHappened', beet.coption(beet.bool)], + ['isMutable', beet.coption(beet.bool)], + ['collection', collectionToggleBeet], + ['collectionDetails', collectionDetailsToggleBeet], + ['uses', usesToggleBeet], + ['ruleSet', ruleSetToggleBeet], + ['authorizationData', beet.coption(authorizationDataBeet)], + ], + 'UpdateArgsRecord["V1"]', + ), + ], +]) as beet.FixableBeet; diff --git a/token-metadata/js/src/generated/types/UseArgs.ts b/token-metadata/js/src/generated/types/UseArgs.ts new file mode 100644 index 0000000000..4a89f7e57a --- /dev/null +++ b/token-metadata/js/src/generated/types/UseArgs.ts @@ -0,0 +1,50 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import { AuthorizationData, authorizationDataBeet } from './AuthorizationData'; +/** + * This type is used to derive the {@link UseArgs} type as well as the de/serializer. + * However don't refer to it in your code but use the {@link UseArgs} type instead. + * + * @category userTypes + * @category enums + * @category generated + * @private + */ +export type UseArgsRecord = { + V1: { authorizationData: beet.COption }; +}; + +/** + * Union type respresenting the UseArgs data enum defined in Rust. + * + * NOTE: that it includes a `__kind` property which allows to narrow types in + * switch/if statements. + * Additionally `isUseArgs*` type guards are exposed below to narrow to a specific variant. + * + * @category userTypes + * @category enums + * @category generated + */ +export type UseArgs = beet.DataEnumKeyAsKind; + +export const isUseArgsV1 = (x: UseArgs): x is UseArgs & { __kind: 'V1' } => x.__kind === 'V1'; + +/** + * @category userTypes + * @category generated + */ +export const useArgsBeet = beet.dataEnum([ + [ + 'V1', + new beet.FixableBeetArgsStruct( + [['authorizationData', beet.coption(authorizationDataBeet)]], + 'UseArgsRecord["V1"]', + ), + ], +]) as beet.FixableBeet; diff --git a/token-metadata/js/src/generated/types/UsesToggle.ts b/token-metadata/js/src/generated/types/UsesToggle.ts new file mode 100644 index 0000000000..34522d9fa9 --- /dev/null +++ b/token-metadata/js/src/generated/types/UsesToggle.ts @@ -0,0 +1,59 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import { Uses, usesBeet } from './Uses'; +/** + * This type is used to derive the {@link UsesToggle} type as well as the de/serializer. + * However don't refer to it in your code but use the {@link UsesToggle} type instead. + * + * @category userTypes + * @category enums + * @category generated + * @private + */ +export type UsesToggleRecord = { + None: void /* scalar variant */; + Clear: void /* scalar variant */; + Set: { fields: [Uses] }; +}; + +/** + * Union type respresenting the UsesToggle data enum defined in Rust. + * + * NOTE: that it includes a `__kind` property which allows to narrow types in + * switch/if statements. + * Additionally `isUsesToggle*` type guards are exposed below to narrow to a specific variant. + * + * @category userTypes + * @category enums + * @category generated + */ +export type UsesToggle = beet.DataEnumKeyAsKind; + +export const isUsesToggleNone = (x: UsesToggle): x is UsesToggle & { __kind: 'None' } => + x.__kind === 'None'; +export const isUsesToggleClear = (x: UsesToggle): x is UsesToggle & { __kind: 'Clear' } => + x.__kind === 'Clear'; +export const isUsesToggleSet = (x: UsesToggle): x is UsesToggle & { __kind: 'Set' } => + x.__kind === 'Set'; + +/** + * @category userTypes + * @category generated + */ +export const usesToggleBeet = beet.dataEnum([ + ['None', beet.unit], + ['Clear', beet.unit], + [ + 'Set', + new beet.BeetArgsStruct( + [['fields', beet.fixedSizeTuple([usesBeet])]], + 'UsesToggleRecord["Set"]', + ), + ], +]) as beet.FixableBeet; diff --git a/token-metadata/js/src/generated/types/VerifyArgs.ts b/token-metadata/js/src/generated/types/VerifyArgs.ts new file mode 100644 index 0000000000..447f8acf25 --- /dev/null +++ b/token-metadata/js/src/generated/types/VerifyArgs.ts @@ -0,0 +1,51 @@ +/** + * This code was GENERATED using the solita package. + * Please DO NOT EDIT THIS FILE, instead rerun solita to update it or write a wrapper to add functionality. + * + * See: https://github.com/metaplex-foundation/solita + */ + +import * as beet from '@metaplex-foundation/beet'; +import { AuthorizationData, authorizationDataBeet } from './AuthorizationData'; +/** + * This type is used to derive the {@link VerifyArgs} type as well as the de/serializer. + * However don't refer to it in your code but use the {@link VerifyArgs} type instead. + * + * @category userTypes + * @category enums + * @category generated + * @private + */ +export type VerifyArgsRecord = { + V1: { authorizationData: beet.COption }; +}; + +/** + * Union type respresenting the VerifyArgs data enum defined in Rust. + * + * NOTE: that it includes a `__kind` property which allows to narrow types in + * switch/if statements. + * Additionally `isVerifyArgs*` type guards are exposed below to narrow to a specific variant. + * + * @category userTypes + * @category enums + * @category generated + */ +export type VerifyArgs = beet.DataEnumKeyAsKind; + +export const isVerifyArgsV1 = (x: VerifyArgs): x is VerifyArgs & { __kind: 'V1' } => + x.__kind === 'V1'; + +/** + * @category userTypes + * @category generated + */ +export const verifyArgsBeet = beet.dataEnum([ + [ + 'V1', + new beet.FixableBeetArgsStruct( + [['authorizationData', beet.coption(authorizationDataBeet)]], + 'VerifyArgsRecord["V1"]', + ), + ], +]) as beet.FixableBeet; diff --git a/token-metadata/js/src/generated/types/index.ts b/token-metadata/js/src/generated/types/index.ts index 65af85fa2d..2f320bec74 100644 --- a/token-metadata/js/src/generated/types/index.ts +++ b/token-metadata/js/src/generated/types/index.ts @@ -1,6 +1,13 @@ export * from './ApproveUseAuthorityArgs'; +export * from './AssetData'; +export * from './AuthorityType'; +export * from './AuthorizationData'; +export * from './BurnArgs'; export * from './Collection'; export * from './CollectionDetails'; +export * from './CollectionDetailsToggle'; +export * from './CollectionToggle'; +export * from './CreateArgs'; export * from './CreateMasterEditionArgs'; export * from './CreateMetadataAccountArgs'; export * from './CreateMetadataAccountArgsV2'; @@ -8,18 +15,41 @@ export * from './CreateMetadataAccountArgsV3'; export * from './Creator'; export * from './Data'; export * from './DataV2'; +export * from './DelegateArgs'; export * from './EscrowAuthority'; export * from './Key'; +export * from './LeafInfo'; +export * from './LockArgs'; +export * from './MetadataDelegateRole'; +export * from './MigrateArgs'; +export * from './MigrationType'; +export * from './MintArgs'; export * from './MintNewEditionFromMasterEditionViaTokenArgs'; export * from './MintPrintingTokensViaTokenArgs'; +export * from './Payload'; +export * from './PayloadKey'; +export * from './PayloadType'; +export * from './PrintSupply'; +export * from './ProgrammableConfig'; export * from './Reservation'; export * from './ReservationV1'; +export * from './RevokeArgs'; +export * from './RuleSetToggle'; +export * from './SeedsVec'; export * from './SetCollectionSizeArgs'; export * from './SetReservationListArgs'; +export * from './TokenDelegateRole'; export * from './TokenStandard'; +export * from './TokenState'; +export * from './TransferArgs'; export * from './TransferOutOfEscrowArgs'; +export * from './UnlockArgs'; +export * from './UpdateArgs'; export * from './UpdateMetadataAccountArgs'; export * from './UpdateMetadataAccountArgsV2'; +export * from './UseArgs'; export * from './UseMethod'; export * from './Uses'; +export * from './UsesToggle'; export * from './UtilizeArgs'; +export * from './VerifyArgs'; diff --git a/token-metadata/js/test/create.test.ts b/token-metadata/js/test/create.test.ts new file mode 100644 index 0000000000..eb74ee7f20 --- /dev/null +++ b/token-metadata/js/test/create.test.ts @@ -0,0 +1,819 @@ +import spok from 'spok'; +import { AssetData, Metadata, TokenStandard } from '../src/generated'; +import test from 'tape'; +import { amman, InitTransactions, killStuckProcess } from './setup'; +import { PublicKey, SystemProgram, Transaction, TransactionInstruction } from '@solana/web3.js'; +import { + createAssociatedTokenAccountInstruction, + createInitializeMintInstruction, + createMintToInstruction, + getMint, + MintLayout, + TOKEN_PROGRAM_ID, +} from '@solana/spl-token'; +import { SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID, spokSameBigint, spokSamePubkey } from './utils'; +import { BN } from 'bn.js'; + +killStuckProcess(); + +test('Create: ProgrammableNonFungible', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const data: AssetData = { + name: 'ProgrammableNonFungible', + symbol: 'PNF', + uri: 'uri', + sellerFeeBasisPoints: 0, + creators: [ + { + address: payer.publicKey, + share: 100, + verified: false, + }, + ], + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.ProgrammableNonFungible, + collection: null, + uses: null, + collectionDetails: null, + ruleSet: null, + }; + + const { tx: transaction, metadata: address } = await API.create(t, payer, data, 0, 0, handler); + // executes the transaction + await transaction.assertSuccess(t); + + const metadata = await Metadata.fromAccountAddress(connection, address); + + spok(t, metadata, { + data: { + sellerFeeBasisPoints: 0, + }, + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.ProgrammableNonFungible, + }); + + t.equal(metadata.data.name.replace(/\0+/, ''), 'ProgrammableNonFungible'); + t.equal(metadata.data.symbol.replace(/\0+/, ''), 'PNF'); + t.equal(metadata.data.uri.replace(/\0+/, ''), 'uri'); +}); + +test('Create: ProgrammableNonFungible with existing mint account', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + // initialize a mint account + + const { tx: mintTx, mint } = await API.createMintAccount(payer, connection, handler); + await mintTx.assertSuccess(t); + + // create the metadata + + const data: AssetData = { + name: 'ProgrammableNonFungible', + symbol: 'PNF', + uri: 'uri', + sellerFeeBasisPoints: 0, + creators: [ + { + address: payer.publicKey, + share: 100, + verified: false, + }, + ], + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.ProgrammableNonFungible, + collection: null, + uses: null, + collectionDetails: null, + ruleSet: null, + }; + + const { tx: transaction, metadata: address } = await API.create( + t, + payer, + data, + 0, + 0, + handler, + mint, + ); + // executes the transaction + await transaction.assertSuccess(t); + + const metadata = await Metadata.fromAccountAddress(connection, address); + + spok(t, metadata, { + data: { + sellerFeeBasisPoints: 0, + }, + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.ProgrammableNonFungible, + }); + + t.equal(metadata.data.name.replace(/\0+/, ''), 'ProgrammableNonFungible'); + t.equal(metadata.data.symbol.replace(/\0+/, ''), 'PNF'); + t.equal(metadata.data.uri.replace(/\0+/, ''), 'uri'); +}); + +test('Create: fail to create ProgrammableNonFungible with minted mint account', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + // initialize a mint account and mints one token + + const [mint, mintKeypair] = await amman.genLabeledKeypair('Mint Account'); + const tokenAccount = PublicKey.findProgramAddressSync( + [payer.publicKey.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()], + SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID, + )[0]; + + const ixs: TransactionInstruction[] = []; + ixs.push( + SystemProgram.createAccount({ + fromPubkey: payer.publicKey, + newAccountPubkey: mint, + lamports: await connection.getMinimumBalanceForRentExemption(MintLayout.span), + space: MintLayout.span, + programId: TOKEN_PROGRAM_ID, + }), + ); + ixs.push(createInitializeMintInstruction(mint, 0, payer.publicKey, payer.publicKey)); + ixs.push( + createAssociatedTokenAccountInstruction(payer.publicKey, tokenAccount, payer.publicKey, mint), + ); + ixs.push(createMintToInstruction(mint, tokenAccount, payer.publicKey, 1, [])); + // candy machine mint instruction + const tx = new Transaction().add(...ixs); + + await handler + .sendAndConfirmTransaction(tx, [payer, mintKeypair], 'tx: Mint One Token') + .assertSuccess(t); + + // create the metadata + + const data: AssetData = { + name: 'ProgrammableNonFungible', + symbol: 'PNF', + uri: 'uri', + sellerFeeBasisPoints: 0, + creators: [ + { + address: payer.publicKey, + share: 100, + verified: false, + }, + ], + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.ProgrammableNonFungible, + collection: null, + uses: null, + collectionDetails: null, + ruleSet: null, + }; + + const { tx: transaction } = await API.create(t, payer, data, 0, 0, handler, mint); + // executes the transaction + await transaction.assertError(t, /Mint supply must be zero/); +}); + +test('Create: failt to create ProgrammableNonFungible with existing metadata account', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const data: AssetData = { + name: 'ProgrammableNonFungible', + symbol: 'PNF', + uri: 'uri', + sellerFeeBasisPoints: 0, + creators: [ + { + address: payer.publicKey, + share: 100, + verified: false, + }, + ], + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.ProgrammableNonFungible, + collection: null, + uses: null, + collectionDetails: null, + ruleSet: null, + }; + + const { + tx: transaction, + metadata: address, + mint, + } = await API.create(t, payer, data, 0, 0, handler); + // executes the transaction + await transaction.assertSuccess(t); + + const metadata = await Metadata.fromAccountAddress(connection, address); + + spok(t, metadata, { + data: { + sellerFeeBasisPoints: 0, + }, + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.ProgrammableNonFungible, + }); + + t.equal(metadata.data.name.replace(/\0+/, ''), 'ProgrammableNonFungible'); + t.equal(metadata.data.symbol.replace(/\0+/, ''), 'PNF'); + t.equal(metadata.data.uri.replace(/\0+/, ''), 'uri'); + + // tries to create another metadata account to the mint + + const { tx: duplicatedTx } = await API.create(t, payer, data, 0, 0, handler, mint); + // executes the transaction + await duplicatedTx.assertError(t, /Mint authority provided does not match the authority/); +}); + +test('Create: failt to create ProgrammableNonFungible with existing master edition account', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const data: AssetData = { + name: 'ProgrammableNonFungible', + symbol: 'PNF', + uri: 'uri', + sellerFeeBasisPoints: 0, + creators: [ + { + address: payer.publicKey, + share: 100, + verified: false, + }, + ], + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.ProgrammableNonFungible, + collection: null, + uses: null, + collectionDetails: null, + ruleSet: null, + }; + + const { + tx: transaction, + metadata: address, + masterEdition, + } = await API.create(t, payer, data, 0, 0, handler); + // executes the transaction + await transaction.assertSuccess(t); + + const metadata = await Metadata.fromAccountAddress(connection, address); + + spok(t, metadata, { + data: { + sellerFeeBasisPoints: 0, + }, + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.ProgrammableNonFungible, + }); + + t.equal(metadata.data.name.replace(/\0+/, ''), 'ProgrammableNonFungible'); + t.equal(metadata.data.symbol.replace(/\0+/, ''), 'PNF'); + t.equal(metadata.data.uri.replace(/\0+/, ''), 'uri'); + + // tries to create another metadata account to the mint + + const { tx: duplicatedTx } = await API.create( + t, + payer, + data, + 0, + 0, + handler, + null, + null, + masterEdition, + ); + // executes the transaction + await duplicatedTx.assertError(t, /Derived key invalid/); +}); + +test('Create: fail to create ProgrammableNonFungible without master edition', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer } = await API.payer(); + + const data: AssetData = { + name: 'ProgrammableNonFungible', + symbol: 'PNF', + uri: 'uri', + sellerFeeBasisPoints: 0, + creators: [ + { + address: payer.publicKey, + share: 100, + verified: false, + }, + ], + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.ProgrammableNonFungible, + collection: null, + uses: null, + collectionDetails: null, + ruleSet: null, + }; + + // tries to create a metadata account + + const { tx: duplicatedTx } = await API.create( + t, + payer, + data, + 0, + 0, + handler, + null /* mint */, + null /* metadata */, + null /* masterEdition */, + true /* skip master edition */, + ); + // executes the transaction + await duplicatedTx.assertError(t, /Missing master edition account/); +}); + +test('Create: fail to create NonFungible without master edition', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer } = await API.payer(); + + const data: AssetData = { + name: 'NonFungible', + symbol: 'NF', + uri: 'uri', + sellerFeeBasisPoints: 0, + creators: [ + { + address: payer.publicKey, + share: 100, + verified: false, + }, + ], + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.NonFungible, + collection: null, + uses: null, + collectionDetails: null, + ruleSet: null, + }; + + const { tx: transaction } = await API.create( + t, + payer, + data, + 0, + 0, + handler, + null /* mint */, + null /* metadata */, + null /* masterEdition */, + true /* skip master edition */, + ); + // executes the transaction + await transaction.assertError(t, /Missing master edition account/); +}); + +test('Create: create NonFungible with minted mint account', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + // initialize a mint account and mints one token + + const [mint, mintKeypair] = await amman.genLabeledKeypair('Mint Account'); + const tokenAccount = PublicKey.findProgramAddressSync( + [payer.publicKey.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()], + SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID, + )[0]; + + const ixs: TransactionInstruction[] = []; + ixs.push( + SystemProgram.createAccount({ + fromPubkey: payer.publicKey, + newAccountPubkey: mint, + lamports: await connection.getMinimumBalanceForRentExemption(MintLayout.span), + space: MintLayout.span, + programId: TOKEN_PROGRAM_ID, + }), + ); + ixs.push(createInitializeMintInstruction(mint, 0, payer.publicKey, payer.publicKey)); + ixs.push( + createAssociatedTokenAccountInstruction(payer.publicKey, tokenAccount, payer.publicKey, mint), + ); + ixs.push(createMintToInstruction(mint, tokenAccount, payer.publicKey, 1, [])); + // candy machine mint instruction + const tx = new Transaction().add(...ixs); + + await handler + .sendAndConfirmTransaction(tx, [payer, mintKeypair], 'tx: Mint One Token') + .assertSuccess(t); + + // create the metadata + + const data: AssetData = { + name: 'NonFungible', + symbol: 'NF', + uri: 'uri', + sellerFeeBasisPoints: 0, + creators: [ + { + address: payer.publicKey, + share: 100, + verified: false, + }, + ], + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.NonFungible, + collection: null, + uses: null, + collectionDetails: null, + ruleSet: null, + }; + + const { tx: transaction, metadata: address } = await API.create( + t, + payer, + data, + 0, + 0, + handler, + mint, + ); + // executes the transaction + await transaction.assertSuccess(t); + + const metadata = await Metadata.fromAccountAddress(connection, address); + + spok(t, metadata, { + data: { + sellerFeeBasisPoints: 0, + }, + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.NonFungible, + }); + + t.equal(metadata.data.name.replace(/\0+/, ''), 'NonFungible'); + t.equal(metadata.data.symbol.replace(/\0+/, ''), 'NF'); + t.equal(metadata.data.uri.replace(/\0+/, ''), 'uri'); +}); + +test('Create: fail to create NonFungible with more than 2 mints', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + // initialize a mint account and mints two tokens + + const [mint, mintKeypair] = await amman.genLabeledKeypair('Mint Account'); + const tokenAccount = PublicKey.findProgramAddressSync( + [payer.publicKey.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()], + SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID, + )[0]; + + const ixs: TransactionInstruction[] = []; + ixs.push( + SystemProgram.createAccount({ + fromPubkey: payer.publicKey, + newAccountPubkey: mint, + lamports: await connection.getMinimumBalanceForRentExemption(MintLayout.span), + space: MintLayout.span, + programId: TOKEN_PROGRAM_ID, + }), + ); + ixs.push(createInitializeMintInstruction(mint, 0, payer.publicKey, payer.publicKey)); + ixs.push( + createAssociatedTokenAccountInstruction(payer.publicKey, tokenAccount, payer.publicKey, mint), + ); + ixs.push(createMintToInstruction(mint, tokenAccount, payer.publicKey, 2, [])); + // candy machine mint instruction + const tx = new Transaction().add(...ixs); + + await handler + .sendAndConfirmTransaction(tx, [payer, mintKeypair], 'tx: Mint Two Tokens') + .assertSuccess(t); + + // create the metadata + + const data: AssetData = { + name: 'NonFungible', + symbol: 'NF', + uri: 'uri', + sellerFeeBasisPoints: 0, + creators: [ + { + address: payer.publicKey, + share: 100, + verified: false, + }, + ], + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.NonFungible, + collection: null, + uses: null, + collectionDetails: null, + ruleSet: null, + }; + + const { tx: transaction } = await API.create(t, payer, data, 0, 0, handler, mint); + // executes the transaction + await transaction.assertError(t, /Invalid mint account/); +}); + +test('Create: Fungible', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const data: AssetData = { + name: 'Fungible', + symbol: 'FUN', + uri: 'uri', + sellerFeeBasisPoints: 0, + creators: [ + { + address: payer.publicKey, + share: 100, + verified: false, + }, + ], + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.Fungible, + collection: null, + uses: null, + collectionDetails: null, + ruleSet: null, + }; + + const { + tx: transaction, + metadata: address, + mint, + } = await API.create(t, payer, data, 9, 0, handler); + // executes the transaction + await transaction.assertSuccess(t); + + const metadata = await Metadata.fromAccountAddress(connection, address); + + spok(t, metadata, { + data: { + sellerFeeBasisPoints: 0, + }, + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.Fungible, + }); + + t.equal(metadata.data.name.replace(/\0+/, ''), 'Fungible'); + t.equal(metadata.data.symbol.replace(/\0+/, ''), 'FUN'); + t.equal(metadata.data.uri.replace(/\0+/, ''), 'uri'); + + const mintAccount = await getMint(connection, mint); + + spok(t, mintAccount, { + decimals: 9, + supply: spokSameBigint(new BN(0)), + mintAuthority: spokSamePubkey(payer.publicKey), + }); +}); + +test('Create: FungibleAsset', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const data: AssetData = { + name: 'FungibleAsset', + symbol: 'FA', + uri: 'uri', + sellerFeeBasisPoints: 0, + creators: [ + { + address: payer.publicKey, + share: 100, + verified: false, + }, + ], + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.FungibleAsset, + collection: null, + uses: null, + collectionDetails: null, + ruleSet: null, + }; + + const { + tx: transaction, + metadata: address, + mint, + } = await API.create(t, payer, data, 2, 0, handler); + // executes the transaction + await transaction.assertSuccess(t); + + const metadata = await Metadata.fromAccountAddress(connection, address); + + spok(t, metadata, { + data: { + sellerFeeBasisPoints: 0, + }, + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.FungibleAsset, + }); + + t.equal(metadata.data.name.replace(/\0+/, ''), 'FungibleAsset'); + t.equal(metadata.data.symbol.replace(/\0+/, ''), 'FA'); + t.equal(metadata.data.uri.replace(/\0+/, ''), 'uri'); + + const mintAccount = await getMint(connection, mint); + + spok(t, mintAccount, { + decimals: 2, + supply: spokSameBigint(new BN(0)), + mintAuthority: spokSamePubkey(payer.publicKey), + }); +}); + +test('Create: create Fungible with minted mint account', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + // initialize a mint account and mints one token + + const [mint, mintKeypair] = await amman.genLabeledKeypair('Mint Account'); + const tokenAccount = PublicKey.findProgramAddressSync( + [payer.publicKey.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()], + SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID, + )[0]; + + const ixs: TransactionInstruction[] = []; + ixs.push( + SystemProgram.createAccount({ + fromPubkey: payer.publicKey, + newAccountPubkey: mint, + lamports: await connection.getMinimumBalanceForRentExemption(MintLayout.span), + space: MintLayout.span, + programId: TOKEN_PROGRAM_ID, + }), + ); + ixs.push(createInitializeMintInstruction(mint, 5, payer.publicKey, payer.publicKey)); + ixs.push( + createAssociatedTokenAccountInstruction(payer.publicKey, tokenAccount, payer.publicKey, mint), + ); + ixs.push(createMintToInstruction(mint, tokenAccount, payer.publicKey, 100, [])); + // candy machine mint instruction + const tx = new Transaction().add(...ixs); + + await handler + .sendAndConfirmTransaction(tx, [payer, mintKeypair], 'tx: Mint 100 Tokens') + .assertSuccess(t); + + // create the metadata + + const data: AssetData = { + name: 'Fungible', + symbol: 'FUN', + uri: 'uri', + sellerFeeBasisPoints: 0, + creators: [ + { + address: payer.publicKey, + share: 100, + verified: false, + }, + ], + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.Fungible, + collection: null, + uses: null, + collectionDetails: null, + ruleSet: null, + }; + + const { tx: transaction, metadata: address } = await API.create( + t, + payer, + data, + 0, + 0, + handler, + mint, + ); + // executes the transaction + await transaction.assertSuccess(t); + + const metadata = await Metadata.fromAccountAddress(connection, address); + + spok(t, metadata, { + data: { + sellerFeeBasisPoints: 0, + }, + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.Fungible, + }); + + t.equal(metadata.data.name.replace(/\0+/, ''), 'Fungible'); + t.equal(metadata.data.symbol.replace(/\0+/, ''), 'FUN'); + t.equal(metadata.data.uri.replace(/\0+/, ''), 'uri'); +}); + +test('Create: create FungibleAsset with minted mint account', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + // initialize a mint account and mints one token + + const [mint, mintKeypair] = await amman.genLabeledKeypair('Mint Account'); + const tokenAccount = PublicKey.findProgramAddressSync( + [payer.publicKey.toBuffer(), TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()], + SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID, + )[0]; + + const ixs: TransactionInstruction[] = []; + ixs.push( + SystemProgram.createAccount({ + fromPubkey: payer.publicKey, + newAccountPubkey: mint, + lamports: await connection.getMinimumBalanceForRentExemption(MintLayout.span), + space: MintLayout.span, + programId: TOKEN_PROGRAM_ID, + }), + ); + ixs.push(createInitializeMintInstruction(mint, 5, payer.publicKey, payer.publicKey)); + ixs.push( + createAssociatedTokenAccountInstruction(payer.publicKey, tokenAccount, payer.publicKey, mint), + ); + ixs.push(createMintToInstruction(mint, tokenAccount, payer.publicKey, 100, [])); + // candy machine mint instruction + const tx = new Transaction().add(...ixs); + + await handler + .sendAndConfirmTransaction(tx, [payer, mintKeypair], 'tx: Mint 100 Tokens') + .assertSuccess(t); + + // create the metadata + + const data: AssetData = { + name: 'FungibleAsset', + symbol: 'FA', + uri: 'uri', + sellerFeeBasisPoints: 0, + creators: [ + { + address: payer.publicKey, + share: 100, + verified: false, + }, + ], + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.FungibleAsset, + collection: null, + uses: null, + collectionDetails: null, + ruleSet: null, + }; + + const { tx: transaction, metadata: address } = await API.create( + t, + payer, + data, + 0, + 0, + handler, + mint, + ); + // executes the transaction + await transaction.assertSuccess(t); + + const metadata = await Metadata.fromAccountAddress(connection, address); + + spok(t, metadata, { + data: { + sellerFeeBasisPoints: 0, + }, + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.FungibleAsset, + }); + + t.equal(metadata.data.name.replace(/\0+/, ''), 'FungibleAsset'); + t.equal(metadata.data.symbol.replace(/\0+/, ''), 'FA'); + t.equal(metadata.data.uri.replace(/\0+/, ''), 'uri'); +}); diff --git a/token-metadata/js/test/delegate.test.ts b/token-metadata/js/test/delegate.test.ts new file mode 100644 index 0000000000..5820317824 --- /dev/null +++ b/token-metadata/js/test/delegate.test.ts @@ -0,0 +1,521 @@ +import { + DelegateArgs, + PROGRAM_ID, + TokenStandard, + TokenRecord, + TokenDelegateRole, + MetadataDelegateRecord, + TokenState, +} from '../src/generated'; +import test from 'tape'; +import { amman, InitTransactions, killStuckProcess } from './setup'; +import { createAndMintDefaultAsset } from './utils/digital-asset-manager'; +import spok from 'spok'; +import { spokSameBigint, spokSamePubkey } from './utils'; +import { BN } from 'bn.js'; +import { getAccount } from '@solana/spl-token'; +import { PublicKey } from '@solana/web3.js'; +import { findTokenRecordPda } from './utils/programmable'; + +killStuckProcess(); + +test('Delegate: create collection delegate', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ); + + // creates a delegate + + const [delegate] = await API.getKeypair('Delegate'); + // delegate PDA + const [delegateRecord] = PublicKey.findProgramAddressSync( + [ + Buffer.from('metadata'), + PROGRAM_ID.toBuffer(), + manager.mint.toBuffer(), + Buffer.from('collection_delegate'), + payer.publicKey.toBuffer(), + delegate.toBuffer(), + ], + PROGRAM_ID, + ); + amman.addr.addLabel('Metadata Delegate Record', delegateRecord); + + const args: DelegateArgs = { + __kind: 'CollectionV1', + authorizationData: null, + }; + + const { tx: delegateTx } = await API.delegate( + delegate, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + args, + handler, + delegateRecord, + manager.masterEdition, + ); + + await delegateTx.assertSuccess(t); + + const pda = await MetadataDelegateRecord.fromAccountAddress(connection, delegateRecord); + + spok(t, pda, { + delegate: spokSamePubkey(delegate), + mint: spokSamePubkey(manager.mint), + }); +}); + +test('Delegate: create sale delegate', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ); + + // creates a delegate + + const [delegate] = await API.getKeypair('Delegate'); + // token record PDA + const tokenRecord = findTokenRecordPda(manager.mint, manager.token); + amman.addr.addLabel('Token Record', tokenRecord); + + const args: DelegateArgs = { + __kind: 'SaleV1', + amount: 1, + authorizationData: null, + }; + + const { tx: delegateTx } = await API.delegate( + delegate, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + args, + handler, + null, + manager.masterEdition, + manager.token, + tokenRecord, + ); + + await delegateTx.assertSuccess(t); + + // asserts + + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + delegatedAmount: spokSameBigint(new BN(1)), + delegate: spokSamePubkey(delegate), + }); + + const pda = await TokenRecord.fromAccountAddress(connection, tokenRecord); + + spok(t, pda, { + delegate: spokSamePubkey(delegate), + delegateRole: TokenDelegateRole.Sale, + state: TokenState.Listed, + }); +}); + +test('Delegate: owner as sale delegate', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ); + + // creates a delegate + + // token record PDA + const tokenRecord = findTokenRecordPda(manager.mint, manager.token); + amman.addr.addLabel('Token Record', tokenRecord); + + const args: DelegateArgs = { + __kind: 'SaleV1', + amount: 1, + authorizationData: null, + }; + + const { tx: delegateTx } = await API.delegate( + payer.publicKey, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + args, + handler, + null, + manager.masterEdition, + manager.token, + tokenRecord, + ); + + await delegateTx.assertSuccess(t); + + // asserts + + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + delegatedAmount: spokSameBigint(new BN(1)), + delegate: spokSamePubkey(payer.publicKey), + }); + + const pda = await TokenRecord.fromAccountAddress(connection, tokenRecord); + + spok(t, pda, { + delegate: spokSamePubkey(payer.publicKey), + delegateRole: TokenDelegateRole.Sale, + state: TokenState.Listed, + }); +}); + +test('Delegate: create transfer delegate', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ); + + // creates a delegate + + const [delegate] = await API.getKeypair('Delegate'); + // token record PDA + const tokenRecord = findTokenRecordPda(manager.mint, manager.token); + amman.addr.addLabel('Token Record', tokenRecord); + + const args: DelegateArgs = { + __kind: 'TransferV1', + amount: 1, + authorizationData: null, + }; + + const { tx: delegateTx } = await API.delegate( + delegate, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + args, + handler, + null, + manager.masterEdition, + manager.token, + tokenRecord, + ); + + await delegateTx.assertSuccess(t); + + // asserts + + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + delegatedAmount: spokSameBigint(new BN(1)), + delegate: spokSamePubkey(delegate), + }); + + const pda = await TokenRecord.fromAccountAddress(connection, tokenRecord); + + spok(t, pda, { + delegate: spokSamePubkey(delegate), + delegateRole: TokenDelegateRole.Transfer, + }); +}); + +test('Delegate: fail to create sale delegate on NFT', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.NonFungible, + ); + + // creates a delegate + + const [delegate] = await API.getKeypair('Delegate'); + // token record PDA + const tokenRecord = findTokenRecordPda(manager.mint, manager.token); + amman.addr.addLabel('Token Record', tokenRecord); + + const args: DelegateArgs = { + __kind: 'SaleV1', + amount: 1, + authorizationData: null, + }; + + const { tx: delegateTx } = await API.delegate( + delegate, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + args, + handler, + null, + manager.masterEdition, + manager.token, + tokenRecord, + ); + + await delegateTx.assertError(t, /Invalid delegate role/); +}); + +test('Delegate: fail to replace pNFT transfer delegate', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ); + + // creates a delegate + + const [delegate] = await API.getKeypair('Delegate'); + // token record PDA + const tokenRecord = findTokenRecordPda(manager.mint, manager.token); + amman.addr.addLabel('Token Record', tokenRecord); + + const args: DelegateArgs = { + __kind: 'TransferV1', + amount: 1, + authorizationData: null, + }; + + const { tx: delegateTx } = await API.delegate( + delegate, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + args, + handler, + null, + manager.masterEdition, + manager.token, + tokenRecord, + ); + + await delegateTx.assertSuccess(t); + + // asserts + + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + delegatedAmount: spokSameBigint(new BN(1)), + delegate: spokSamePubkey(delegate), + }); + + const pda = await TokenRecord.fromAccountAddress(connection, tokenRecord); + + spok(t, pda, { + delegate: spokSamePubkey(delegate), + delegateRole: TokenDelegateRole.Transfer, + }); + + // creates a new delegate + + const [newDelegate] = await API.getKeypair('Delegate'); + + const { tx: delegateTx2 } = await API.delegate( + newDelegate, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + args, + handler, + null, + manager.masterEdition, + manager.token, + tokenRecord, + ); + + await delegateTx2.assertError(t, /Delegate already exists/); +}); + +test('Delegate: create utility delegate', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ); + + // creates a delegate + + const [delegate] = await API.getKeypair('Delegate'); + // token record PDA + const tokenRecord = findTokenRecordPda(manager.mint, manager.token); + amman.addr.addLabel('Token Record', tokenRecord); + + const args: DelegateArgs = { + __kind: 'UtilityV1', + amount: 1, + authorizationData: null, + }; + + const { tx: delegateTx } = await API.delegate( + delegate, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + args, + handler, + null, + manager.masterEdition, + manager.token, + tokenRecord, + ); + + await delegateTx.assertSuccess(t); + + // asserts + + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + delegatedAmount: spokSameBigint(new BN(1)), + delegate: spokSamePubkey(delegate), + }); + + const pda = await TokenRecord.fromAccountAddress(connection, tokenRecord); + + spok(t, pda, { + delegate: spokSamePubkey(delegate), + delegateRole: TokenDelegateRole.Utility, + }); +}); + +test('Delegate: try replace sale delegate', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ); + + // creates a delegate + + const [delegate] = await API.getKeypair('Delegate'); + // token record PDA + const tokenRecord = findTokenRecordPda(manager.mint, manager.token); + amman.addr.addLabel('Token Record', tokenRecord); + + const args: DelegateArgs = { + __kind: 'SaleV1', + amount: 1, + authorizationData: null, + }; + + const { tx: delegateTx } = await API.delegate( + delegate, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + args, + handler, + null, + manager.masterEdition, + manager.token, + tokenRecord, + ); + + await delegateTx.assertSuccess(t); + + // asserts + + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + delegatedAmount: spokSameBigint(new BN(1)), + delegate: spokSamePubkey(delegate), + }); + + const pda = await TokenRecord.fromAccountAddress(connection, tokenRecord); + + spok(t, pda, { + delegate: spokSamePubkey(delegate), + delegateRole: TokenDelegateRole.Sale, + }); + + // creates a transfer delegate + + const [newDelegate] = await API.getKeypair('Delegate'); + + const args2: DelegateArgs = { + __kind: 'TransferV1', + amount: 1, + authorizationData: null, + }; + + const { tx: delegateTx2 } = await API.delegate( + newDelegate, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + args2, + handler, + null, + manager.masterEdition, + manager.token, + tokenRecord, + ); + + await delegateTx2.assertError(t, /Delegate already exists/); +}); diff --git a/token-metadata/js/test/token-metadata-deserializer.ts b/token-metadata/js/test/deserializer.test.ts similarity index 100% rename from token-metadata/js/test/token-metadata-deserializer.ts rename to token-metadata/js/test/deserializer.test.ts diff --git a/token-metadata/js/test/lock.test.ts b/token-metadata/js/test/lock.test.ts new file mode 100644 index 0000000000..60c1ae289f --- /dev/null +++ b/token-metadata/js/test/lock.test.ts @@ -0,0 +1,617 @@ +import { getAccount } from '@solana/spl-token'; +import { BN } from 'bn.js'; +import spok from 'spok'; +import { DelegateArgs, TokenRecord, TokenStandard, TokenState } from '../src/generated'; +import test from 'tape'; +import { amman, InitTransactions, killStuckProcess } from './setup'; +import { spokSameBigint } from './utils'; +import { createAndMintDefaultAsset } from './utils/digital-asset-manager'; +import { findTokenRecordPda } from './utils/programmable'; + +killStuckProcess(); + +test('Lock: owner lock NonFungible asset', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.NonFungible, + ); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + amount: spokSameBigint(new BN(1)), + isFrozen: false, + owner: payer.publicKey, + }); + } + + // lock asset + + const { tx: lockTx } = await API.lock( + payer, + manager.mint, + manager.metadata, + manager.token, + payer, + handler, + null, + null, + manager.masterEdition, + ); + await lockTx.assertError(t, /Invalid authority type/); +}); + +test('Lock: owner lock ProgrammableNonFungible asset', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + amount: spokSameBigint(new BN(1)), + isFrozen: true, + owner: payer.publicKey, + }); + } + + // token record PDA + const tokenRecord = findTokenRecordPda(manager.mint, manager.token); + amman.addr.addLabel('Token Record', tokenRecord); + + const pda = await TokenRecord.fromAccountAddress(connection, tokenRecord); + + spok(t, pda, { + state: TokenState.Unlocked /* asset should be unlocked */, + }); + + // lock asset + + const { tx: lockTx } = await API.lock( + payer, + manager.mint, + manager.metadata, + manager.token, + payer, + handler, + tokenRecord, + ); + await lockTx.assertError(t, /Invalid authority type/); +}); + +test('Lock: delegate lock ProgrammableNonFungible asset', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + amount: spokSameBigint(new BN(1)), + isFrozen: true, + owner: payer.publicKey, + }); + } + + // token record PDA + const tokenRecord = findTokenRecordPda(manager.mint, manager.token); + amman.addr.addLabel('Token Record', tokenRecord); + + let pda = await TokenRecord.fromAccountAddress(connection, tokenRecord); + + spok(t, pda, { + state: TokenState.Unlocked /* asset should be unlocked */, + }); + + // creates a delegate + + const [, delegate] = await API.getKeypair('Delegate'); + + const args: DelegateArgs = { + __kind: 'UtilityV1', + amount: 1, + authorizationData: null, + }; + + const { tx: delegateTx } = await API.delegate( + delegate.publicKey, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + args, + handler, + null, + manager.masterEdition, + manager.token, + tokenRecord, + ); + + await delegateTx.assertSuccess(t); + + // lock asset with delegate + + const { tx: lockTx } = await API.lock( + delegate, + manager.mint, + manager.metadata, + manager.token, + payer, + handler, + tokenRecord, + null, + manager.masterEdition, + ); + await lockTx.assertSuccess(t); + + pda = await TokenRecord.fromAccountAddress(connection, tokenRecord); + + spok(t, pda, { + state: TokenState.Locked /* asset should be locked */, + }); +}); + +test('Lock: delegate lock NonFungible asset', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.NonFungible, + ); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + amount: spokSameBigint(new BN(1)), + isFrozen: false, + owner: payer.publicKey, + }); + } + + // creates a delegate + + const [, delegate] = await API.getKeypair('Delegate'); + + const args: DelegateArgs = { + __kind: 'StandardV1', + amount: 1, + }; + + const { tx: delegateTx } = await API.delegate( + delegate.publicKey, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + args, + handler, + null, + manager.masterEdition, + manager.token, + ); + + await delegateTx.assertSuccess(t); + + // lock asset with delegate + + const { tx: lockTx } = await API.lock( + delegate, + manager.mint, + manager.metadata, + manager.token, + payer, + handler, + null, + null, + manager.masterEdition, + ); + await lockTx.assertSuccess(t); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + isFrozen: true, + }); + } +}); + +test('Lock: lock Fungible asset', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.Fungible, + null, + 100, + ); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + amount: spokSameBigint(new BN(100)), + isFrozen: false, + owner: payer.publicKey, + }); + } + + // creates a delegate + + const [, delegate] = await API.getKeypair('Delegate'); + + const args: DelegateArgs = { + __kind: 'StandardV1', + amount: 100, + }; + + const { tx: delegateTx } = await API.delegate( + delegate.publicKey, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + args, + handler, + null, + null, + manager.token, + ); + + await delegateTx.assertSuccess(t); + + // lock asset + + const { tx: lockTx } = await API.lock( + delegate, + manager.mint, + manager.metadata, + manager.token, + payer, + handler, + null, + payer.publicKey, + ); + await lockTx.assertSuccess(t); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + isFrozen: true, + }); + } +}); + +test('Lock: lock ProgrammableNonFungible asset with wrong authority', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + amount: spokSameBigint(new BN(1)), + isFrozen: true, + owner: payer.publicKey, + }); + } + + // token record PDA + const tokenRecord = findTokenRecordPda(manager.mint, manager.token); + amman.addr.addLabel('Token Record', tokenRecord); + + const pda = await TokenRecord.fromAccountAddress(connection, tokenRecord); + + spok(t, pda, { + state: TokenState.Unlocked /* asset should be unlocked */, + }); + + // lock asset + + const [, wrongApprover] = await amman.genLabeledKeypair('Wrong Approver'); + + const { tx: lockTx } = await API.lock( + wrongApprover, + manager.mint, + manager.metadata, + manager.token, + payer, + handler, + ); + await lockTx.assertError(t, /Invalid authority type/); +}); + +test('Lock: wrong delegate lock NonFungible asset', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.NonFungible, + ); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + amount: spokSameBigint(new BN(1)), + isFrozen: false, + owner: payer.publicKey, + }); + } + + // creates a delegate + + const [, delegate] = await API.getKeypair('Delegate'); + + const args: DelegateArgs = { + __kind: 'StandardV1', + amount: 1, + }; + + const { tx: delegateTx } = await API.delegate( + delegate.publicKey, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + args, + handler, + null, + manager.masterEdition, + manager.token, + ); + + await delegateTx.assertSuccess(t); + + // lock asset with wrong delegate + + const [, wrongDelegate] = await API.getKeypair('Wrong Delegate'); + + const { tx: lockTx } = await API.lock( + wrongDelegate, + manager.mint, + manager.metadata, + manager.token, + payer, + handler, + null, + manager.masterEdition, + ); + await lockTx.assertError(t, /not been delegated to this user/); +}); + +test('Lock: wrong delegate lock ProgrammableNonFungible asset', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ); + + const tokenRecord = findTokenRecordPda(manager.mint, manager.token); + amman.addr.addLabel('Token Record', tokenRecord); + + const pda = await TokenRecord.fromAccountAddress(connection, tokenRecord); + + spok(t, pda, { + state: TokenState.Unlocked /* asset should be unlocked */, + }); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + amount: spokSameBigint(new BN(1)), + isFrozen: true, + owner: payer.publicKey, + }); + } + + // creates a delegate + + const [, delegate] = await API.getKeypair('Delegate'); + + const args: DelegateArgs = { + __kind: 'TransferV1', + amount: 1, + authorizationData: null, + }; + + const { tx: delegateTx } = await API.delegate( + delegate.publicKey, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + args, + handler, + null, + manager.masterEdition, + manager.token, + tokenRecord, + ); + + await delegateTx.assertSuccess(t); + + // lock asset with delegate + + const { tx: utilityTx } = await API.lock( + delegate, + manager.mint, + manager.metadata, + manager.token, + payer, + handler, + tokenRecord, + null, + manager.masterEdition, + ); + await utilityTx.assertError(t, /Invalid authority type/); +}); + +test('Lock: already locked ProgrammableNonFungible asset', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ); + + const tokenRecord = findTokenRecordPda(manager.mint, manager.token); + amman.addr.addLabel('Token Record', tokenRecord); + + let pda = await TokenRecord.fromAccountAddress(connection, tokenRecord); + + spok(t, pda, { + state: TokenState.Unlocked /* asset should be unlocked */, + }); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + amount: spokSameBigint(new BN(1)), + isFrozen: true, + owner: payer.publicKey, + }); + } + + // creates a delegate + + const [, delegate] = await API.getKeypair('Delegate'); + + const args: DelegateArgs = { + __kind: 'UtilityV1', + amount: 1, + authorizationData: null, + }; + + const { tx: delegateTx } = await API.delegate( + delegate.publicKey, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + args, + handler, + null, + manager.masterEdition, + manager.token, + tokenRecord, + ); + + await delegateTx.assertSuccess(t); + + // lock asset with delegate + + const { tx: utilityTx } = await API.lock( + delegate, + manager.mint, + manager.metadata, + manager.token, + payer, + handler, + tokenRecord, + null, + manager.masterEdition, + ); + await utilityTx.assertSuccess(t); + + pda = await TokenRecord.fromAccountAddress(connection, tokenRecord); + + spok(t, pda, { + state: TokenState.Locked /* asset should be unlocked */, + }); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + amount: spokSameBigint(new BN(1)), + isFrozen: true, + owner: payer.publicKey, + }); + } + + // tries to create a new delegate + + const [, newDelegate] = await API.getKeypair('Delegate'); + + const { tx: newDelegateTx } = await API.delegate( + newDelegate.publicKey, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + args, + handler, + null, + manager.masterEdition, + manager.token, + tokenRecord, + ); + + await newDelegateTx.assertError(t, /Token is locked/); +}); diff --git a/token-metadata/js/test/mint.test.ts b/token-metadata/js/test/mint.test.ts new file mode 100644 index 0000000000..307fd2faab --- /dev/null +++ b/token-metadata/js/test/mint.test.ts @@ -0,0 +1,277 @@ +import { getAccount } from '@solana/spl-token'; +import { PublicKey } from '@solana/web3.js'; +import { BN } from 'bn.js'; +import spok from 'spok'; +import { AssetData, PROGRAM_ID, TokenStandard } from '../src/generated'; +import test from 'tape'; +import { amman, InitTransactions, killStuckProcess } from './setup'; +import { spokSameBigint } from './utils'; +import { DigitalAssetManager } from './utils/digital-asset-manager'; + +killStuckProcess(); + +test('Mint: ProgrammableNonFungible', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const data: AssetData = { + name: 'ProgrammableNonFungible', + symbol: 'PNF', + uri: 'uri', + sellerFeeBasisPoints: 0, + creators: [ + { + address: payer.publicKey, + share: 100, + verified: false, + }, + ], + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.ProgrammableNonFungible, + collection: null, + uses: null, + collectionDetails: null, + ruleSet: null, + }; + + const { tx: createTx, metadata, mint } = await API.create(t, payer, data, 0, 0, handler); + await createTx.assertSuccess(t); + + // mint 1 asset + + const amount = 1; + + const [masterEdition] = PublicKey.findProgramAddressSync( + [Buffer.from('metadata'), PROGRAM_ID.toBuffer(), mint.toBuffer(), Buffer.from('edition')], + PROGRAM_ID, + ); + amman.addr.addLabel('Master Edition Account', masterEdition); + const daManager = new DigitalAssetManager(mint, metadata, masterEdition); + + const { tx: mintTx, token } = await API.mint( + t, + connection, + payer, + mint, + metadata, + masterEdition, + daManager.emptyAuthorizationData(), + amount, + handler, + ); + await mintTx.assertSuccess(t); + + const tokenAccount = await getAccount(connection, token); + + spok(t, tokenAccount, { + amount: spokSameBigint(new BN(1)), + isFrozen: true, + owner: payer.publicKey, + }); +}); + +test('Mint: ProgrammableNonFungible with existing token account', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const data: AssetData = { + name: 'ProgrammableNonFungible', + symbol: 'PNF', + uri: 'uri', + sellerFeeBasisPoints: 0, + creators: [ + { + address: payer.publicKey, + share: 100, + verified: false, + }, + ], + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.ProgrammableNonFungible, + collection: null, + uses: null, + collectionDetails: null, + ruleSet: null, + }; + + const { tx: createTx, metadata, mint } = await API.create(t, payer, data, 0, 0, handler); + await createTx.assertSuccess(t); + + // initialize a token account + + const { tx: tokenTx, token } = await API.createTokenAccount( + mint, + payer, + connection, + handler, + payer.publicKey, + ); + await tokenTx.assertSuccess(t); + + // mint 1 asset + + const amount = 1; + + const [masterEdition] = PublicKey.findProgramAddressSync( + [Buffer.from('metadata'), PROGRAM_ID.toBuffer(), mint.toBuffer(), Buffer.from('edition')], + PROGRAM_ID, + ); + amman.addr.addLabel('Master Edition Account', masterEdition); + const daManager = new DigitalAssetManager(mint, metadata, masterEdition); + + const { tx: mintTx } = await API.mint( + t, + connection, + payer, + mint, + metadata, + masterEdition, + daManager.emptyAuthorizationData(), + amount, + handler, + token, + ); + await mintTx.assertSuccess(t); + + const tokenAccount = await getAccount(connection, token); + + spok(t, tokenAccount, { + amount: spokSameBigint(new BN(1)), + isFrozen: true, + owner: payer.publicKey, + }); +}); + +test('Mint: fail to mint zero (0) tokens from ProgrammableNonFungible', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const data: AssetData = { + name: 'ProgrammableNonFungible', + symbol: 'PNF', + uri: 'uri', + sellerFeeBasisPoints: 0, + creators: [ + { + address: payer.publicKey, + share: 100, + verified: false, + }, + ], + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.ProgrammableNonFungible, + collection: null, + uses: null, + collectionDetails: null, + ruleSet: null, + }; + + const { tx: createTx, metadata, mint } = await API.create(t, payer, data, 0, 0, handler); + await createTx.assertSuccess(t); + + // mint 0 asset + + const [masterEdition] = PublicKey.findProgramAddressSync( + [Buffer.from('metadata'), PROGRAM_ID.toBuffer(), mint.toBuffer(), Buffer.from('edition')], + PROGRAM_ID, + ); + amman.addr.addLabel('Master Edition Account', masterEdition); + const daManager = new DigitalAssetManager(mint, metadata, masterEdition); + + const { tx: mintTx } = await API.mint( + t, + connection, + payer, + mint, + metadata, + masterEdition, + daManager.emptyAuthorizationData(), + 0, + handler, + ); + await mintTx.assertError(t, /Amount must be greater than zero/); +}); + +test('Mint: fail to mint multiple from ProgrammableNonFungible', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const data: AssetData = { + name: 'ProgrammableNonFungible', + symbol: 'PNF', + uri: 'uri', + sellerFeeBasisPoints: 0, + creators: [ + { + address: payer.publicKey, + share: 100, + verified: false, + }, + ], + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.ProgrammableNonFungible, + collection: null, + uses: null, + collectionDetails: null, + ruleSet: null, + }; + + const { tx: createTx, metadata, mint } = await API.create(t, payer, data, 0, 0, handler); + await createTx.assertSuccess(t); + + // tries to mint 2 asset + + const [masterEdition] = PublicKey.findProgramAddressSync( + [Buffer.from('metadata'), PROGRAM_ID.toBuffer(), mint.toBuffer(), Buffer.from('edition')], + PROGRAM_ID, + ); + amman.addr.addLabel('Master Edition Account', masterEdition); + const manager = new DigitalAssetManager(mint, metadata, masterEdition); + + const { tx: multipleMintTx } = await API.mint( + t, + connection, + payer, + mint, + metadata, + masterEdition, + manager.emptyAuthorizationData(), + 2, + handler, + ); + await multipleMintTx.assertError(t, /Editions must have exactly one token/); + + // tries to mint 1 asset + + const { tx: mintTx } = await API.mint( + t, + connection, + payer, + mint, + metadata, + masterEdition, + manager.emptyAuthorizationData(), + 1, + handler, + ); + await mintTx.assertSuccess(t); + + // tries to mint another one asset + + const { tx: mintTx2 } = await API.mint( + t, + connection, + payer, + mint, + metadata, + masterEdition, + manager.emptyAuthorizationData(), + 1, + handler, + ); + await mintTx2.assertError(t, /Editions must have exactly one token/); +}); diff --git a/token-metadata/js/test/revoke.test.ts b/token-metadata/js/test/revoke.test.ts new file mode 100644 index 0000000000..1072b5c0bb --- /dev/null +++ b/token-metadata/js/test/revoke.test.ts @@ -0,0 +1,289 @@ +import { + DelegateArgs, + MetadataDelegateRecord, + PROGRAM_ID, + RevokeArgs, + TokenDelegateRole, + TokenRecord, + TokenStandard, +} from '../src/generated'; +import test from 'tape'; +import { amman, InitTransactions, killStuckProcess } from './setup'; +import { createAndMintDefaultAsset } from './utils/digital-asset-manager'; +import spok from 'spok'; +import { spokSameBigint, spokSamePubkey } from './utils'; +import { BN } from 'bn.js'; +import { getAccount } from '@solana/spl-token'; +import { PublicKey } from '@solana/web3.js'; +import { findTokenRecordPda } from './utils/programmable'; + +killStuckProcess(); + +test('Revoke: revoke transfer delegate', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ); + + // creates a delegate + + const [delegate] = await API.getKeypair('Delegate'); + // token record PDA + const tokenRecord = findTokenRecordPda(manager.mint, manager.token); + amman.addr.addLabel('Token Record', tokenRecord); + + const delegateArgs: DelegateArgs = { + __kind: 'TransferV1', + amount: 1, + authorizationData: null, + }; + + const { tx: delegateTx } = await API.delegate( + delegate, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + delegateArgs, + handler, + null, + manager.masterEdition, + manager.token, + tokenRecord, + ); + + await delegateTx.assertSuccess(t); + + // asserts + + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + delegatedAmount: spokSameBigint(new BN(1)), + delegate: spokSamePubkey(delegate), + }); + + let pda = await TokenRecord.fromAccountAddress(connection, tokenRecord); + + spok(t, pda, { + delegate: spokSamePubkey(delegate), + delegateRole: TokenDelegateRole.Transfer, + }); + + // revoke + + const { tx: revoketeTx } = await API.revoke( + delegate, + manager.mint, + manager.metadata, + payer, + payer, + RevokeArgs.TransferV1, + handler, + null, + manager.masterEdition, + manager.token, + tokenRecord, + ); + + await revoketeTx.assertSuccess(t); + + pda = await TokenRecord.fromAccountAddress(connection, tokenRecord); + + spok(t, pda, { + delegate: null, + delegateRole: null, + }); +}); + +test('Revoke: revoke collection delegate', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ); + + // creates a delegate + + const [, delegate] = await API.getKeypair('Delegate'); + // delegate PDA + const [delegateRecord] = PublicKey.findProgramAddressSync( + [ + Buffer.from('metadata'), + PROGRAM_ID.toBuffer(), + manager.mint.toBuffer(), + Buffer.from('collection_delegate'), + payer.publicKey.toBuffer(), + delegate.publicKey.toBuffer(), + ], + PROGRAM_ID, + ); + amman.addr.addLabel('Delegate Record', delegateRecord); + + const delegateArgs: DelegateArgs = { + __kind: 'CollectionV1', + authorizationData: null, + }; + + const { tx: delegateTx } = await API.delegate( + delegate.publicKey, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + delegateArgs, + handler, + delegateRecord, + manager.masterEdition, + manager.token, + ); + + await delegateTx.assertSuccess(t); + + // asserts + + const account = await MetadataDelegateRecord.fromAccountAddress(connection, delegateRecord); + + spok(t, account, { + delegate: spokSamePubkey(delegate.publicKey), + mint: spokSamePubkey(manager.mint), + }); + + // revoke + + const { tx: revoketeTx } = await API.revoke( + delegate.publicKey, + manager.mint, + manager.metadata, + payer, + payer, + RevokeArgs.CollectionV1, + handler, + delegateRecord, + manager.masterEdition, + manager.token, + ); + + await revoketeTx.assertSuccess(t); + + try { + await MetadataDelegateRecord.fromAccountAddress(connection, delegateRecord); + t.fail(`Metadata delegate account ${delegateRecord} was found`); + } catch (err) { + // we are expecting an error, since the account must be deleted + } +}); + +test('Revoke: self-revoke collection delegate', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ); + + // creates a delegate + + const [, delegate] = await API.getKeypair('Delegate'); + // delegate PDA + const [delegateRecord] = PublicKey.findProgramAddressSync( + [ + Buffer.from('metadata'), + PROGRAM_ID.toBuffer(), + manager.mint.toBuffer(), + Buffer.from('collection_delegate'), + payer.publicKey.toBuffer(), + delegate.publicKey.toBuffer(), + ], + PROGRAM_ID, + ); + amman.addr.addLabel('Delegate Record', delegateRecord); + + const delegateArgs: DelegateArgs = { + __kind: 'CollectionV1', + authorizationData: null, + }; + + const { tx: delegateTx } = await API.delegate( + delegate.publicKey, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + delegateArgs, + handler, + delegateRecord, + manager.masterEdition, + manager.token, + ); + + await delegateTx.assertSuccess(t); + + // asserts + + const account = await MetadataDelegateRecord.fromAccountAddress(connection, delegateRecord); + + spok(t, account, { + delegate: spokSamePubkey(delegate.publicKey), + mint: spokSamePubkey(manager.mint), + }); + + // revoke + + const { tx: revoketeTx } = await API.revoke( + delegate.publicKey, + manager.mint, + manager.metadata, + delegate, + payer, + RevokeArgs.CollectionV1, + handler, + delegateRecord, + manager.masterEdition, + manager.token, + ); + + await revoketeTx.assertSuccess(t); + + try { + await MetadataDelegateRecord.fromAccountAddress(connection, delegateRecord); + t.fail(`Delegate account ${delegateRecord} was found`); + } catch (err) { + // we are expecting an error, since the account must be deleted + } + + // try to revoke again + + const { tx: revoketeTx2 } = await API.revoke( + delegate.publicKey, + manager.mint, + manager.metadata, + delegate, + payer, + RevokeArgs.CollectionV1, + handler, + delegateRecord, + manager.token, + manager.masterEdition, + ); + + await revoketeTx2.assertError(t, /Delegate not found/); +}); diff --git a/token-metadata/js/test/setup/amman.ts b/token-metadata/js/test/setup/amman.ts new file mode 100644 index 0000000000..de69b36feb --- /dev/null +++ b/token-metadata/js/test/setup/amman.ts @@ -0,0 +1,11 @@ +import { Amman } from '@metaplex-foundation/amman-client'; +import { cusper } from '../utils/errors'; + +import { PROGRAM_ADDRESS } from '../../src/generated'; +import { logDebug } from '.'; + +export const amman = Amman.instance({ + knownLabels: { [PROGRAM_ADDRESS]: 'Token Metadata Program' }, + log: logDebug, + errorResolver: cusper, +}); diff --git a/token-metadata/js/test/setup/index.ts b/token-metadata/js/test/setup/index.ts new file mode 100644 index 0000000000..7945a74c24 --- /dev/null +++ b/token-metadata/js/test/setup/index.ts @@ -0,0 +1,14 @@ +import test from 'tape'; + +export * from './amman'; +export * from './txs-init'; +export * from './log'; +export * from './lut'; + +export function killStuckProcess() { + test.onFinish(() => process.exit(0)); +} + +export function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/token-metadata/js/test/setup/log.ts b/token-metadata/js/test/setup/log.ts new file mode 100644 index 0000000000..5383bf7b61 --- /dev/null +++ b/token-metadata/js/test/setup/log.ts @@ -0,0 +1,5 @@ +import debug from 'debug'; +export const logError = debug('man:test:error'); +export const logInfo = debug('man:test:info'); +export const logDebug = debug('man:test:debug'); +export const logTrace = debug('man:test:trace'); diff --git a/token-metadata/js/test/setup/lut.ts b/token-metadata/js/test/setup/lut.ts new file mode 100644 index 0000000000..8fcda3aa5a --- /dev/null +++ b/token-metadata/js/test/setup/lut.ts @@ -0,0 +1,88 @@ +import { + ConfirmedTransactionAssertablePromise, + PayerTransactionHandler, +} from '@metaplex-foundation/amman-client'; +import { + AddressLookupTableAccount, + AddressLookupTableProgram, + Connection, + Keypair, + PublicKey, + RpcResponseAndContext, + SignatureResult, + Transaction, + TransactionInstruction, + TransactionMessage, + VersionedTransaction, +} from '@solana/web3.js'; + +export async function createLookupTable( + authority: PublicKey, + payer: Keypair, + handler: PayerTransactionHandler, + connection: Connection, +): Promise<{ tx: ConfirmedTransactionAssertablePromise; lookupTable: PublicKey }> { + // get current `slot` + const slot = await connection.getSlot(); + + // create an Address Lookup Table + const [lookupTableIx, address] = AddressLookupTableProgram.createLookupTable({ + authority: authority, + payer: payer.publicKey, + recentSlot: slot, + }); + + const tx = new Transaction().add(lookupTableIx); + // send the transaction + return { + tx: handler.sendAndConfirmTransaction(tx, [payer], 'tx: Create Lookup Table'), + lookupTable: address, + }; +} + +export async function addAddressesToTable( + lookupTable: PublicKey, + authority: PublicKey, + payer: Keypair, + addresses: PublicKey[], + connection: Connection, +): Promise<{ response: RpcResponseAndContext; signature: string }> { + const addAddressesInstruction = AddressLookupTableProgram.extendLookupTable({ + payer: payer.publicKey, + authority, + lookupTable, + addresses, + }); + + return await createAndSendV0Tx(payer, [addAddressesInstruction], connection); +} + +export async function createAndSendV0Tx( + payer: Keypair, + instructions: TransactionInstruction[], + connection: Connection, + lookupTables: AddressLookupTableAccount[] = [], +): Promise<{ response: RpcResponseAndContext; signature: string }> { + const latestBlockhash = await connection.getLatestBlockhash('finalized'); + + const messageV0 = new TransactionMessage({ + payerKey: payer.publicKey, + recentBlockhash: latestBlockhash.blockhash, + instructions, + }).compileToV0Message(lookupTables); + + // creates the versioned transaction + const transaction = new VersionedTransaction(messageV0); + //console.log('Transaction size with address lookup: ' + transaction.serialize().length + ' bytes'); + transaction.sign([payer]); + + const signature = await connection.sendTransaction(transaction, { maxRetries: 5 }); + + const response = await connection.confirmTransaction({ + signature, + blockhash: latestBlockhash.blockhash, + lastValidBlockHeight: latestBlockhash.lastValidBlockHeight, + }); + + return { response, signature }; +} diff --git a/token-metadata/js/test/setup/txs-init.ts b/token-metadata/js/test/setup/txs-init.ts new file mode 100644 index 0000000000..c886910430 --- /dev/null +++ b/token-metadata/js/test/setup/txs-init.ts @@ -0,0 +1,785 @@ +import { + ConfirmedTransactionAssertablePromise, + GenLabeledKeypair, + LoadOrGenKeypair, + LOCALHOST, + PayerTransactionHandler, +} from '@metaplex-foundation/amman-client'; +import * as splToken from '@solana/spl-token'; +import { + Connection, + Keypair, + PublicKey, + SystemProgram, + SYSVAR_INSTRUCTIONS_PUBKEY, + Transaction, + TransactionInstruction, +} from '@solana/web3.js'; +import { + AssetData, + createCreateInstruction, + CreateInstructionAccounts, + CreateInstructionArgs, + createMintInstruction, + MintInstructionAccounts, + MintInstructionArgs, + createUpdateInstruction, + createTransferInstruction, + UpdateInstructionAccounts, + UpdateInstructionArgs, + PROGRAM_ID, + TokenStandard, + TransferInstructionAccounts, + TransferInstructionArgs, + AuthorizationData, + Payload, + SignMetadataInstructionAccounts, + VerifyCollectionInstructionAccounts, + createVerifyCollectionInstruction, + createSignMetadataInstruction, + Metadata, + DelegateInstructionAccounts, + DelegateInstructionArgs, + DelegateArgs, + createDelegateInstruction, + RevokeInstructionAccounts, + RevokeInstructionArgs, + createRevokeInstruction, + RevokeArgs, + LockInstructionAccounts, + LockInstructionArgs, + createLockInstruction, + UnlockInstructionAccounts, + UnlockInstructionArgs, + createUnlockInstruction, +} from '../../src/generated'; +import { Test } from 'tape'; +import { amman } from '.'; +import { UpdateTestData } from '../utils/update-test-data'; +import { + CreateOrUpdateInstructionAccounts, + CreateOrUpdateInstructionArgs, + createCreateOrUpdateInstruction, + PROGRAM_ID as TOKEN_AUTH_RULES_ID, +} from '@metaplex-foundation/mpl-token-auth-rules'; +import { + ACCOUNT_SIZE, + createInitializeAccountInstruction, + createInitializeMintInstruction, + MintLayout, + TOKEN_PROGRAM_ID, +} from '@solana/spl-token'; +import { findTokenRecordPda } from '../utils/programmable'; + +export class InitTransactions { + readonly getKeypair: LoadOrGenKeypair | GenLabeledKeypair; + + constructor(readonly resuseKeypairs = false) { + this.getKeypair = resuseKeypairs ? amman.loadOrGenKeypair : amman.genLabeledKeypair; + } + + async payer() { + const [payer, payerPair] = await this.getKeypair('Payer'); + + const connection = new Connection(LOCALHOST, 'confirmed'); + await amman.airdrop(connection, payer, 2); + + const transactionHandler = amman.payerTransactionHandler(connection, payerPair); + + return { + fstTxHandler: transactionHandler, + connection, + payer, + payerPair, + }; + } + + async create( + t: Test, + payer: Keypair, + assetData: AssetData, + decimals: number, + printSupply: number, + handler: PayerTransactionHandler, + mint: PublicKey | null = null, + metadata: PublicKey | null = null, + masterEdition: PublicKey | null = null, + skipMasterEdition = false, + ): Promise<{ + tx: ConfirmedTransactionAssertablePromise; + mint: PublicKey; + metadata: PublicKey; + masterEdition?: PublicKey; + }> { + let mintPair = null; + // create a keypair for the mint account (if needed) + if (!mint) { + const [, keypair] = await this.getKeypair('Mint Account'); + amman.addr.addLabel('Mint Account', keypair.publicKey); + mintPair = keypair; + } + + // metadata account + if (!metadata) { + const [address] = PublicKey.findProgramAddressSync( + [ + Buffer.from('metadata'), + PROGRAM_ID.toBuffer(), + mint ? mint.toBuffer() : mintPair.publicKey.toBuffer(), + ], + PROGRAM_ID, + ); + amman.addr.addLabel('Metadata Account', address); + metadata = address; + } + + if ( + !masterEdition && + (assetData.tokenStandard == TokenStandard.NonFungible || + assetData.tokenStandard == TokenStandard.ProgrammableNonFungible) && + !skipMasterEdition + ) { + // master edition (optional) + const [address] = PublicKey.findProgramAddressSync( + [ + Buffer.from('metadata'), + PROGRAM_ID.toBuffer(), + mint ? mint.toBuffer() : mintPair.publicKey.toBuffer(), + Buffer.from('edition'), + ], + PROGRAM_ID, + ); + amman.addr.addLabel('Master Edition Account', address); + masterEdition = address; + } + + const accounts: CreateInstructionAccounts = { + metadata, + masterEdition, + mint: mint ? mint : mintPair.publicKey, + authority: payer.publicKey, + payer: payer.publicKey, + splTokenProgram: splToken.TOKEN_PROGRAM_ID, + sysvarInstructions: SYSVAR_INSTRUCTIONS_PUBKEY, + updateAuthority: payer.publicKey, + }; + + const args: CreateInstructionArgs = { + createArgs: { + __kind: 'V1', + assetData, + decimals, + printSupply: + printSupply == 0 ? { __kind: 'Zero' } : { __kind: 'Limited', fields: [printSupply] }, + }, + }; + + const createIx = createCreateInstruction(accounts, args); + + if (!mint) { + // this test always initializes the mint, we we need to set the + // account to be writable and a signer + for (let i = 0; i < createIx.keys.length; i++) { + if (createIx.keys[i].pubkey.toBase58() === mintPair.publicKey.toBase58()) { + createIx.keys[i].isSigner = true; + createIx.keys[i].isWritable = true; + } + } + } + + const tx = new Transaction().add(createIx); + const signers = [payer]; + if (!mint) { + signers.push(mintPair); + } + + return { + tx: handler.sendAndConfirmTransaction(tx, signers, 'tx: Create'), + mint: mint ? mint : mintPair.publicKey, + metadata, + masterEdition, + }; + } + + async mint( + t: Test, + connection: Connection, + payer: Keypair, + mint: PublicKey, + metadata: PublicKey, + masterEdition: PublicKey, + authorizationData: AuthorizationData, + amount: number, + handler: PayerTransactionHandler, + token: PublicKey | null = null, + tokenRecord: PublicKey | null = null, + tokenOwner: PublicKey | null = null, + ): Promise<{ tx: ConfirmedTransactionAssertablePromise; token: PublicKey }> { + if (!token) { + // mint instrution will initialize a ATA account + const [tokenPda] = PublicKey.findProgramAddressSync( + [payer.publicKey.toBuffer(), splToken.TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()], + splToken.ASSOCIATED_TOKEN_PROGRAM_ID, + ); + token = tokenPda; + } + + if (!tokenOwner) { + tokenOwner = payer.publicKey; + } + + if (!tokenRecord) { + tokenRecord = findTokenRecordPda(mint, token); + } + + amman.addr.addLabel('Token Account', token); + + const metadataAccount = await Metadata.fromAccountAddress(connection, metadata); + const authConfig = metadataAccount.programmableConfig; + + const mintAcccounts: MintInstructionAccounts = { + token, + tokenOwner, + metadata, + masterEdition, + tokenRecord, + mint, + payer: payer.publicKey, + authority: payer.publicKey, + sysvarInstructions: SYSVAR_INSTRUCTIONS_PUBKEY, + splAtaProgram: splToken.ASSOCIATED_TOKEN_PROGRAM_ID, + splTokenProgram: splToken.TOKEN_PROGRAM_ID, + authorizationRules: authConfig ? authConfig.ruleSet : null, + authorizationRulesProgram: TOKEN_AUTH_RULES_ID, + }; + + const payload: Payload = { + map: new Map(), + }; + + if (!authorizationData) { + authorizationData = { + payload, + }; + } + + const mintArgs: MintInstructionArgs = { + mintArgs: { + __kind: 'V1', + amount, + authorizationData, + }, + }; + + const mintIx = createMintInstruction(mintAcccounts, mintArgs); + + // creates the transaction + + const tx = new Transaction().add(mintIx); + + return { + tx: handler.sendAndConfirmTransaction(tx, [payer], 'tx: Mint'), + token, + }; + } + + async transfer( + authority: Keypair, + tokenOwner: PublicKey, + token: PublicKey, + mint: PublicKey, + metadata: PublicKey, + edition: PublicKey, + destinationOwner: PublicKey, + destination: PublicKey, + authorizationRules: PublicKey, + amount: number, + handler: PayerTransactionHandler, + tokenRecord: PublicKey | null = null, + destinationTokenRecord: PublicKey | null = null, + ): Promise<{ tx: ConfirmedTransactionAssertablePromise }> { + amman.addr.addLabel('Mint Account', mint); + amman.addr.addLabel('Metadata Account', metadata); + if (edition != null) { + amman.addr.addLabel('Master Edition Account', edition); + } + amman.addr.addLabel('Authority', authority.publicKey); + amman.addr.addLabel('Token Owner', tokenOwner); + amman.addr.addLabel('Token Account', token); + amman.addr.addLabel('Destination', destinationOwner); + amman.addr.addLabel('Destination Token Account', destination); + + const transferAcccounts: TransferInstructionAccounts = { + authority: authority.publicKey, + tokenOwner, + token, + metadata, + mint, + edition, + destinationOwner, + destination, + payer: authority.publicKey, + splTokenProgram: splToken.TOKEN_PROGRAM_ID, + splAtaProgram: splToken.ASSOCIATED_TOKEN_PROGRAM_ID, + systemProgram: SystemProgram.programId, + sysvarInstructions: SYSVAR_INSTRUCTIONS_PUBKEY, + authorizationRules, + authorizationRulesProgram: TOKEN_AUTH_RULES_ID, + ownerTokenRecord: tokenRecord, + destinationTokenRecord, + }; + + const transferArgs: TransferInstructionArgs = { + transferArgs: { + __kind: 'V1', + amount, + authorizationData: null, + }, + }; + + const transferIx = createTransferInstruction(transferAcccounts, transferArgs); + + const tx = new Transaction().add(transferIx); + + return { + tx: handler.sendAndConfirmTransaction(tx, [authority], 'tx: Transfer'), + }; + } + + async update( + t: Test, + handler: PayerTransactionHandler, + mint: PublicKey, + metadata: PublicKey, + authority: Keypair, + updateTestData: UpdateTestData, + delegateRecord: PublicKey | null = null, + masterEdition: PublicKey | null = null, + token: PublicKey | null = null, + ruleSetPda?: PublicKey | null, + authorizationData?: AuthorizationData | null, + ): Promise<{ tx: ConfirmedTransactionAssertablePromise }> { + amman.addr.addLabel('Mint Account', mint); + amman.addr.addLabel('Metadata Account', metadata); + if (masterEdition != null) { + amman.addr.addLabel('Edition Account', masterEdition); + } + + const updateAcccounts: UpdateInstructionAccounts = { + metadata, + edition: masterEdition, + mint, + systemProgram: SystemProgram.programId, + sysvarInstructions: SYSVAR_INSTRUCTIONS_PUBKEY, + authority: authority.publicKey, + payer: authority.publicKey, + token, + delegateRecord, + authorizationRulesProgram: ruleSetPda ? TOKEN_AUTH_RULES_ID : PROGRAM_ID, + authorizationRules: ruleSetPda, + }; + + const updateArgs: UpdateInstructionArgs = { + updateArgs: { + __kind: 'V1', + newUpdateAuthority: updateTestData.newUpdateAuthority, + data: updateTestData.data, + primarySaleHappened: updateTestData.primarySaleHappened, + isMutable: updateTestData.isMutable, + collection: updateTestData.collection, + uses: updateTestData.uses, + collectionDetails: updateTestData.collectionDetails, + ruleSet: updateTestData.ruleSet, + authorizationData, + }, + }; + + const updateIx = createUpdateInstruction(updateAcccounts, updateArgs); + + const tx = new Transaction().add(updateIx); + + return { + tx: handler.sendAndConfirmTransaction(tx, [authority], 'tx: Update'), + }; + } + + async delegate( + delegate: PublicKey, + mint: PublicKey, + metadata: PublicKey, + authority: PublicKey, + payer: Keypair, + args: DelegateArgs, + handler: PayerTransactionHandler, + delegateRecord: PublicKey | null = null, + masterEdition: PublicKey | null = null, + token: PublicKey | null = null, + tokenRecord: PublicKey | null = null, + ruleSetPda: PublicKey | null = null, + ): Promise<{ tx: ConfirmedTransactionAssertablePromise }> { + const delegateAcccounts: DelegateInstructionAccounts = { + delegateRecord, + delegate, + metadata, + masterEdition, + tokenRecord, + mint, + token, + authority, + payer: payer.publicKey, + sysvarInstructions: SYSVAR_INSTRUCTIONS_PUBKEY, + splTokenProgram: splToken.TOKEN_PROGRAM_ID, + authorizationRulesProgram: TOKEN_AUTH_RULES_ID, + authorizationRules: ruleSetPda, + }; + + const mintArgs: DelegateInstructionArgs = { + delegateArgs: args, + }; + + const mintIx = createDelegateInstruction(delegateAcccounts, mintArgs); + + // creates the transaction + + const tx = new Transaction().add(mintIx); + + return { + tx: handler.sendAndConfirmTransaction(tx, [payer], 'tx: Delegate'), + }; + } + + async revoke( + delegate: PublicKey, + mint: PublicKey, + metadata: PublicKey, + authority: Keypair, + payer: Keypair, + args: RevokeArgs, + handler: PayerTransactionHandler, + delegateRecord: PublicKey | null = null, + masterEdition: PublicKey | null = null, + token: PublicKey | null = null, + tokenRecord: PublicKey | null = null, + ruleSetPda: PublicKey | null = null, + ): Promise<{ tx: ConfirmedTransactionAssertablePromise; delegate: PublicKey }> { + const revokeAcccounts: RevokeInstructionAccounts = { + delegateRecord, + delegate, + metadata, + masterEdition, + tokenRecord, + mint, + token, + authority: authority.publicKey, + payer: payer.publicKey, + sysvarInstructions: SYSVAR_INSTRUCTIONS_PUBKEY, + splTokenProgram: splToken.TOKEN_PROGRAM_ID, + authorizationRulesProgram: TOKEN_AUTH_RULES_ID, + authorizationRules: ruleSetPda, + }; + + const revokeArgs: RevokeInstructionArgs = { + revokeArgs: args, + }; + + const mintIx = createRevokeInstruction(revokeAcccounts, revokeArgs); + + // creates the transaction + + const tx = new Transaction().add(mintIx); + + return { + tx: handler.sendAndConfirmTransaction(tx, [payer, authority], 'tx: Revoke'), + delegate, + }; + } + + async lock( + delegate: Keypair, + mint: PublicKey, + metadata: PublicKey, + token: PublicKey, + payer: Keypair, + handler: PayerTransactionHandler, + tokenRecord: PublicKey | null = null, + tokenOwner: PublicKey | null = null, + masterEdition: PublicKey | null = null, + ruleSetPda: PublicKey | null = null, + ): Promise<{ tx: ConfirmedTransactionAssertablePromise }> { + const lockAcccounts: LockInstructionAccounts = { + authority: delegate.publicKey, + tokenOwner, + tokenRecord, + token, + mint, + metadata, + edition: masterEdition, + payer: payer.publicKey, + sysvarInstructions: SYSVAR_INSTRUCTIONS_PUBKEY, + splTokenProgram: splToken.TOKEN_PROGRAM_ID, + authorizationRulesProgram: TOKEN_AUTH_RULES_ID, + authorizationRules: ruleSetPda, + }; + + const lockArgs: LockInstructionArgs = { + lockArgs: { + __kind: 'V1', + authorizationData: null, + }, + }; + + const mintIx = createLockInstruction(lockAcccounts, lockArgs); + + // creates the transaction + + const tx = new Transaction().add(mintIx); + + return { + tx: handler.sendAndConfirmTransaction(tx, [payer, delegate], 'tx: Lock'), + }; + } + + async unlock( + delegate: Keypair, + mint: PublicKey, + metadata: PublicKey, + token: PublicKey, + payer: Keypair, + handler: PayerTransactionHandler, + tokenRecord: PublicKey | null = null, + tokenOwner: PublicKey | null = null, + masterEdition: PublicKey | null = null, + ruleSetPda: PublicKey | null = null, + ): Promise<{ tx: ConfirmedTransactionAssertablePromise }> { + const unlockAcccounts: UnlockInstructionAccounts = { + authority: delegate.publicKey, + tokenOwner, + tokenRecord, + token, + mint, + metadata, + edition: masterEdition, + payer: payer.publicKey, + sysvarInstructions: SYSVAR_INSTRUCTIONS_PUBKEY, + splTokenProgram: splToken.TOKEN_PROGRAM_ID, + authorizationRulesProgram: TOKEN_AUTH_RULES_ID, + authorizationRules: ruleSetPda, + }; + + const unlockArgs: UnlockInstructionArgs = { + unlockArgs: { + __kind: 'V1', + authorizationData: null, + }, + }; + + const mintIx = createUnlockInstruction(unlockAcccounts, unlockArgs); + + // creates the transaction + + const tx = new Transaction().add(mintIx); + + return { + tx: handler.sendAndConfirmTransaction(tx, [payer, delegate], 'tx: Unlock'), + }; + } + + //--------------------+ + // Helpers | + //--------------------+ + + async verifyCollection( + t: Test, + payer: Keypair, + metadata: PublicKey, + collectionMint: PublicKey, + collectionMetadata: PublicKey, + collectionMasterEdition: PublicKey, + collectionAuthority: Keypair, + handler: PayerTransactionHandler, + ): Promise<{ tx: ConfirmedTransactionAssertablePromise }> { + amman.addr.addLabel('Metadata Account', metadata); + amman.addr.addLabel('Collection Mint Account', collectionMint); + amman.addr.addLabel('Collection Metadata Account', collectionMetadata); + amman.addr.addLabel('Collection Master Edition Account', collectionMasterEdition); + + const verifyCollectionAcccounts: VerifyCollectionInstructionAccounts = { + metadata, + collectionAuthority: collectionAuthority.publicKey, + collectionMint, + collection: collectionMetadata, + collectionMasterEditionAccount: collectionMasterEdition, + payer: payer.publicKey, + }; + + const verifyInstruction = createVerifyCollectionInstruction(verifyCollectionAcccounts); + const tx = new Transaction().add(verifyInstruction); + + return { + tx: handler.sendAndConfirmTransaction( + tx, + [payer, collectionAuthority], + 'tx: Verify Collection', + ), + }; + } + async signMetadata( + t: Test, + creator: Keypair, + metadata: PublicKey, + handler: PayerTransactionHandler, + ): Promise<{ tx: ConfirmedTransactionAssertablePromise }> { + amman.addr.addLabel('Metadata Account', metadata); + + const signMetadataAcccounts: SignMetadataInstructionAccounts = { + metadata, + creator: creator.publicKey, + }; + + const signMetadataInstruction = createSignMetadataInstruction(signMetadataAcccounts); + const tx = new Transaction().add(signMetadataInstruction); + + return { + tx: handler.sendAndConfirmTransaction(tx, [creator], 'tx: Sign Metadata'), + }; + } + + async createRuleSet( + t: Test, + payer: Keypair, + ruleSetPda: PublicKey, + serializedRuleSet: Uint8Array, + handler: PayerTransactionHandler, + ): Promise<{ tx: ConfirmedTransactionAssertablePromise }> { + amman.addr.addLabel('Payer', payer.publicKey); + + const createRuleSetAccounts: CreateOrUpdateInstructionAccounts = { + ruleSetPda, + payer: payer.publicKey, + bufferPda: TOKEN_AUTH_RULES_ID, + }; + + const createRuleSetArgs: CreateOrUpdateInstructionArgs = { + createOrUpdateArgs: { + __kind: 'V1', + serializedRuleSet, + }, + }; + + const createRuleSetInstruction = createCreateOrUpdateInstruction( + createRuleSetAccounts, + createRuleSetArgs, + ); + const tx = new Transaction().add(createRuleSetInstruction); + + return { + tx: handler.sendAndConfirmTransaction(tx, [payer], 'tx: CreateOrUpdateRuleSet'), + }; + } + + async createMintAccount( + payer: Keypair, + connection: Connection, + handler: PayerTransactionHandler, + ): Promise<{ tx: ConfirmedTransactionAssertablePromise; mint: PublicKey }> { + const mint = Keypair.generate(); + amman.addr.addLabel('Mint Account', mint.publicKey); + + const ixs: TransactionInstruction[] = []; + ixs.push( + SystemProgram.createAccount({ + fromPubkey: payer.publicKey, + newAccountPubkey: mint.publicKey, + lamports: await connection.getMinimumBalanceForRentExemption(MintLayout.span), + space: MintLayout.span, + programId: TOKEN_PROGRAM_ID, + }), + ); + ixs.push(createInitializeMintInstruction(mint.publicKey, 0, payer.publicKey, payer.publicKey)); + + const tx = new Transaction().add(...ixs); + + return { + tx: handler.sendAndConfirmTransaction(tx, [payer, mint], 'tx: Create Mint Account'), + mint: mint.publicKey, + }; + } + + async createTokenAccount( + mint: PublicKey, + payer: Keypair, + connection: Connection, + handler: PayerTransactionHandler, + owner: PublicKey | null = null, + ): Promise<{ tx: ConfirmedTransactionAssertablePromise; token: PublicKey }> { + const token = Keypair.generate(); + amman.addr.addLabel('Token Account', token.publicKey); + + const tx = new Transaction(); + tx.add( + // create account + SystemProgram.createAccount({ + fromPubkey: payer.publicKey, + newAccountPubkey: token.publicKey, + space: ACCOUNT_SIZE, + lamports: await connection.getMinimumBalanceForRentExemption(ACCOUNT_SIZE), + programId: TOKEN_PROGRAM_ID, + }), + // initialize token account + createInitializeAccountInstruction(token.publicKey, mint, owner), + ); + + return { + tx: handler.sendAndConfirmTransaction(tx, [payer, token], 'tx: Create Token Account'), + token: token.publicKey, + }; + } + + async getTransferInstruction( + authority: Keypair, + tokenOwner: PublicKey, + token: PublicKey, + mint: PublicKey, + metadata: PublicKey, + edition: PublicKey, + destinationOwner: PublicKey, + destination: PublicKey, + authorizationRules: PublicKey, + amount: number, + handler: PayerTransactionHandler, + tokenRecord: PublicKey | null = null, + destinationTokenRecord: PublicKey | null = null, + ): Promise<{ instruction: TransactionInstruction }> { + const transferAcccounts: TransferInstructionAccounts = { + authority: authority.publicKey, + tokenOwner, + token, + metadata, + mint, + edition, + destinationOwner, + destination, + payer: authority.publicKey, + splTokenProgram: splToken.TOKEN_PROGRAM_ID, + splAtaProgram: splToken.ASSOCIATED_TOKEN_PROGRAM_ID, + systemProgram: SystemProgram.programId, + sysvarInstructions: SYSVAR_INSTRUCTIONS_PUBKEY, + authorizationRules, + authorizationRulesProgram: TOKEN_AUTH_RULES_ID, + ownerTokenRecord: tokenRecord, + destinationTokenRecord, + }; + + const transferArgs: TransferInstructionArgs = { + transferArgs: { + __kind: 'V1', + amount, + authorizationData: null, + }, + }; + + const instruction = createTransferInstruction(transferAcccounts, transferArgs); + + return { instruction }; + } +} diff --git a/token-metadata/js/test/transfer.test.ts b/token-metadata/js/test/transfer.test.ts new file mode 100644 index 0000000000..b0a3bb04f8 --- /dev/null +++ b/token-metadata/js/test/transfer.test.ts @@ -0,0 +1,1202 @@ +import test from 'tape'; +import { + addAddressesToTable, + amman, + createAndSendV0Tx, + createLookupTable, + InitTransactions, + killStuckProcess, + sleep, +} from './setup'; +import { + AddressLookupTableAccount, + Keypair, + PublicKey, + sendAndConfirmTransaction, + SYSVAR_INSTRUCTIONS_PUBKEY, + Transaction, +} from '@solana/web3.js'; +import { createAndMintDefaultAsset } from './utils/digital-asset-manager'; +import { + createAssociatedTokenAccount, + createAssociatedTokenAccountInstruction, + getAccount, + getAssociatedTokenAddress, + getOrCreateAssociatedTokenAccount, + TOKEN_PROGRAM_ID, +} from '@solana/spl-token'; +import * as splToken from '@solana/spl-token'; +import { + Metadata, + DelegateArgs, + TokenStandard, + TokenRecord, + TokenDelegateRole, +} from '../src/generated'; +import { PROGRAM_ID as TOKEN_AUTH_RULES_ID } from '@metaplex-foundation/mpl-token-auth-rules'; +import { PROGRAM_ID as TOKEN_METADATA_ID } from '../src/generated'; +import { encode } from '@msgpack/msgpack'; +import spok from 'spok'; +import { spokSameBignum, spokSamePubkey } from './utils'; +import { findTokenRecordPda } from './utils/programmable'; + +killStuckProcess(); + +test('Transfer: NonFungible', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const { mint, metadata, masterEdition, token } = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + ); + + const owner = payer; + const destination = Keypair.generate(); + const destinationToken = await getOrCreateAssociatedTokenAccount( + connection, + payer, + mint, + destination.publicKey, + ); + + const amountBeforeTransfer = destinationToken.amount; + + // transfer + + const amount = 1; + + const { tx: transferTx } = await API.transfer( + payer, + owner.publicKey, + token, + mint, + metadata, + masterEdition, + destination.publicKey, + destinationToken.address, + null, + amount, + handler, + ); + + await transferTx.assertSuccess(t); + + // asserts + + const amountAfterTransfer = (await getAccount(connection, destinationToken.address)).amount; + const remainingAmount = (await getAccount(connection, token)).amount; + + t.true( + amountAfterTransfer > amountBeforeTransfer, + 'amount after transfer is greater than before', + ); + t.true(amountAfterTransfer.toString() === '1', 'destination amount equal to 1'); + t.true(remainingAmount.toString() === '0', 'source amount equal to 0'); +}); + +test('Transfer: ProgrammableNonFungible (wallet-to-wallet)', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const owner = payer; + const authority = payer; + const destination = Keypair.generate(); + const invalidDestination = Keypair.generate(); + + amman.airdrop(connection, destination.publicKey, 1); + amman.airdrop(connection, invalidDestination.publicKey, 1); + + // Set up our rule set with one pubkey match rule for transfer. + + const ruleSetName = 'transfer_test'; + const ruleSet = { + libVersion: 1, + ruleSetName: ruleSetName, + owner: Array.from(owner.publicKey.toBytes()), + operations: { + Transfer: { + ProgramOwned: { + program: Array.from(owner.publicKey.toBytes()), + field: 'Destination', + }, + }, + }, + }; + const serializedRuleSet = encode(ruleSet); + + // Find the ruleset PDA + const [ruleSetPda] = PublicKey.findProgramAddressSync( + [Buffer.from('rule_set'), payer.publicKey.toBuffer(), Buffer.from(ruleSetName)], + TOKEN_AUTH_RULES_ID, + ); + + // Create the ruleset at the PDA address with the serialized ruleset values. + const { tx: createRuleSetTx } = await API.createRuleSet( + t, + payer, + ruleSetPda, + serializedRuleSet, + handler, + ); + await createRuleSetTx.assertSuccess(t); + + // Create an NFT with the programmable config stored on the metadata. + const { mint, metadata, masterEdition, token } = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ruleSetPda, + ); + + const metadataAccount = await Metadata.fromAccountAddress(connection, metadata); + spok(t, metadataAccount.programmableConfig, { + ruleSet: spokSamePubkey(ruleSetPda), + }); + + const tokenAccount = await getAccount(connection, token, 'confirmed', TOKEN_PROGRAM_ID); + t.true(tokenAccount.amount.toString() === '1', 'token account amount equal to 1'); + + const destinationToken = await createAssociatedTokenAccount( + connection, + payer, + mint, + destination.publicKey, + ); + + // owner token record + const ownerTokenRecord = findTokenRecordPda(mint, token); + amman.addr.addLabel('Owner Token Record', ownerTokenRecord); + // destination token record + const destinationTokenRecord = findTokenRecordPda(mint, destinationToken); + amman.addr.addLabel('Destination Token Record', destinationTokenRecord); + + // Transfer the NFT to the destination account, this should work since + // the destination account is in the ruleset. + const { tx: transferTx } = await API.transfer( + authority, + owner.publicKey, + token, + mint, + metadata, + masterEdition, + destination.publicKey, + destinationToken, + ruleSetPda, + 1, + handler, + ownerTokenRecord, + destinationTokenRecord, + ); + + await transferTx.assertSuccess(t); + + t.true( + (await getAccount(connection, token)).amount.toString() === '0', + 'token amount after transfer equal to 0', + ); +}); + +test('Transfer: ProgrammableNonFungible (program-owned)', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const owner = payer; + const authority = payer; + + // Set up our rule set with one pubkey match rule for transfer + // where the target is a program-owned account of the Token Metadata + // program. + const ruleSetName = 'transfer_test'; + const ruleSet = { + libVersion: 1, + ruleSetName: ruleSetName, + owner: Array.from(owner.publicKey.toBytes()), + operations: { + 'Transfer:Owner': { + ProgramOwned: { + program: Array.from(TOKEN_METADATA_ID.toBytes()), + field: 'Destination', + }, + }, + }, + }; + const serializedRuleSet = encode(ruleSet); + + // Find the ruleset PDA + const [ruleSetPda] = PublicKey.findProgramAddressSync( + [Buffer.from('rule_set'), payer.publicKey.toBuffer(), Buffer.from(ruleSetName)], + TOKEN_AUTH_RULES_ID, + ); + + // Create the ruleset at the PDA address with the serialized ruleset values. + const { tx: createRuleSetTx } = await API.createRuleSet( + t, + payer, + ruleSetPda, + serializedRuleSet, + handler, + ); + await createRuleSetTx.assertSuccess(t); + + // Create an NFT with the programmable config stored on the metadata. + const { mint, metadata, masterEdition, token } = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ruleSetPda, + ); + + const metadataAccount = await Metadata.fromAccountAddress(connection, metadata); + spok(t, metadataAccount.programmableConfig, { + ruleSet: spokSamePubkey(ruleSetPda), + }); + + const tokenAccount = await getAccount(connection, token, 'confirmed', TOKEN_PROGRAM_ID); + t.true(tokenAccount.amount.toString() === '1', 'token account amount equal to 1'); + + // [FAIL] Our first destination is going to be an account owned by the + // mpl-token-auth-rules program as a convenient program-owned account + // that is not owned by token-metadata. + const invalidDestination = ruleSetPda; + + // We have to manually run the create ATA transaction since the helper + // function from SPL token does not allow creating one for an off-curve + // address. + const invalidDestinationToken = await getAssociatedTokenAddress( + mint, + invalidDestination, + true, // Allow off-curve addresses + splToken.TOKEN_PROGRAM_ID, + splToken.ASSOCIATED_TOKEN_PROGRAM_ID, + ); + + const invalidAtaTx = new Transaction().add( + createAssociatedTokenAccountInstruction( + payer.publicKey, + invalidDestinationToken, + invalidDestination, + mint, + splToken.TOKEN_PROGRAM_ID, + splToken.ASSOCIATED_TOKEN_PROGRAM_ID, + ), + ); + + await sendAndConfirmTransaction(connection, invalidAtaTx, [payer]); + + // owner token record + const ownerTokenRecord = findTokenRecordPda(mint, token); + amman.addr.addLabel('Owner Token Record', ownerTokenRecord); + // destination token record + let destinationTokenRecord = findTokenRecordPda(mint, invalidDestinationToken); + amman.addr.addLabel('Destination Token Record', destinationTokenRecord); + + // Transfer the NFT to the invalid destination account, this should fail. + const { tx: invalidTransferTx } = await API.transfer( + authority, + owner.publicKey, + token, + mint, + metadata, + masterEdition, + invalidDestination, + invalidDestinationToken, + ruleSetPda, + 1, + handler, + ownerTokenRecord, + destinationTokenRecord, + ); + + // Cusper matches the error code from mpl-token-auth-rules + // to a mpl-token-metadata error which gives us the wrong message + // so we match on the actual log values here instead. + invalidTransferTx.then((x) => + x.assertLogs(t, [/Program Owned check failed/i], { + txLabel: 'tx: Transfer', + }), + ); + await invalidTransferTx.assertError(t); + + // Transfer failed so token should still be present on the original + // account. + t.true( + (await getAccount(connection, token)).amount.toString() === '1', + 'token amount after transfer equal to 1', + ); + t.true( + (await getAccount(connection, invalidDestinationToken)).amount.toString() === '0', + 'token amount after transfer equal to 0', + ); + + // [SUCESS] Our valid destination is going to be an account owned by the + // mpl-token-metadata program. Any one will do so for convenience + // we just use the existing metadata account. + const destination = metadata; + + // We have to manually run the create ATA transaction since the helper + // function from SPL token does not allow creating one for an off-curve + // address. + const destinationToken = await getAssociatedTokenAddress( + mint, + destination, + true, // Allow off-curve addresses + splToken.TOKEN_PROGRAM_ID, + splToken.ASSOCIATED_TOKEN_PROGRAM_ID, + ); + + const ataTx = new Transaction().add( + createAssociatedTokenAccountInstruction( + payer.publicKey, + destinationToken, + destination, + mint, + splToken.TOKEN_PROGRAM_ID, + splToken.ASSOCIATED_TOKEN_PROGRAM_ID, + ), + ); + + await sendAndConfirmTransaction(connection, ataTx, [payer]); + + // destination token record + destinationTokenRecord = findTokenRecordPda(mint, destinationToken); + amman.addr.addLabel('Destination Token Record', destinationTokenRecord); + + // Transfer the NFT to the destination account, this should work since + // the destination account is in the ruleset. + const { tx: transferTx } = await API.transfer( + authority, + owner.publicKey, + token, + mint, + metadata, + masterEdition, + destination, + destinationToken, + ruleSetPda, + 1, + handler, + ownerTokenRecord, + destinationTokenRecord, + ); + + // Cusper matches the error code from mpl-token-auth-rules + // to a mpl-token-metadata error which gives us the wrong message + // so we match on the actual log values here instead. + await transferTx.assertSuccess(t); + + // Transfer succeed so token should have moved to the destination + // account. + t.true( + (await getAccount(connection, token)).amount.toString() === '0', + 'token amount after transfer equal to 0', + ); + t.true( + (await getAccount(connection, destinationToken)).amount.toString() === '1', + 'token amount after transfer equal to 1', + ); +}); + +/* +test('Transfer: NonFungibleEdition', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + +Need to call print instead of mint + const { mint, metadata, masterEdition, token } = await createAndMintDefaultAsset( + t, + API, + handler, + payer, + TokenStandard.NonFungibleEdition, + ); + + const owner = payer; + const destination = Keypair.generate(); + const destinationToken = await createAssociatedTokenAccount( + connection, + payer, + mint, + destination.publicKey, + ); + const amount = 1; + + const { tx: transferTx } = await API.transfer( + owner, + token, + mint, + metadata, + masterEdition, + destination.publicKey, + destinationToken, + amount, + handler, + ); + + await transferTx.assertSuccess(t); +}); +*/ + +test('Transfer: Fungible', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const { mint, metadata, masterEdition, token } = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.Fungible, + null, + 100, + ); + + const owner = payer; + const destination = Keypair.generate(); + const destinationToken = await getOrCreateAssociatedTokenAccount( + connection, + payer, + mint, + destination.publicKey, + ); + + const amountBeforeTransfer = destinationToken.amount; + + // transfer + + const amount = 5; + + const { tx: transferTx } = await API.transfer( + payer, + owner.publicKey, + token, + mint, + metadata, + masterEdition, + destination.publicKey, + destinationToken.address, + null, + amount, + handler, + ); + + await transferTx.assertSuccess(t); + + // asserts + + const amountAfterTransfer = (await getAccount(connection, destinationToken.address)).amount; + const remainingAmount = (await getAccount(connection, token)).amount; + + t.true( + amountAfterTransfer > amountBeforeTransfer, + 'amount after transfer is greater than before', + ); + t.true(amountAfterTransfer.toString() === '5', 'destination amount equal to 5'); + t.equal(remainingAmount.toString(), '95', 'remaining amount after transfer is 95'); +}); + +test('Transfer: FungibleAsset', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const { mint, metadata, masterEdition, token } = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.FungibleAsset, + null, + 10, + ); + + const owner = payer; + const destination = Keypair.generate(); + const destinationToken = await getOrCreateAssociatedTokenAccount( + connection, + payer, + mint, + destination.publicKey, + ); + + const amountBeforeTransfer = destinationToken.amount; + + // transfer + + const amount = 5; + + const { tx: transferTx } = await API.transfer( + payer, + owner.publicKey, + token, + mint, + metadata, + masterEdition, + destination.publicKey, + destinationToken.address, + null, + amount, + handler, + ); + + await transferTx.assertSuccess(t); + + // asserts + + const amountAfterTransfer = (await getAccount(connection, destinationToken.address)).amount; + const remainingAmount = (await getAccount(connection, token)).amount; + + t.true( + amountAfterTransfer > amountBeforeTransfer, + 'amount after transfer is greater than before', + ); + t.true(amountAfterTransfer.toString() === '5', 'destination amount equal to 5'); + t.equal(remainingAmount.toString(), '5', 'remaining amount after transfer is 5'); +}); + +test('Transfer: NonFungible asset with delegate', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const owner = payer; + + const { mint, metadata, masterEdition, token } = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.NonFungible, + null, + 1, + ); + + // Generate the delegate keypair + const delegate = Keypair.generate(); + + const delegateArgs: DelegateArgs = { + __kind: 'StandardV1', + amount: 1, + }; + + // Approve delegate + const { tx: delegateTx } = await API.delegate( + delegate.publicKey, + mint, + metadata, + payer.publicKey, + payer, + delegateArgs, + handler, + null, + masterEdition, + token, + ); + await delegateTx.assertSuccess(t); + + const destination = Keypair.generate(); + const destinationToken = await getOrCreateAssociatedTokenAccount( + connection, + payer, + mint, + destination.publicKey, + ); + + const fakeDelegate = Keypair.generate(); + + const amount = 1; + + // [FAIL] Try to transfer with fake delegate. This should fail. + const { tx: fakeDelegateTransferTx } = await API.transfer( + fakeDelegate, // Transfer authority: the fake delegate + payer.publicKey, // Owner of the asset + token, + mint, + metadata, + masterEdition, + destination.publicKey, + destinationToken.address, + null, + amount, + handler, + ); + + await fakeDelegateTransferTx.assertError(t, /Invalid authority type/); + + // Transfer using the legitimate delegate + // Try to transfer with fake delegate. This should fail. + const { tx: transferTx } = await API.transfer( + delegate, // Transfer authority: the real delegate + owner.publicKey, // Owner of the asset + token, + mint, + metadata, + masterEdition, + destination.publicKey, + destinationToken.address, + null, + amount, + handler, + ); + + await transferTx.assertSuccess(t); +}); + +test('Transfer: NonFungible asset with invalid authority', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const { mint, metadata, masterEdition, token } = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.NonFungible, + null, + 1, + ); + + // This is not a delegate, owner, or a public key in auth rules. + // Because this is a NFT not a PNFT, it will fail as an + // invalid authority, not as a failed auth rules check. + const invalidAuthority = Keypair.generate(); + + const destination = Keypair.generate(); + const destinationToken = await getOrCreateAssociatedTokenAccount( + connection, + payer, + mint, + destination.publicKey, + ); + + const amount = 1; + + // Try to transfer with fake delegate. This should fail. + const { tx: fakeDelegateTransferTx } = await API.transfer( + invalidAuthority, // transfer authority: the invalid authority + payer.publicKey, // Owner of the asset + token, + mint, + metadata, + masterEdition, + destination.publicKey, + destinationToken.address, + null, + amount, + handler, + ); + + await fakeDelegateTransferTx.assertError(t, /Invalid authority type/); +}); + +test('Transfer: ProgrammableNonFungible asset with invalid authority', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const owner = payer; + + // We add this authority to the rule_set as an "Authority" + // type, which will allow it to transfer the asset. + const validAuthority = Keypair.generate(); + + // This is not a delegate, owner, or a public key in auth rules. + const invalidAuthority = Keypair.generate(); + + // Set up our rule set + const ruleSetName = 'transfer_test'; + const ruleSet = { + libVersion: 1, + ruleSetName: ruleSetName, + owner: Array.from(owner.publicKey.toBytes()), + operations: { + Transfer: { + PubkeyMatch: { + pubkey: Array.from(validAuthority.publicKey.toBytes()), + field: 'Authority', + }, + }, + }, + }; + const serializedRuleSet = encode(ruleSet); + + // Find the ruleset PDA + const [ruleSetPda] = PublicKey.findProgramAddressSync( + [Buffer.from('rule_set'), payer.publicKey.toBuffer(), Buffer.from(ruleSetName)], + TOKEN_AUTH_RULES_ID, + ); + + // Create the ruleset at the PDA address with the serialized ruleset values. + const { tx: createRuleSetTx } = await API.createRuleSet( + t, + payer, + ruleSetPda, + serializedRuleSet, + handler, + ); + await createRuleSetTx.assertSuccess(t); + + const { mint, metadata, masterEdition, token } = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ruleSetPda, + 1, + ); + + const destination = Keypair.generate(); + const destinationToken = await getOrCreateAssociatedTokenAccount( + connection, + payer, + mint, + destination.publicKey, + ); + + const amount = 1; + + // Try to transfer with fake delegate. This should fail. + const { tx: invalidTransferTx } = await API.transfer( + invalidAuthority, // transfer authority: the invalid authority + payer.publicKey, // Owner of the asset + token, + mint, + metadata, + masterEdition, + destination.publicKey, + destinationToken.address, + ruleSetPda, + amount, + handler, + ); + + await invalidTransferTx.assertError(t, /Invalid authority type/); +}); + +test('Transfer: ProgrammableNonFungible (uninitialized wallet-to-wallet)', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const owner = payer; + const authority = payer; + const destination = Keypair.generate(); + const invalidDestination = Keypair.generate(); + + amman.airdrop(connection, destination.publicKey, 1); + amman.airdrop(connection, invalidDestination.publicKey, 1); + + // Set up our rule set with one pubkey match rule for transfer. + + const ruleSetName = 'transfer_test'; + const ruleSet = { + libVersion: 1, + ruleSetName: ruleSetName, + owner: Array.from(owner.publicKey.toBytes()), + operations: { + Transfer: { + ProgramOwned: { + program: Array.from(owner.publicKey.toBytes()), + field: 'Destination', + }, + }, + }, + }; + const serializedRuleSet = encode(ruleSet); + + // Find the ruleset PDA + const [ruleSetPda] = PublicKey.findProgramAddressSync( + [Buffer.from('rule_set'), payer.publicKey.toBuffer(), Buffer.from(ruleSetName)], + TOKEN_AUTH_RULES_ID, + ); + + // Create the ruleset at the PDA address with the serialized ruleset values. + const { tx: createRuleSetTx } = await API.createRuleSet( + t, + payer, + ruleSetPda, + serializedRuleSet, + handler, + ); + await createRuleSetTx.assertSuccess(t); + + // Create an NFT with the programmable config stored on the metadata. + const { mint, metadata, masterEdition, token } = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ruleSetPda, + ); + + const metadataAccount = await Metadata.fromAccountAddress(connection, metadata); + spok(t, metadataAccount.programmableConfig, { + ruleSet: spokSamePubkey(ruleSetPda), + }); + + const tokenAccount = await getAccount(connection, token, 'confirmed', TOKEN_PROGRAM_ID); + t.true(tokenAccount.amount.toString() === '1', 'token account amount equal to 1'); + + const [destinationToken] = PublicKey.findProgramAddressSync( + [destination.publicKey.toBuffer(), splToken.TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()], + splToken.ASSOCIATED_TOKEN_PROGRAM_ID, + ); + + // owner token record + const ownerTokenRecord = findTokenRecordPda(mint, token); + amman.addr.addLabel('Owner Token Record', ownerTokenRecord); + // destination token record + const destinationTokenRecord = findTokenRecordPda(mint, destinationToken); + amman.addr.addLabel('Destination Token Record', destinationTokenRecord); + + // Transfer the NFT to the destination account, this should work since + // the destination account is in the ruleset. + const { tx: transferTx } = await API.transfer( + authority, + owner.publicKey, + token, + mint, + metadata, + masterEdition, + destination.publicKey, + destinationToken, + ruleSetPda, + 1, + handler, + ownerTokenRecord, + destinationTokenRecord, + ); + + await transferTx.assertSuccess(t); + + t.true( + (await getAccount(connection, token)).amount.toString() === '0', + 'token amount after transfer equal to 0', + ); +}); + +test('Transfer: ProgrammableNonFungible (rule set revision)', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + const owner = payer; + + // create a rule set that allows transfers to token metadata (revision 0) + + const ruleSetName = 'transfer_test'; + const ruleSetTokenMetadata = { + libVersion: 1, + ruleSetName: ruleSetName, + owner: Array.from(owner.publicKey.toBytes()), + operations: { + 'Transfer:TransferDelegate': { + ProgramOwned: { + program: Array.from(TOKEN_METADATA_ID.toBytes()), + field: 'Destination', + }, + }, + 'Transfer:Owner': { + ProgramOwned: { + program: Array.from(TOKEN_METADATA_ID.toBytes()), + field: 'Destination', + }, + }, + }, + }; + + const [ruleSetPda] = PublicKey.findProgramAddressSync( + [Buffer.from('rule_set'), payer.publicKey.toBuffer(), Buffer.from(ruleSetName)], + TOKEN_AUTH_RULES_ID, + ); + + const { tx: createRuleSetTx } = await API.createRuleSet( + t, + payer, + ruleSetPda, + encode(ruleSetTokenMetadata), + handler, + ); + await createRuleSetTx.assertSuccess(t); + + // creates a pNFT + + const { mint, metadata, masterEdition, token } = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ruleSetPda, + ); + + // creates a delegate + + const [, delegate] = await API.getKeypair('Delegate'); + amman.airdrop(connection, delegate.publicKey, 1); + // token record PDA + const tokenRecord = findTokenRecordPda(mint, token); + amman.addr.addLabel('Token Record', tokenRecord); + + const args: DelegateArgs = { + __kind: 'TransferV1', + amount: 1, + authorizationData: null, + }; + + const { tx: delegateTx } = await API.delegate( + delegate.publicKey, + mint, + metadata, + payer.publicKey, + payer, + args, + handler, + null, + masterEdition, + token, + tokenRecord, + ruleSetPda, + ); + + await delegateTx.assertSuccess(t); + + // checks that the rule set revision has been saved + + const pda = await TokenRecord.fromAccountAddress(connection, tokenRecord); + + spok(t, pda, { + delegate: spokSamePubkey(delegate.publicKey), + delegateRole: TokenDelegateRole.Transfer, + ruleSetRevision: spokSameBignum(0), + }); + + // updates the rule set to allow transfers only to token auth rules (revision 1) + + const ruleSetTokenAuthRules = { + libVersion: 1, + ruleSetName: ruleSetName, + owner: Array.from(owner.publicKey.toBytes()), + operations: { + 'Transfer:TransferDelegate': { + ProgramOwned: { + program: Array.from(TOKEN_AUTH_RULES_ID.toBytes()), + field: 'Destination', + }, + }, + 'Transfer:Owner': { + ProgramOwned: { + program: Array.from(TOKEN_AUTH_RULES_ID.toBytes()), + field: 'Destination', + }, + }, + }, + }; + + const { tx: createRuleSetTx2 } = await API.createRuleSet( + t, + payer, + ruleSetPda, + encode(ruleSetTokenAuthRules), + handler, + ); + await createRuleSetTx2.assertSuccess(t); + + // performs a transfer using the delegate to the metadata account, which is + // allowed by revision 0 (this will work because the revision was saved when + // we set the delegate) + + const [destinationToken] = PublicKey.findProgramAddressSync( + [metadata.toBuffer(), splToken.TOKEN_PROGRAM_ID.toBuffer(), mint.toBuffer()], + splToken.ASSOCIATED_TOKEN_PROGRAM_ID, + ); + + // owner token record + const ownerTokenRecord = findTokenRecordPda(mint, token); + amman.addr.addLabel('Owner Token Record', ownerTokenRecord); + // destination token record + const destinationTokenRecord = findTokenRecordPda(mint, destinationToken); + amman.addr.addLabel('Destination Token Record', destinationTokenRecord); + + // Transfer the NFT to the destination account, this should work since + // the destination account is in the ruleset. + const { tx: transferTx } = await API.transfer( + owner, + owner.publicKey, + token, + mint, + metadata, + masterEdition, + metadata, + destinationToken, + ruleSetPda, + 1, + handler, + ownerTokenRecord, + destinationTokenRecord, + ); + + await transferTx.assertSuccess(t); + + t.true( + (await getAccount(connection, token)).amount.toString() === '0', + 'token amount after transfer equal to 0', + ); +}); + +test('Transfer: ProgrammableNonFungible with address lookup table (LUT)', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + // 1) prepares the rule set and the programmable NFT for the transfer + + const owner = payer; + const destination = Keypair.generate(); + amman.airdrop(connection, destination.publicKey, 1); + + //-- set up our rule set with one pubkey match rule for transfer. + + const ruleSetName = 'transfer_test'; + const ruleSet = { + libVersion: 1, + ruleSetName: ruleSetName, + owner: Array.from(owner.publicKey.toBytes()), + operations: { + 'Transfer:Owner': { + ProgramOwned: { + program: Array.from(owner.publicKey.toBytes()), + field: 'Destination', + }, + }, + }, + }; + const serializedRuleSet = encode(ruleSet); + + //-- Find the ruleset PDA + const [ruleSetPda] = PublicKey.findProgramAddressSync( + [Buffer.from('rule_set'), payer.publicKey.toBuffer(), Buffer.from(ruleSetName)], + TOKEN_AUTH_RULES_ID, + ); + + //-- create the ruleset at the PDA address with the serialized ruleset values. + const { tx: createRuleSetTx } = await API.createRuleSet( + t, + payer, + ruleSetPda, + serializedRuleSet, + handler, + ); + await createRuleSetTx.assertSuccess(t); + + //-- create an NFT with the programmable config stored on the metadata. + const { mint, metadata, masterEdition, token } = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ruleSetPda, + ); + + const metadataAccount = await Metadata.fromAccountAddress(connection, metadata); + spok(t, metadataAccount.programmableConfig, { + ruleSet: spokSamePubkey(ruleSetPda), + }); + + const tokenAccount = await getAccount(connection, token, 'confirmed', TOKEN_PROGRAM_ID); + t.true(tokenAccount.amount.toString() === '1', 'token account amount equal to 1'); + + const destinationToken = await createAssociatedTokenAccount( + connection, + payer, + mint, + destination.publicKey, + ); + + // owner token record + const ownerTokenRecord = findTokenRecordPda(mint, token); + amman.addr.addLabel('Owner Token Record', ownerTokenRecord); + // destination token record + const destinationTokenRecord = findTokenRecordPda(mint, destinationToken); + amman.addr.addLabel('Destination Token Record', destinationTokenRecord); + + // 2) creates the lookup table (in practice the LUT would be created when the user 'deposits' + // the NFT into the program) + + const { tx, lookupTable } = await createLookupTable(payer.publicKey, payer, handler, connection); + await tx.assertSuccess(t); + + //-- adds addresses to the lookup table + + const addresses = [ + owner.publicKey, + ownerTokenRecord, + token, + mint, + metadata, + masterEdition, + ruleSetPda, + SYSVAR_INSTRUCTIONS_PUBKEY, + ]; + + const { response } = await addAddressesToTable( + lookupTable, + payer.publicKey, + payer, + addresses, + connection, + ); + + t.true(response.value.err == null); + + const account = await connection.getAccountInfo(lookupTable); + const table = AddressLookupTableAccount.deserialize(account.data); + + spok(t, table, { + authority: spokSamePubkey(payer.publicKey), + addresses: [...addresses.map((value) => spokSamePubkey(value))], + }); + + // 3) transfer the programmable NFT using the LUT + + const { instruction: transferIx } = await API.getTransferInstruction( + owner, + owner.publicKey, + token, + mint, + metadata, + masterEdition, + destination.publicKey, + destinationToken, + ruleSetPda, + 1, + handler, + ownerTokenRecord, + destinationTokenRecord, + ); + + const lookupTableAccount = await connection.getAddressLookupTable(lookupTable); + + console.log('[ Waiting for lookup table activation ]'); + await sleep(1000); + + await createAndSendV0Tx(payer, [transferIx], connection, [lookupTableAccount.value]); + + t.true( + (await getAccount(connection, token)).amount.toString() === '0', + 'token amount after transfer equal to 0', + ); +}); diff --git a/token-metadata/js/test/unlock.test.ts b/token-metadata/js/test/unlock.test.ts new file mode 100644 index 0000000000..1a3c099ec4 --- /dev/null +++ b/token-metadata/js/test/unlock.test.ts @@ -0,0 +1,399 @@ +import { getAccount } from '@solana/spl-token'; +import { BN } from 'bn.js'; +import spok from 'spok'; +import { DelegateArgs, TokenRecord, TokenStandard, TokenState } from '../src/generated'; +import test from 'tape'; +import { amman, InitTransactions, killStuckProcess } from './setup'; +import { spokSameBigint } from './utils'; +import { createAndMintDefaultAsset } from './utils/digital-asset-manager'; +import { findTokenRecordPda } from './utils/programmable'; + +killStuckProcess(); + +test('Unlock: owner unlock NonFungible asset', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.NonFungible, + ); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + amount: spokSameBigint(new BN(1)), + isFrozen: false, + owner: payer.publicKey, + }); + } + + // creates a delegate + + const [, delegate] = await API.getKeypair('Delegate'); + + const args: DelegateArgs = { + __kind: 'StandardV1', + amount: 1, + }; + + const { tx: delegateTx } = await API.delegate( + delegate.publicKey, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + args, + handler, + null, + manager.masterEdition, + manager.token, + ); + + await delegateTx.assertSuccess(t); + + // lock asset + + const { tx: lockTx } = await API.lock( + delegate, + manager.mint, + manager.metadata, + manager.token, + payer, + handler, + null, + null, + manager.masterEdition, + ); + await lockTx.assertSuccess(t); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + isFrozen: true, + }); + } + + // unlock asset + + const { tx: unlockTx } = await API.unlock( + payer, + manager.mint, + manager.metadata, + manager.token, + payer, + handler, + null, + manager.masterEdition, + ); + await unlockTx.assertError(t, /Invalid authority type/); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + isFrozen: true, + }); + } +}); + +test('Unlock: owner unlock ProgrammableNonFungible asset', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + amount: spokSameBigint(new BN(1)), + isFrozen: true, + owner: payer.publicKey, + }); + } + + const tokenRecord = findTokenRecordPda(manager.mint, manager.token); + amman.addr.addLabel('Token Record', tokenRecord); + + let pda = await TokenRecord.fromAccountAddress(connection, tokenRecord); + + spok(t, pda, { + state: TokenState.Unlocked /* asset should be unlocked */, + }); + + // creates a delegate + + const [, delegate] = await API.getKeypair('Delegate'); + + const args: DelegateArgs = { + __kind: 'UtilityV1', + amount: 1, + authorizationData: null, + }; + + const { tx: delegateTx } = await API.delegate( + delegate.publicKey, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + args, + handler, + null, + manager.masterEdition, + manager.token, + tokenRecord, + ); + + await delegateTx.assertSuccess(t); + + // lock asset + + const { tx: lockTx } = await API.lock( + delegate, + manager.mint, + manager.metadata, + manager.token, + payer, + handler, + tokenRecord, + manager.masterEdition, + ); + await lockTx.assertSuccess(t); + + pda = await TokenRecord.fromAccountAddress(connection, tokenRecord); + + spok(t, pda, { + state: TokenState.Locked /* asset should be locked */, + }); + + // unlock asset + + const { tx: unlockTx } = await API.unlock( + payer, + manager.mint, + manager.metadata, + manager.token, + payer, + handler, + tokenRecord, + manager.masterEdition, + ); + await unlockTx.assertError(t, /Invalid authority type/); + + pda = await TokenRecord.fromAccountAddress(connection, tokenRecord); + + spok(t, pda, { + state: TokenState.Locked /* should be unlocked still */, + }); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + isFrozen: true, + }); + } +}); + +test('Unlock: unlock Fungible asset', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.Fungible, + null, + 100, + ); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + amount: spokSameBigint(new BN(100)), + isFrozen: false, + owner: payer.publicKey, + }); + } + + // creates a delegate + + const [, delegate] = await API.getKeypair('Delegate'); + + const args: DelegateArgs = { + __kind: 'StandardV1', + amount: 100, + }; + + const { tx: delegateTx } = await API.delegate( + delegate.publicKey, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + args, + handler, + null, + null, + manager.token, + ); + + await delegateTx.assertSuccess(t); + + // lock asset + + const { tx: lockTx } = await API.lock( + delegate, + manager.mint, + manager.metadata, + manager.token, + payer, + handler, + null, + payer.publicKey, + ); + await lockTx.assertSuccess(t); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + isFrozen: true, + }); + } + + // lock asset + + const { tx: unlockTx } = await API.unlock( + delegate, + manager.mint, + manager.metadata, + manager.token, + payer, + handler, + null, + payer.publicKey, + ); + await unlockTx.assertSuccess(t); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + isFrozen: false, + }); + } +}); + +test('Unlock: delegate unlock NonFungible asset', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const manager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.NonFungible, + ); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + amount: spokSameBigint(new BN(1)), + isFrozen: false, + owner: payer.publicKey, + }); + } + + // creates a delegate + + const [, delegate] = await API.getKeypair('Delegate'); + + const args: DelegateArgs = { + __kind: 'StandardV1', + amount: 1, + }; + + const { tx: delegateTx } = await API.delegate( + delegate.publicKey, + manager.mint, + manager.metadata, + payer.publicKey, + payer, + args, + handler, + null, + manager.masterEdition, + manager.token, + ); + + await delegateTx.assertSuccess(t); + + // lock asset with delegate + + const { tx: utilityTx } = await API.lock( + delegate, + manager.mint, + manager.metadata, + manager.token, + payer, + handler, + null, + null, + manager.masterEdition, + ); + await utilityTx.assertSuccess(t); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + isFrozen: true, + }); + } + + // unlock asset with delegate + + const { tx: unlockTx } = await API.unlock( + delegate, + manager.mint, + manager.metadata, + manager.token, + payer, + handler, + null, + null, + manager.masterEdition, + ); + await unlockTx.assertSuccess(t); + + if (manager.token) { + const tokenAccount = await getAccount(connection, manager.token); + + spok(t, tokenAccount, { + isFrozen: false, + }); + } +}); diff --git a/token-metadata/js/test/update.test.ts b/token-metadata/js/test/update.test.ts new file mode 100644 index 0000000000..8db09f251f --- /dev/null +++ b/token-metadata/js/test/update.test.ts @@ -0,0 +1,1424 @@ +import spok from 'spok'; +import { + AssetData, + Data, + DelegateArgs, + Metadata, + PROGRAM_ID, + TokenRecord, + TokenStandard, + TokenState, +} from '../src/generated'; +import test from 'tape'; +import { amman, InitTransactions, killStuckProcess } from './setup'; +import { Keypair, PublicKey } from '@solana/web3.js'; +import { createAndMintDefaultAsset, createDefaultAsset } from './utils/digital-asset-manager'; +import { UpdateTestData } from './utils/update-test-data'; +import { encode } from '@msgpack/msgpack'; +import { PROGRAM_ID as TOKEN_AUTH_RULES_ID } from '@metaplex-foundation/mpl-token-auth-rules'; +import { spokSamePubkey } from './utils'; +import { findTokenRecordPda } from './utils/programmable'; + +killStuckProcess(); + +test('Update: NonFungible asset', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const daManager = await createDefaultAsset(t, connection, API, handler, payer); + const { mint, metadata, masterEdition } = daManager; + const assetData = await daManager.getAssetData(connection); + + const authority = payer; + + // Change some values and run update. + const data: Data = { + name: 'DigitalAsset2', + symbol: 'DA2', + uri: 'uri2', + sellerFeeBasisPoints: 0, + creators: assetData.creators, + }; + + const authorizationData = daManager.emptyAuthorizationData(); + + const updateData = new UpdateTestData(); + updateData.data = data; + updateData.authorizationData = authorizationData; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx.assertSuccess(t); +}); + +test('Update: Fungible Token', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const daManager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.Fungible, + null, + 10, + ); + const { mint, metadata, masterEdition } = daManager; + + const assetData = await daManager.getAssetData(connection); + + const authority = payer; + + // Change some values and run update. + const data: Data = { + name: 'DigitalAsset2', + symbol: 'DA2', + uri: 'uri2', + sellerFeeBasisPoints: 0, + creators: assetData.creators, + }; + + const authorizationData = daManager.emptyAuthorizationData(); + + const updateData = new UpdateTestData(); + updateData.data = data; + updateData.authorizationData = authorizationData; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx.assertSuccess(t); +}); + +test('Update: Fungible Asset', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const daManager = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.FungibleAsset, + null, + 10, + ); + const { mint, metadata, masterEdition } = daManager; + + const assetData = await daManager.getAssetData(connection); + + const authority = payer; + + // Change some values and run update. + const data: Data = { + name: 'DigitalAsset2', + symbol: 'DA2', + uri: 'uri2', + sellerFeeBasisPoints: 0, + creators: assetData.creators, + }; + + const authorizationData = daManager.emptyAuthorizationData(); + + const updateData = new UpdateTestData(); + updateData.data = data; + updateData.authorizationData = authorizationData; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx.assertSuccess(t); +}); + +test('Update: Cannot Flip IsMutable to True', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const daManager = await createDefaultAsset(t, connection, API, handler, payer); + const { mint, metadata, masterEdition } = daManager; + + const authority = payer; + + // Flip isMutable to false + const updateData = new UpdateTestData(); + updateData.isMutable = false; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx.assertSuccess(t); + + const updatedMetadata = await Metadata.fromAccountAddress(connection, metadata); + + spok(t, updatedMetadata, { + isMutable: false, + }); + + // Try to flip isMutable to true + updateData.isMutable = true; + + const { tx: updateTx2 } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx2.assertError(t, /Is Mutable can only be flipped to false/i); +}); + +test('Update: Cannot Flip PrimarySaleHappened to False', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const daManager = await createDefaultAsset(t, connection, API, handler, payer); + const { mint, metadata, masterEdition } = daManager; + + const authority = payer; + + // Flip to true + const updateData = new UpdateTestData(); + updateData.primarySaleHappened = true; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx.assertSuccess(t); + + const updatedMetadata = await Metadata.fromAccountAddress(connection, metadata); + + spok(t, updatedMetadata, { + primarySaleHappened: true, + }); + + // Try to flip false -- this should fail + updateData.primarySaleHappened = false; + + const { tx: updateTx2 } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx2.assertError(t, /Primary sale can only be flipped to true/i); +}); + +test('Update: Set New Update Authority', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const daManager = await createDefaultAsset(t, connection, API, handler, payer); + const { mint, metadata, masterEdition } = daManager; + + const authority = payer; + const newUpdateAuthority = new Keypair().publicKey; + + // Flip to true + const updateData = new UpdateTestData(); + updateData.newUpdateAuthority = newUpdateAuthority; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx.assertSuccess(t); + + const updatedMetadata = await Metadata.fromAccountAddress(connection, metadata); + + spok(t, updatedMetadata, { + updateAuthority: newUpdateAuthority, + }); +}); + +test('Update: Cannot Update Immutable Data', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const daManager = await createDefaultAsset(t, connection, API, handler, payer); + const { mint, metadata, masterEdition } = daManager; + + const authority = payer; + + // Flip isMutable to false + const updateData = new UpdateTestData(); + updateData.isMutable = false; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx.assertSuccess(t); + + // Try to write some data. + updateData.data = { + name: 'new-name', + symbol: 'new-symbol', + uri: 'new-uri', + sellerFeeBasisPoints: 500, + creators: null, + }; + + const { tx: updateTx2 } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx2.assertError(t, /Data is immutable/i); +}); + +test('Update: Name Cannot Exceed 32 Bytes', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const daManager = await createDefaultAsset(t, connection, API, handler, payer); + const { mint, metadata, masterEdition } = daManager; + + const authority = payer; + + const updateData = new UpdateTestData(); + updateData.data = { + name: ''.padEnd(33, 'a'), + symbol: 'new-symbol', + uri: 'new-uri', + sellerFeeBasisPoints: 100, + creators: null, + }; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx.assertError(t, /Name too long/i); +}); + +test('Update: Symbol Cannot Exceed 10 Bytes', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const daManager = await createDefaultAsset(t, connection, API, handler, payer); + const { mint, metadata, masterEdition } = daManager; + + const authority = payer; + + const updateData = new UpdateTestData(); + updateData.data = { + name: 'new-name', + symbol: ''.padEnd(11, 'a'), + uri: 'new-uri', + sellerFeeBasisPoints: 100, + creators: null, + }; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx.assertError(t, /Symbol too long/i); +}); + +test('Update: URI Cannot Exceed 200 Bytes', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const daManager = await createDefaultAsset(t, connection, API, handler, payer); + const { mint, metadata, masterEdition } = daManager; + + const authority = payer; + + const updateData = new UpdateTestData(); + updateData.data = { + name: 'new-name', + symbol: 'new-symbol', + uri: ''.padEnd(201, 'a'), + sellerFeeBasisPoints: 100, + creators: null, + }; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx.assertError(t, /Uri too long/i); +}); + +test('Update: SellerFeeBasisPoints Cannot Exceed 10_000', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const daManager = await createDefaultAsset(t, connection, API, handler, payer); + const { mint, metadata, masterEdition } = daManager; + + const authority = payer; + + const updateData = new UpdateTestData(); + updateData.data = { + name: 'new-name', + symbol: 'new-symbol', + uri: 'new-uri', + sellerFeeBasisPoints: 10_005, + creators: null, + }; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx.assertError(t, /Basis points cannot be more than 10000/i); +}); + +test('Update: Creators Array Cannot Exceed Five Items', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const daManager = await createDefaultAsset(t, connection, API, handler, payer); + const { mint, metadata, masterEdition } = daManager; + + const authority = payer; + + const creators = []; + + for (let i = 0; i < 6; i++) { + creators.push({ + address: new Keypair().publicKey, + verified: false, + share: i < 5 ? 20 : 0, // Don't exceed 100% share total. + }); + } + + const updateData = new UpdateTestData(); + updateData.data = { + name: 'new-name', + symbol: 'new-symbol', + uri: 'new-uri', + sellerFeeBasisPoints: 100, + creators, + }; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx.assertError(t, /Creators list too long/i); +}); + +test('Update: No Duplicate Creator Addresses', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const daManager = await createDefaultAsset(t, connection, API, handler, payer); + const { mint, metadata, masterEdition } = daManager; + + const authority = payer; + + const creators = []; + + for (let i = 0; i < 2; i++) { + creators.push({ + address: payer.publicKey, + verified: true, + share: 50, + }); + } + + const updateData = new UpdateTestData(); + updateData.data = { + name: 'new-name', + symbol: 'new-symbol', + uri: 'new-uri', + sellerFeeBasisPoints: 100, + creators, + }; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx.assertError(t, /No duplicate creator addresses/i); +}); + +test('Update: Creator Shares Must Equal 100', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const daManager = await createDefaultAsset(t, connection, API, handler, payer); + const { mint, metadata, masterEdition } = daManager; + + const authority = payer; + + const creators = []; + + creators.push({ + address: payer.publicKey, + verified: true, + share: 101, + }); + + const updateData = new UpdateTestData(); + updateData.data = { + name: 'new-name', + symbol: 'new-symbol', + uri: 'new-uri', + sellerFeeBasisPoints: 100, + creators, + }; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx.assertError(t, /Share total must equal 100 for creator array/i); +}); + +test('Update: Cannot Unverify Another Creator', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const daManager = await createDefaultAsset(t, connection, API, handler, payer); + const { mint, metadata, masterEdition } = daManager; + + const authority = payer; + + // Create a new creator with a different keypair. + const creatorKey = new Keypair(); + await amman.airdrop(connection, creatorKey.publicKey, 1); + + // Add new creator to metadata. + const updateData = new UpdateTestData(); + updateData.data = { + name: 'new-name', + symbol: 'new-symbol', + uri: 'new-uri', + sellerFeeBasisPoints: 100, + creators: [ + { + address: payer.publicKey, + share: 100, + verified: false, + }, + { + address: creatorKey.publicKey, + share: 0, + verified: false, + }, + ], + }; + + // Update metadata with new creator. + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx.assertSuccess(t); + + // Sign metadata with new creator. + const { tx: signMetadataTx } = await API.signMetadata(t, creatorKey, metadata, handler); + await signMetadataTx.assertSuccess(t); + + const updatedMetadata = await Metadata.fromAccountAddress(connection, metadata); + + t.equal(updatedMetadata.data.creators[1].verified, true); + + // Have the original keypair try to unverify it. + const newCreators = []; + newCreators.push({ + address: creatorKey.publicKey, + verified: false, + share: 100, + }); + + const updateData2 = new UpdateTestData(); + updateData2.data = { + name: 'new-name', + symbol: 'new-symbol', + uri: 'new-uri', + sellerFeeBasisPoints: 100, + creators: newCreators, + }; + + const { tx: updateTx2 } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + + await updateTx2.assertError(t, /cannot unilaterally unverify another creator/i); +}); + +test('Update: Cannot Verify Another Creator', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const daManager = await createDefaultAsset(t, connection, API, handler, payer); + const { mint, metadata, masterEdition } = daManager; + + const authority = payer; + + const creatorKey = new Keypair(); + await amman.airdrop(connection, creatorKey.publicKey, 1); + + // Start with an unverified creator + const creators = []; + creators.push({ + address: creatorKey.publicKey, + verified: false, + share: 100, + }); + + const updateData = new UpdateTestData(); + updateData.data = { + name: 'new-name', + symbol: 'new-symbol', + uri: 'new-uri', + sellerFeeBasisPoints: 100, + creators, + }; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx.assertSuccess(t); + + const updatedMetadata = await Metadata.fromAccountAddress(connection, metadata); + + spok(t, updatedMetadata.data, { + creators: updateData.data.creators, + }); + + // Have a different keypair try to verify it. + const newCreators = []; + newCreators.push({ + address: creatorKey.publicKey, + verified: true, + share: 100, + }); + + const updateData2 = new UpdateTestData(); + updateData2.data = { + name: 'new-name', + symbol: 'new-symbol', + uri: 'new-uri', + sellerFeeBasisPoints: 100, + creators: newCreators, + }; + + const { tx: updateTx2 } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData2, + null, + masterEdition, + ); + + await updateTx2.assertError(t, /cannot unilaterally verify another creator, they must sign/i); +}); + +test('Update: Update Unverified Collection Key', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const name = 'DigitalAsset'; + const symbol = 'DA'; + const uri = 'uri'; + + const authority = payer; + + const collectionParent = new Keypair(); + const newCollectionParent = new Keypair(); + + // Create the initial asset and ensure it was created successfully + const assetData: AssetData = { + name, + symbol, + uri, + sellerFeeBasisPoints: 0, + creators: [ + { + address: payer.publicKey, + share: 100, + verified: false, + }, + ], + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.NonFungible, + collection: { key: collectionParent.publicKey, verified: false }, + uses: null, + collectionDetails: null, + ruleSet: null, + }; + + const { + tx: createTx, + mint, + metadata, + masterEdition, + } = await API.create(t, payer, assetData, 0, 0, handler); + await createTx.assertSuccess(t); + + const createdMetadata = await Metadata.fromAccountAddress(connection, metadata); + spok(t, createdMetadata, { + collection: { + key: collectionParent.publicKey, + verified: false, + }, + }); + + const updateData = new UpdateTestData(); + updateData.collection = { + __kind: 'Set', + fields: [ + { + key: newCollectionParent.publicKey, + verified: false, + }, + ], + }; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx.assertSuccess(t); + + const updatedMetadata = await Metadata.fromAccountAddress(connection, metadata); + + spok(t, updatedMetadata.collection, { + verified: false, + key: spokSamePubkey(newCollectionParent.publicKey), + }); +}); + +test('Update: Fail to Verify an Unverified Collection', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const name = 'DigitalAsset'; + const symbol = 'DA'; + const uri = 'uri'; + + const authority = payer; + + const collectionParent = new Keypair(); + + // Create the initial asset and ensure it was created successfully + const assetData: AssetData = { + name, + symbol, + uri, + sellerFeeBasisPoints: 0, + creators: [ + { + address: payer.publicKey, + share: 100, + verified: false, + }, + ], + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.NonFungible, + collection: { key: collectionParent.publicKey, verified: false }, + uses: null, + collectionDetails: null, + ruleSet: null, + }; + + const { + tx: createTx, + mint, + metadata, + masterEdition, + } = await API.create(t, payer, assetData, 0, 0, handler); + await createTx.assertSuccess(t); + + const createdMetadata = await Metadata.fromAccountAddress(connection, metadata); + spok(t, createdMetadata, { + collection: { + key: collectionParent.publicKey, + verified: false, + }, + }); + + const updateData = new UpdateTestData(); + updateData.collection = { + __kind: 'Set', + fields: [ + { + key: collectionParent.publicKey, + verified: true, + }, + ], + }; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx.assertError(t, /Collection cannot be verified in this instruction/); +}); + +test('Update: Fail to Update a Verified Collection', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const name = 'DigitalAsset'; + const symbol = 'DA'; + const uri = 'uri'; + + // Create parent NFT. + const daManager = await createDefaultAsset(t, connection, API, handler, payer); + const { + mint: collectionMint, + metadata: collectionMetadata, + masterEdition: collectionMasterEdition, + } = daManager; + + const authority = payer; + + const newCollectionParent = new Keypair(); + + // Create the initial asset and ensure it was created successfully + const assetData: AssetData = { + name, + symbol, + uri, + sellerFeeBasisPoints: 0, + creators: [ + { + address: payer.publicKey, + share: 100, + verified: false, + }, + ], + primarySaleHappened: false, + isMutable: true, + tokenStandard: TokenStandard.NonFungible, + collection: { key: collectionMint, verified: false }, + uses: null, + collectionDetails: null, + ruleSet: null, + }; + + const { + tx: createTx, + mint, + metadata, + masterEdition, + } = await API.create(t, payer, assetData, 0, 0, handler); + await createTx.assertSuccess(t); + + const createdMetadata = await Metadata.fromAccountAddress(connection, metadata); + spok(t, createdMetadata, { + collection: { + key: collectionMint, + verified: false, + }, + }); + + const { tx: verifyCollectionTx } = await API.verifyCollection( + t, + payer, + metadata, + collectionMint, + collectionMetadata, + collectionMasterEdition, + payer, + handler, + ); + await verifyCollectionTx.assertSuccess(t); + + const updateData = new UpdateTestData(); + updateData.collection = { + __kind: 'Set', + fields: [ + { + key: newCollectionParent.publicKey, + verified: true, + }, + ], + }; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx.assertError(t, /Collection cannot be verified in this instruction/); +}); + +test('Update: Update pNFT Config', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const { mint, metadata, masterEdition } = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + null, + 1, + ); + + const authority = payer; + const dummyRuleSet = Keypair.generate().publicKey; + + const updateData = new UpdateTestData(); + updateData.ruleSet = { + __kind: 'Set', + fields: [dummyRuleSet], + }; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx.assertSuccess(t); + + const updatedMetadata = await Metadata.fromAccountAddress(connection, metadata); + + spok(t, updatedMetadata.programmableConfig, { + ruleSet: dummyRuleSet, + }); +}); + +test('Update: Fail to update rule set on NFT', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const authority = payer; + const dummyRuleSet = Keypair.generate().publicKey; + + const { mint, metadata, masterEdition } = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.NonFungible, + null, + 1, + ); + + const updateData = new UpdateTestData(); + updateData.ruleSet = { + __kind: 'Set', + fields: [dummyRuleSet], + }; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx.assertError(t, /Invalid token standard/); +}); + +test('Update: Update existing pNFT rule set config to None', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const authority = payer; + + // We need a real ruleset here to pass the mint checks. + // Set up our rule set + const ruleSetName = 'update_test'; + const ruleSet = { + libVersion: 1, + ruleSetName: ruleSetName, + owner: Array.from(authority.publicKey.toBytes()), + operations: { + 'Transfer:Owner': { + PubkeyMatch: { + pubkey: Array.from(authority.publicKey.toBytes()), + field: 'Destination', + }, + }, + }, + }; + const serializedRuleSet = encode(ruleSet); + + // Find the ruleset PDA + const [ruleSetPda] = PublicKey.findProgramAddressSync( + [Buffer.from('rule_set'), payer.publicKey.toBuffer(), Buffer.from(ruleSetName)], + TOKEN_AUTH_RULES_ID, + ); + + const { tx: createRuleSetTx } = await API.createRuleSet( + t, + payer, + ruleSetPda, + serializedRuleSet, + handler, + ); + await createRuleSetTx.assertSuccess(t); + + const { mint, metadata, masterEdition } = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + ruleSetPda, + 1, + ); + + const updateData = new UpdateTestData(); + updateData.ruleSet = { + __kind: 'Clear', + }; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + null, + ruleSetPda, + ); + await updateTx.assertSuccess(t); + + const updatedMetadata = await Metadata.fromAccountAddress(connection, metadata); + + t.equal(updatedMetadata.programmableConfig, null); +}); + +test('Update: Invalid Update Authority Fails', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const daManager = await createDefaultAsset(t, connection, API, handler, payer); + const { mint, metadata, masterEdition } = daManager; + + const invalidUpdateAuthority = new Keypair(); + + // Flip to true + const updateData = new UpdateTestData(); + updateData.data = { + name: 'fake name', + symbol: 'fake', + uri: 'fake uri', + sellerFeeBasisPoints: 500, + creators: null, + }; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + invalidUpdateAuthority, + updateData, + null, + masterEdition, + ); + await updateTx.assertError(t, /Update Authority given does not match/); +}); + +test('Update: Delegate Authority Type Not Supported', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const daManager = await createDefaultAsset(t, connection, API, handler, payer); + const { mint, metadata, masterEdition } = daManager; + + // creates a delegate + + const [, delegate] = await API.getKeypair('Delegate'); + // delegate PDA + const [delegateRecord] = PublicKey.findProgramAddressSync( + [ + Buffer.from('metadata'), + PROGRAM_ID.toBuffer(), + daManager.mint.toBuffer(), + Buffer.from('update_delegate'), + payer.publicKey.toBuffer(), + delegate.publicKey.toBuffer(), + ], + PROGRAM_ID, + ); + amman.addr.addLabel('Delegate Record', delegateRecord); + + const args: DelegateArgs = { + __kind: 'UpdateV1', + authorizationData: null, + }; + + const { tx: delegateTx } = await API.delegate( + delegate.publicKey, + daManager.mint, + daManager.metadata, + payer.publicKey, + payer, + args, + handler, + delegateRecord, + daManager.masterEdition, + ); + await delegateTx.assertSuccess(t); + + const assetData = await daManager.getAssetData(connection); + const authority = delegate; + + // Change some values and run update. + const data: Data = { + name: 'DigitalAsset2', + symbol: 'DA2', + uri: 'uri2', + sellerFeeBasisPoints: 0, + creators: assetData.creators, + }; + const authorizationData = daManager.emptyAuthorizationData(); + + const updateData = new UpdateTestData(); + updateData.data = data; + updateData.authorizationData = authorizationData; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + delegateRecord, + masterEdition, + ); + + updateTx.then((x) => + x.assertLogs(t, [/Authority type: Delegate/i, /Feature not supported currently/i], { + txLabel: 'tx: Update', + }), + ); + await updateTx.assertError(t); +}); + +test('Update: Holder Authority Type Not Supported', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const daManager = await createDefaultAsset(t, connection, API, handler, payer); + const { mint, metadata, masterEdition } = daManager; + + // initialize a token account + + const [, holder] = await amman.genLabeledKeypair('Holder'); + + const { tx: tokenTx, token } = await API.createTokenAccount( + mint, + payer, + connection, + handler, + holder.publicKey, + ); + await tokenTx.assertSuccess(t); + + // mint 1 asset + + const amount = 1; + + const { tx: mintTx } = await API.mint( + t, + connection, + payer, + mint, + metadata, + masterEdition, + daManager.emptyAuthorizationData(), + amount, + handler, + token, + ); + await mintTx.assertSuccess(t); + + const assetData = await daManager.getAssetData(connection); + + // Change some values and run update. + const data: Data = { + name: 'DigitalAsset2', + symbol: 'DA2', + uri: 'uri2', + sellerFeeBasisPoints: 0, + creators: assetData.creators, + }; + + const authorizationData = daManager.emptyAuthorizationData(); + + const updateData = new UpdateTestData(); + updateData.data = data; + updateData.authorizationData = authorizationData; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + holder, + updateData, + null, + masterEdition, + token, + ); + + updateTx.then((x) => + x.assertLogs(t, [/Authority type: Holder/i, /Feature not supported currently/i], { + txLabel: 'tx: Update', + }), + ); + await updateTx.assertError(t); +}); + +test('Update: Update pNFT Config with locked token', async (t) => { + const API = new InitTransactions(); + const { fstTxHandler: handler, payerPair: payer, connection } = await API.payer(); + + const { mint, metadata, masterEdition, token } = await createAndMintDefaultAsset( + t, + connection, + API, + handler, + payer, + TokenStandard.ProgrammableNonFungible, + null, + 1, + ); + + // token record PDA + const tokenRecord = findTokenRecordPda(mint, token); + amman.addr.addLabel('Token Record', tokenRecord); + + const pda = await TokenRecord.fromAccountAddress(connection, tokenRecord); + + spok(t, pda, { + state: TokenState.Unlocked /* asset should be unlocked */, + }); + + // creates a delegate + + const [, delegate] = await API.getKeypair('Delegate'); + + const args: DelegateArgs = { + __kind: 'UtilityV1', + amount: 1, + authorizationData: null, + }; + + const { tx: delegateTx } = await API.delegate( + delegate.publicKey, + mint, + metadata, + payer.publicKey, + payer, + args, + handler, + null, + masterEdition, + token, + tokenRecord, + ); + + await delegateTx.assertSuccess(t); + + // lock asset with delegate + + const { tx: lockTx } = await API.lock( + delegate, + mint, + metadata, + token, + payer, + handler, + tokenRecord, + null, + masterEdition, + ); + await lockTx.assertSuccess(t); + + // updates the metadata + + const authority = payer; + const dummyRuleSet = Keypair.generate().publicKey; + + const updateData = new UpdateTestData(); + updateData.ruleSet = { + __kind: 'Set', + fields: [dummyRuleSet], + }; + + const { tx: updateTx } = await API.update( + t, + handler, + mint, + metadata, + authority, + updateData, + null, + masterEdition, + ); + await updateTx.assertSuccess(t); + + const updatedMetadata = await Metadata.fromAccountAddress(connection, metadata); + + spok(t, updatedMetadata.programmableConfig, { + ruleSet: dummyRuleSet, + }); +}); diff --git a/token-metadata/js/test/utils/digital-asset-manager.ts b/token-metadata/js/test/utils/digital-asset-manager.ts new file mode 100644 index 0000000000..05b2eb28c1 --- /dev/null +++ b/token-metadata/js/test/utils/digital-asset-manager.ts @@ -0,0 +1,133 @@ +import { Connection, PublicKey } from '@solana/web3.js'; +import { AssetData, Metadata, TokenStandard, AuthorizationData } from '../../src/generated'; +import { InitTransactions } from '../setup'; +import test from 'tape'; +import { PayerTransactionHandler } from '@metaplex-foundation/amman-client'; +import { Keypair } from '@solana/web3.js'; + +export class DigitalAssetManager { + mint: PublicKey; + metadata: PublicKey; + masterEdition: PublicKey; + token?: PublicKey; + + constructor(mint: PublicKey, metadata: PublicKey, masterEdition: PublicKey) { + this.mint = mint; + this.metadata = metadata; + this.masterEdition = masterEdition; + } + + emptyAuthorizationData(): AuthorizationData { + return { + payload: { + map: new Map(), + }, + }; + } + + async getAssetData(connection: Connection): Promise { + const md = await Metadata.fromAccountAddress(connection, this.metadata); + + return { + name: md.data.name, + symbol: md.data.symbol, + uri: md.data.uri, + sellerFeeBasisPoints: md.data.sellerFeeBasisPoints, + creators: md.data.creators, + primarySaleHappened: md.primarySaleHappened, + isMutable: md.isMutable, + tokenStandard: md.tokenStandard, + collection: md.collection, + uses: md.uses, + collectionDetails: md.collectionDetails, + ruleSet: md.programmableConfig ? md.programmableConfig.ruleSet : null, + }; + } +} + +export async function createDefaultAsset( + t: test.Test, + connection: Connection, + API: InitTransactions, + handler: PayerTransactionHandler, + payer: Keypair, + tokenStandard: TokenStandard = TokenStandard.NonFungible, + ruleSet: PublicKey | null = null, +): Promise { + const name = 'DigitalAsset'; + const symbol = 'DA'; + const uri = 'uri'; + + // Create the initial asset and ensure it was created successfully + const assetData: AssetData = { + name, + symbol, + uri, + sellerFeeBasisPoints: 0, + creators: [ + { + address: payer.publicKey, + share: 100, + verified: false, + }, + ], + primarySaleHappened: false, + isMutable: true, + tokenStandard, + collection: null, + uses: null, + collectionDetails: null, + ruleSet, + }; + + const { + tx: createTx, + mint, + metadata, + masterEdition, + } = await API.create(t, payer, assetData, 0, 0, handler); + await createTx.assertSuccess(t); + + const daManager = new DigitalAssetManager(mint, metadata, masterEdition); + + return daManager; +} + +export async function createAndMintDefaultAsset( + t: test.Test, + connection: Connection, + API: InitTransactions, + handler: PayerTransactionHandler, + payer: Keypair, + tokenStandard: TokenStandard = TokenStandard.NonFungible, + ruleSet: PublicKey | null = null, + amount = 1, +): Promise { + const daManager = await createDefaultAsset( + t, + connection, + API, + handler, + payer, + tokenStandard, + ruleSet, + ); + const { mint, metadata, masterEdition } = daManager; + + const { tx: mintTx, token } = await API.mint( + t, + connection, + payer, + mint, + metadata, + masterEdition, + daManager.emptyAuthorizationData(), + amount, + handler, + ); + await mintTx.assertSuccess(t); + + daManager.token = token; + + return daManager; +} diff --git a/token-metadata/js/test/utils/errors.ts b/token-metadata/js/test/utils/errors.ts new file mode 100644 index 0000000000..3913f73cbb --- /dev/null +++ b/token-metadata/js/test/utils/errors.ts @@ -0,0 +1,4 @@ +import { initCusper } from '@metaplex-foundation/cusper'; +import { errorFromCode } from '../../src/generated'; + +export const cusper = initCusper(errorFromCode); diff --git a/token-metadata/js/test/utils/index.ts b/token-metadata/js/test/utils/index.ts new file mode 100644 index 0000000000..46ca7ff02f --- /dev/null +++ b/token-metadata/js/test/utils/index.ts @@ -0,0 +1,61 @@ +import { bignum, COption } from '@metaplex-foundation/beet'; +import { PublicKey } from '@solana/web3.js'; +import BN from 'bn.js'; +import { Assert, Specification, Specifications } from 'spok'; +import { Test } from 'tape'; + +export * from './errors'; + +export const SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID: PublicKey = new PublicKey( + 'ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL', +); + +export function spokSameBignum(a?: BN | bignum): Specification { + const same = (b?: BN | bignum) => { + if (a == null && b == null) { + return true; + } + if (a == null) { + return false; + } + + return b != null && new BN(a).eq(new BN(b)); + }; + + same.$spec = `spokSameBignum(${a})`; + same.$description = `${a} equal`; + return same; +} + +export function spokSameBigint(a?: BN | bigint): Specification { + const same = (b?: BN | bigint) => { + if (a == null && b == null) { + return true; + } + if (a == null) { + return false; + } + + return b != null && new BN(a.toString()).eq(new BN(b.toString())); + }; + + same.$spec = `spokSameBigint(${a})`; + same.$description = `${a} equal`; + return same; +} + +export function spokSamePubkey(a: PublicKey | COption): Specifications { + const same = (b: PublicKey | null | undefined) => b != null && !!a?.equals(b); + + same.$spec = `spokSamePubkey(${a?.toBase58()})`; + same.$description = `${a?.toBase58()} equal`; + return same; +} + +export function assertIsNotNull(t: Test, x: T | null | undefined): asserts x is T { + t.ok(x, 'should be non null'); +} + +export function assertSamePubkey(t: Assert, a: PublicKey | COption, b: PublicKey) { + t.equal(a?.toBase58(), b.toBase58(), 'pubkeys are same'); +} diff --git a/token-metadata/js/test/utils/programmable.ts b/token-metadata/js/test/utils/programmable.ts new file mode 100644 index 0000000000..619c66e7d0 --- /dev/null +++ b/token-metadata/js/test/utils/programmable.ts @@ -0,0 +1,33 @@ +// @ts-ignore +import { encode } from '@msgpack/msgpack'; +import { PublicKey } from '@solana/web3.js'; +import { PROGRAM_ID } from '../../src/generated'; + +export function createPassRuleSet( + ruleSetName: string, + owner: PublicKey, + operation: string, +): Uint8Array { + const operations = {}; + operations[operation] = 'Pass'; + + const ruleSet = { + ruleSetName, + owner: Array.from(owner.toBytes()), + operations, + }; + return encode(ruleSet); +} + +export function findTokenRecordPda(mint: PublicKey, token: PublicKey): PublicKey { + return PublicKey.findProgramAddressSync( + [ + Buffer.from('metadata'), + PROGRAM_ID.toBuffer(), + mint.toBuffer(), + Buffer.from('token_record'), + token.toBuffer(), + ], + PROGRAM_ID, + )[0]; +} diff --git a/token-metadata/js/test/utils/update-test-data.ts b/token-metadata/js/test/utils/update-test-data.ts new file mode 100644 index 0000000000..5f75b155d7 --- /dev/null +++ b/token-metadata/js/test/utils/update-test-data.ts @@ -0,0 +1,33 @@ +import { PublicKey } from '@solana/web3.js'; +import { + Data, + AuthorizationData, + CollectionToggle, + UsesToggle, + CollectionDetailsToggle, + RuleSetToggle, +} from '../../src/generated'; + +export class UpdateTestData { + newUpdateAuthority: PublicKey; + data: Data; + primarySaleHappened: boolean; + isMutable: boolean; + collection: CollectionToggle; + uses: UsesToggle; + collectionDetails: CollectionDetailsToggle; + ruleSet: RuleSetToggle; + authorizationData: AuthorizationData; + + constructor() { + this.newUpdateAuthority = null; + this.data = null; + this.primarySaleHappened = null; + this.isMutable = null; + this.collection = { __kind: 'None' }; + this.uses = { __kind: 'None' }; + this.collectionDetails = { __kind: 'None' }; + this.authorizationData = null; + this.ruleSet = { __kind: 'None' }; + } +} diff --git a/token-metadata/js/tsconfig.build.json b/token-metadata/js/tsconfig.build.json index f229ece187..a737e55f0c 100644 --- a/token-metadata/js/tsconfig.build.json +++ b/token-metadata/js/tsconfig.build.json @@ -5,6 +5,6 @@ "baseUrl": ".", "strict": false }, - "include": ["./src"], + "include": ["./src", "./test"], "exclude": ["node_modules", "dist", "build", "lib"] } diff --git a/token-metadata/js/yarn.lock b/token-metadata/js/yarn.lock index d3b7b1a4ca..fdc1019053 100644 --- a/token-metadata/js/yarn.lock +++ b/token-metadata/js/yarn.lock @@ -9,6 +9,16 @@ dependencies: regenerator-runtime "^0.13.10" +"@esbuild/android-arm@0.15.12": + version "0.15.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.15.12.tgz#e548b10a5e55b9e10537a049ebf0bc72c453b769" + integrity sha512-IC7TqIqiyE0MmvAhWkl/8AEzpOtbhRNDo7aph47We1NbE5w2bt/Q+giAhe0YYeVpYnIhGMcuZY92qDK6dQauvA== + +"@esbuild/linux-loong64@0.15.12": + version "0.15.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.12.tgz#475b33a2631a3d8ca8aa95ee127f9a61d95bf9c1" + integrity sha512-tZEowDjvU7O7I04GYvWQOS4yyP9E/7YlsB0jjw1Ycukgr2ycEzKyIk5tms5WnLBymaewc6VmRKnn5IJWgK4eFw== + "@eslint/eslintrc@^1.3.3": version "1.3.3" resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95" @@ -24,6 +34,18 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" +"@hapi/hoek@^9.0.0": + version "9.3.0" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" + integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== + +"@hapi/topo@^5.0.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" + integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== + dependencies: + "@hapi/hoek" "^9.0.0" + "@humanwhocodes/config-array@^0.11.6": version "0.11.7" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.7.tgz#38aec044c6c828f6ed51d5d7ae3d9b9faf6dbb0f" @@ -43,6 +65,44 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@metaplex-foundation/amman-client@^0.2.0", "@metaplex-foundation/amman-client@^0.2.2": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/amman-client/-/amman-client-0.2.4.tgz#bcf9cb2b8fa0cdaec3b17a68e638c8cf0531d08a" + integrity sha512-AB2zKDrGyJtQqUmOswsZCflIuldYmsPvL/zIU/66JOD9HDo8yyz5ITCrs+/0QoW+CIJkotC5JEhuYuBnqLhmNw== + dependencies: + "@metaplex-foundation/cusper" "^0.0.2" + "@solana/spl-token-registry" "^0.2.2405" + "@solana/web3.js" "^1.66.2" + bn.js "^5.2.1" + debug "^4.3.3" + js-sha3 "^0.8.0" + socket.io-client "^4.4.1" + tweetnacl "^1.0.3" + +"@metaplex-foundation/amman@^0.12.0": + version "0.12.1" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/amman/-/amman-0.12.1.tgz#8d135fa4e4ac4667133762eca02e6cc221e7f656" + integrity sha512-F3cdHr11ByLGMCrSBRvRCf5uIlhLE+5sWaHT2ZzcE6zVyDta3gs/A12ZBzYZS8ugNETNpJySfB42kMp1VZwbUA== + dependencies: + "@metaplex-foundation/amman-client" "^0.2.0" + "@solana/spl-token" "^0.2.0" + "@solana/web3.js" "^1.63.1" + ansi-colors "^4.1.1" + bn.js "^5.2.0" + buffer-hexdump "^1.0.0" + date-fns "^2.28.0" + debug "^4.3.3" + deep-diff "^1.0.2" + diff "^5.1.0" + numeral "^2.0.6" + port-pid "^0.0.7" + socket.io "^4.4.1" + text-table "^0.2.0" + timeago.js "^4.0.2" + ts-essentials "^9.3.0" + wait-on "^6.0.0" + yargs "^17.3.0" + "@metaplex-foundation/beet-solana@^0.3.1": version "0.3.1" resolved "https://registry.yarnpkg.com/@metaplex-foundation/beet-solana/-/beet-solana-0.3.1.tgz#4b37cda5c7f32ffd2bdd8b3164edc05c6463ab35" @@ -77,6 +137,17 @@ resolved "https://registry.yarnpkg.com/@metaplex-foundation/cusper/-/cusper-0.0.2.tgz#dc2032a452d6c269e25f016aa4dd63600e2af975" integrity sha512-S9RulC2fFCFOQraz61bij+5YCHhSO9llJegK8c8Y6731fSi6snUSQJdCUqYS8AIgR0TKbQvdvgSyIIdbDFZbBA== +"@metaplex-foundation/mpl-token-auth-rules@^0.2.7": + version "0.2.7" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/mpl-token-auth-rules/-/mpl-token-auth-rules-0.2.7.tgz#25a25d52b5b6bce3be820697c15e2d5cae0f4c17" + integrity sha512-/MIPGAByLixSNFVBMAH8ElJ1BU6tZcBwnLAxu2SqsuuDHIqjLTd+/wMsGAmq7W41d8OWhAW7ymZMa71aK1Wx/g== + dependencies: + "@metaplex-foundation/beet" "^0.7.1" + "@metaplex-foundation/beet-solana" "^0.4.0" + "@metaplex-foundation/cusper" "^0.0.2" + "@solana/spl-token" "^0.3.6" + "@solana/web3.js" "^1.66.2" + "@metaplex-foundation/rustbin@^0.3.0": version "0.3.1" resolved "https://registry.yarnpkg.com/@metaplex-foundation/rustbin/-/rustbin-0.3.1.tgz#bbcd61e8699b73c0b062728c6f5e8d52e8145042" @@ -87,10 +158,10 @@ text-table "^0.2.0" toml "^3.0.0" -"@metaplex-foundation/solita@^0.19.1": - version "0.19.1" - resolved "https://registry.yarnpkg.com/@metaplex-foundation/solita/-/solita-0.19.1.tgz#29becdc0fbef79c971558f1405da66fb1b5e5c1e" - integrity sha512-5kjhWDT8Qt/B6sOWbtRl5tzc1y98K4Q+fPNH+vhIhC5NzBW2BD5V+5mMU3W7agl5viT8UjKJ1HquqdtQYjj3MA== +"@metaplex-foundation/solita@^0.19.3": + version "0.19.3" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/solita/-/solita-0.19.3.tgz#dbb7659f6af513cdae93201ab079f28b162b0199" + integrity sha512-VnCqW05opw8UueP9IsH3ax6Dk7J+8QuqTs/ceSeMhJquOpoLv3M4crrzFqX+GXr5GH0j/Fk32UlRD7iEp6sEnQ== dependencies: "@metaplex-foundation/beet" "^0.7.1" "@metaplex-foundation/beet-solana" "^0.3.1" @@ -104,6 +175,11 @@ snake-case "^3.0.4" spok "^1.4.3" +"@msgpack/msgpack@^2.8.0": + version "2.8.0" + resolved "https://registry.yarnpkg.com/@msgpack/msgpack/-/msgpack-2.8.0.tgz#4210deb771ee3912964f14a15ddfb5ff877e70b9" + integrity sha512-h9u4u/jiIRKbq25PM+zymTyW6bhTzELvOoUd+AvYriWOAKpLGnIamaET3pnHYoI5iYphAHBI4ayx0MehR+VVPQ== + "@noble/ed25519@^1.7.0": version "1.7.1" resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.1.tgz#6899660f6fbb97798a6fbd227227c4589a454724" @@ -140,6 +216,28 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@sideway/address@^4.1.3": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0" + integrity sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@sideway/formula@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" + integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg== + +"@sideway/pinpoint@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== + +"@socket.io/component-emitter@~3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" + integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== + "@solana/buffer-layout-utils@^0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@solana/buffer-layout-utils/-/buffer-layout-utils-0.2.0.tgz#b45a6cab3293a2eb7597cceb474f229889d875ca" @@ -157,6 +255,23 @@ dependencies: buffer "~6.0.3" +"@solana/spl-token-registry@^0.2.2405": + version "0.2.4574" + resolved "https://registry.yarnpkg.com/@solana/spl-token-registry/-/spl-token-registry-0.2.4574.tgz#13f4636b7bec90d2bb43bbbb83512cd90d2ce257" + integrity sha512-JzlfZmke8Rxug20VT/VpI2XsXlsqMlcORIUivF+Yucj7tFi7A0dXG7h+2UnD0WaZJw8BrUz2ABNkUnv89vbv1A== + dependencies: + cross-fetch "3.0.6" + +"@solana/spl-token@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.2.0.tgz#329bb6babb5de0f9c40035ddb1657f01a8347acd" + integrity sha512-RWcn31OXtdqIxmkzQfB2R+WpsJOVS6rKuvpxJFjvik2LyODd+WN58ZP3Rpjpro03fscGAkzlFuP3r42doRJgyQ== + dependencies: + "@solana/buffer-layout" "^4.0.0" + "@solana/buffer-layout-utils" "^0.2.0" + "@solana/web3.js" "^1.32.0" + start-server-and-test "^1.14.0" + "@solana/spl-token@^0.3.6": version "0.3.6" resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.3.6.tgz#35473ad2ed71fe91e5754a2ac72901e1b8b26a42" @@ -187,6 +302,27 @@ rpc-websockets "^7.5.0" superstruct "^0.14.2" +"@solana/web3.js@^1.63.1": + 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" + "@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.7.0" + bs58 "^4.0.1" + buffer "6.0.1" + fast-stable-stringify "^1.0.0" + jayson "^3.4.4" + node-fetch "2" + rpc-websockets "^7.5.0" + superstruct "^0.14.2" + "@types/bn.js@^5.1.1": version "5.1.1" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682" @@ -201,6 +337,18 @@ dependencies: "@types/node" "*" +"@types/cookie@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" + integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== + +"@types/cors@^2.8.12": + version "2.8.13" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.13.tgz#b8ade22ba455a1b8cb3b5d3f35910fd204f84f94" + integrity sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA== + dependencies: + "@types/node" "*" + "@types/debug@^4.1.7": version "4.1.7" resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82" @@ -223,6 +371,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.9.tgz#02d013de7058cea16d36168ef2fc653464cfbad4" integrity sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg== +"@types/node@>=10.0.0": + version "18.11.12" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.12.tgz#89e7f8aa8c88abf432f9bd594888144d7dba10aa" + integrity sha512-FgD3NtTAKvyMmD44T07zz2fEf+OKwutgBCEVM8GcvMGVGaDktiLNTDvPwC/LUe3PinMW+X6CuLOF2Ui1mAlSXg== + "@types/node@^12.12.54": version "12.20.55" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" @@ -338,6 +491,14 @@ JSONStream@^1.3.5: jsonparse "^1.2.0" through ">=2.2.7 <3" +accepts@~1.3.4: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -358,7 +519,7 @@ ajv@^6.10.0, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ansi-colors@^4.1.3: +ansi-colors@^4.1.1, ansi-colors@^4.1.3: version "4.1.3" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== @@ -368,7 +529,7 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-styles@^4.1.0: +ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== @@ -380,6 +541,11 @@ ansicolors@^0.3.2, ansicolors@~0.3.2: resolved "https://registry.yarnpkg.com/ansicolors/-/ansicolors-0.3.2.tgz#665597de86a9ffe3aa9bfbe6cae5c6ea426b4979" integrity sha512-QXu7BPrP29VllRxH8GwB7x5iX5qWKAAMLqKQGWTeLWVlNHNOpVMJ91dsxQAIWXpjuW5wqvxu3Jd/nRjrJ+0pqg== +arg@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + argparse@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" @@ -405,6 +571,13 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== +axios@^0.25.0: + version "0.25.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.25.0.tgz#349cfbb31331a9b4453190791760a8d35b093e0a" + integrity sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g== + dependencies: + follow-redirects "^1.14.7" + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -427,6 +600,11 @@ base64-js@^1.3.1: resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== +base64id@2.0.0, base64id@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" + integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== + bigint-buffer@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/bigint-buffer/-/bigint-buffer-1.1.5.tgz#d038f31c8e4534c1f8d0015209bf34b4fa6dd442" @@ -446,7 +624,12 @@ bindings@^1.3.0: dependencies: file-uri-to-path "1.0.0" -bn.js@^5.0.0, bn.js@^5.2.0: +bluebird@3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bn.js@^5.0.0, bn.js@^5.2.0, bn.js@^5.2.1: 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== @@ -468,6 +651,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + braces@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" @@ -489,6 +679,16 @@ bs58@^5.0.0: dependencies: base-x "^4.0.0" +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +buffer-hexdump@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-hexdump/-/buffer-hexdump-1.0.0.tgz#33cd809130c754b956c32cc15fcb258002ac77b9" + integrity sha512-Zfe5uJT9Y7J2cm2MVkmhRdkAqlbj0uw9oBaeXgOKUiXd+kdcABkp7+W+hQ8fCW/5TouBClXSzD8K5/XwVckDsg== + buffer@6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.1.tgz#3cbea8c1463e5a0779e30b66d4c88c6ffa182ac2" @@ -538,6 +738,20 @@ chalk@^4.0.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +check-more-types@2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" + integrity sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA== + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + color-convert@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" @@ -560,6 +774,19 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +cookie@~0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== + +cors@~2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + cpr@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/cpr/-/cpr-3.0.1.tgz#b9a55038b7cd81a35c17b9761895bd8496aef1e5" @@ -570,7 +797,14 @@ cpr@^3.0.1: mkdirp "~0.5.1" rimraf "^2.5.4" -cross-spawn@^7.0.2: +cross-fetch@3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.6.tgz#3a4040bc8941e653e0e9cf17f29ebcd177d3365c" + integrity sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ== + dependencies: + node-fetch "2.6.1" + +cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -579,13 +813,23 @@ cross-spawn@^7.0.2: shebang-command "^2.0.0" which "^2.0.1" -debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4: +date-fns@^2.28.0: + version "2.29.3" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8" + integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA== + +debug@4.3.4, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" +deep-diff@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-1.0.2.tgz#afd3d1f749115be965e89c63edc7abb1506b9c26" + integrity sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg== + deep-equal@^2.0.5: version "2.1.0" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.1.0.tgz#5ba60402cf44ab92c2c07f3f3312c3d857a0e1dd" @@ -630,6 +874,11 @@ delay@^5.0.0: resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== +diff@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40" + integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw== + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -659,6 +908,48 @@ dotignore@^0.1.2: dependencies: minimatch "^3.0.4" +duplexer@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +engine.io-client@~6.2.3: + version "6.2.3" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.2.3.tgz#a8cbdab003162529db85e9de31575097f6d29458" + integrity sha512-aXPtgF1JS3RuuKcpSrBtimSjYvrbhKW9froICH4s0F3XQWLxsKNxqzG39nnvQZQnva4CMvUK63T7shevxRyYHw== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.1" + engine.io-parser "~5.0.3" + ws "~8.2.3" + xmlhttprequest-ssl "~2.0.0" + +engine.io-parser@~5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.4.tgz#0b13f704fa9271b3ec4f33112410d8f3f41d0fc0" + integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg== + +engine.io@~6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.2.1.tgz#e3f7826ebc4140db9bbaa9021ad6b1efb175878f" + integrity sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA== + dependencies: + "@types/cookie" "^0.4.1" + "@types/cors" "^2.8.12" + "@types/node" ">=10.0.0" + accepts "~1.3.4" + base64id "2.0.0" + cookie "~0.4.1" + cors "~2.8.5" + debug "~4.3.1" + engine.io-parser "~5.0.3" + ws "~8.2.3" + es-abstract@^1.19.0, es-abstract@^1.20.4: version "1.20.4" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.4.tgz#1d103f9f8d78d4cf0713edcd6d0ed1a46eed5861" @@ -724,6 +1015,147 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" +esbuild-android-64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.12.tgz#5e8151d5f0a748c71a7fbea8cee844ccf008e6fc" + integrity sha512-MJKXwvPY9g0rGps0+U65HlTsM1wUs9lbjt5CU19RESqycGFDRijMDQsh68MtbzkqWSRdEtiKS1mtPzKneaAI0Q== + +esbuild-android-arm64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.15.12.tgz#5ee72a6baa444bc96ffcb472a3ba4aba2cc80666" + integrity sha512-Hc9SEcZbIMhhLcvhr1DH+lrrec9SFTiRzfJ7EGSBZiiw994gfkVV6vG0sLWqQQ6DD7V4+OggB+Hn0IRUdDUqvA== + +esbuild-darwin-64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.15.12.tgz#70047007e093fa1b3ba7ef86f9b3fa63db51fe25" + integrity sha512-qkmqrTVYPFiePt5qFjP8w/S+GIUMbt6k8qmiPraECUWfPptaPJUGkCKrWEfYFRWB7bY23FV95rhvPyh/KARP8Q== + +esbuild-darwin-arm64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.12.tgz#41c951f23d9a70539bcca552bae6e5196696ae04" + integrity sha512-z4zPX02tQ41kcXMyN3c/GfZpIjKoI/BzHrdKUwhC/Ki5BAhWv59A9M8H+iqaRbwpzYrYidTybBwiZAIWCLJAkw== + +esbuild-freebsd-64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.12.tgz#a761b5afd12bbedb7d56c612e9cfa4d2711f33f0" + integrity sha512-XFL7gKMCKXLDiAiBjhLG0XECliXaRLTZh6hsyzqUqPUf/PY4C6EJDTKIeqqPKXaVJ8+fzNek88285krSz1QECw== + +esbuild-freebsd-arm64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.12.tgz#6b0839d4d58deabc6cbd96276eb8cbf94f7f335e" + integrity sha512-jwEIu5UCUk6TjiG1X+KQnCGISI+ILnXzIzt9yDVrhjug2fkYzlLbl0K43q96Q3KB66v6N1UFF0r5Ks4Xo7i72g== + +esbuild-linux-32@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.15.12.tgz#bd50bfe22514d434d97d5150977496e2631345b4" + integrity sha512-uSQuSEyF1kVzGzuIr4XM+v7TPKxHjBnLcwv2yPyCz8riV8VUCnO/C4BF3w5dHiVpCd5Z1cebBtZJNlC4anWpwA== + +esbuild-linux-64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.15.12.tgz#074bb2b194bf658245f8490f29c01ffcdfa8c931" + integrity sha512-QcgCKb7zfJxqT9o5z9ZUeGH1k8N6iX1Y7VNsEi5F9+HzN1OIx7ESxtQXDN9jbeUSPiRH1n9cw6gFT3H4qbdvcA== + +esbuild-linux-arm64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.12.tgz#3bf789c4396dc032875a122988efd6f3733f28f5" + integrity sha512-HtNq5xm8fUpZKwWKS2/YGwSfTF+339L4aIA8yphNKYJckd5hVdhfdl6GM2P3HwLSCORS++++7++//ApEwXEuAQ== + +esbuild-linux-arm@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.15.12.tgz#b91b5a8d470053f6c2c9c8a5e67ec10a71fe4a67" + integrity sha512-Wf7T0aNylGcLu7hBnzMvsTfEXdEdJY/hY3u36Vla21aY66xR0MS5I1Hw8nVquXjTN0A6fk/vnr32tkC/C2lb0A== + +esbuild-linux-mips64le@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.12.tgz#2fb54099ada3c950a7536dfcba46172c61e580e2" + integrity sha512-Qol3+AvivngUZkTVFgLpb0H6DT+N5/zM3V1YgTkryPYFeUvuT5JFNDR3ZiS6LxhyF8EE+fiNtzwlPqMDqVcc6A== + +esbuild-linux-ppc64le@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.12.tgz#9e3b8c09825fb27886249dfb3142a750df29a1b7" + integrity sha512-4D8qUCo+CFKaR0cGXtGyVsOI7w7k93Qxb3KFXWr75An0DHamYzq8lt7TNZKoOq/Gh8c40/aKaxvcZnTgQ0TJNg== + +esbuild-linux-riscv64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.12.tgz#923d0f5b6e12ee0d1fe116b08e4ae4478fe40693" + integrity sha512-G9w6NcuuCI6TUUxe6ka0enjZHDnSVK8bO+1qDhMOCtl7Tr78CcZilJj8SGLN00zO5iIlwNRZKHjdMpfFgNn1VA== + +esbuild-linux-s390x@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.12.tgz#3b1620220482b96266a0c6d9d471d451a1eab86f" + integrity sha512-Lt6BDnuXbXeqSlVuuUM5z18GkJAZf3ERskGZbAWjrQoi9xbEIsj/hEzVnSAFLtkfLuy2DE4RwTcX02tZFunXww== + +esbuild-netbsd-64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.12.tgz#276730f80da646859b1af5a740e7802d8cd73e42" + integrity sha512-jlUxCiHO1dsqoURZDQts+HK100o0hXfi4t54MNRMCAqKGAV33JCVvMplLAa2FwviSojT/5ZG5HUfG3gstwAG8w== + +esbuild-openbsd-64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.12.tgz#bd0eea1dd2ca0722ed489d88c26714034429f8ae" + integrity sha512-1o1uAfRTMIWNOmpf8v7iudND0L6zRBYSH45sofCZywrcf7NcZA+c7aFsS1YryU+yN7aRppTqdUK1PgbZVaB1Dw== + +esbuild-runner@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/esbuild-runner/-/esbuild-runner-2.2.2.tgz#4243089f14c9690bff70beee16da3c41fd1dec50" + integrity sha512-fRFVXcmYVmSmtYm2mL8RlUASt2TDkGh3uRcvHFOKNr/T58VrfVeKD9uT9nlgxk96u0LS0ehS/GY7Da/bXWKkhw== + dependencies: + source-map-support "0.5.21" + tslib "2.4.0" + +esbuild-sunos-64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.15.12.tgz#5e56bf9eef3b2d92360d6d29dcde7722acbecc9e" + integrity sha512-nkl251DpoWoBO9Eq9aFdoIt2yYmp4I3kvQjba3jFKlMXuqQ9A4q+JaqdkCouG3DHgAGnzshzaGu6xofGcXyPXg== + +esbuild-windows-32@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.15.12.tgz#a4f1a301c1a2fa7701fcd4b91ef9d2620cf293d0" + integrity sha512-WlGeBZHgPC00O08luIp5B2SP4cNCp/PcS+3Pcg31kdcJPopHxLkdCXtadLU9J82LCfw4TVls21A6lilQ9mzHrw== + +esbuild-windows-64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.15.12.tgz#bc2b467541744d653be4fe64eaa9b0dbbf8e07f6" + integrity sha512-VActO3WnWZSN//xjSfbiGOSyC+wkZtI8I4KlgrTo5oHJM6z3MZZBCuFaZHd8hzf/W9KPhF0lY8OqlmWC9HO5AA== + +esbuild-windows-arm64@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.12.tgz#9a7266404334a86be800957eaee9aef94c3df328" + integrity sha512-Of3MIacva1OK/m4zCNIvBfz8VVROBmQT+gRX6pFTLPngFYcj6TFH/12VveAqq1k9VB2l28EoVMNMUCcmsfwyuA== + +esbuild@0.15.12: + version "0.15.12" + resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.15.12.tgz#6c8e22d6d3b7430d165c33848298d3fc9a1f251c" + integrity sha512-PcT+/wyDqJQsRVhaE9uX/Oq4XLrFh0ce/bs2TJh4CSaw9xuvI+xFrH2nAYOADbhQjUgAhNWC5LKoUsakm4dxng== + optionalDependencies: + "@esbuild/android-arm" "0.15.12" + "@esbuild/linux-loong64" "0.15.12" + esbuild-android-64 "0.15.12" + esbuild-android-arm64 "0.15.12" + esbuild-darwin-64 "0.15.12" + esbuild-darwin-arm64 "0.15.12" + esbuild-freebsd-64 "0.15.12" + esbuild-freebsd-arm64 "0.15.12" + esbuild-linux-32 "0.15.12" + esbuild-linux-64 "0.15.12" + esbuild-linux-arm "0.15.12" + esbuild-linux-arm64 "0.15.12" + esbuild-linux-mips64le "0.15.12" + esbuild-linux-ppc64le "0.15.12" + esbuild-linux-riscv64 "0.15.12" + esbuild-linux-s390x "0.15.12" + esbuild-netbsd-64 "0.15.12" + esbuild-openbsd-64 "0.15.12" + esbuild-sunos-64 "0.15.12" + esbuild-windows-32 "0.15.12" + esbuild-windows-64 "0.15.12" + esbuild-windows-arm64 "0.15.12" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" @@ -857,11 +1289,39 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +event-stream@=3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" + integrity sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g== + dependencies: + duplexer "~0.1.1" + from "~0" + map-stream "~0.1.0" + pause-stream "0.0.11" + split "0.3" + stream-combiner "~0.0.4" + through "~2.3.1" + eventemitter3@^4.0.7: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== +execa@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + eyes@^0.1.8: version "0.1.8" resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" @@ -950,6 +1410,11 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== +follow-redirects@^1.14.7: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -957,6 +1422,11 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +from@~0: + version "0.1.7" + resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" + integrity sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g== + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -982,6 +1452,11 @@ functions-have-names@^1.2.2: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385" @@ -996,6 +1471,11 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + get-symbol-description@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" @@ -1110,6 +1590,11 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -1202,6 +1687,11 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -1256,6 +1746,11 @@ is-shared-array-buffer@^1.0.2: dependencies: call-bind "^1.0.2" +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + is-string@^1.0.5, is-string@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" @@ -1335,6 +1830,17 @@ jayson@^3.4.4: uuid "^8.3.2" ws "^7.4.5" +joi@^17.6.0: + version "17.7.0" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.7.0.tgz#591a33b1fe1aca2bc27f290bcad9b9c1c570a6b3" + integrity sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@sideway/address" "^4.1.3" + "@sideway/formula" "^3.0.0" + "@sideway/pinpoint" "^2.0.0" + js-sdsl@^4.1.4: version "4.2.0" resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.2.0.tgz#278e98b7bea589b8baaf048c20aeb19eb7ad09d0" @@ -1345,6 +1851,11 @@ 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" @@ -1367,11 +1878,21 @@ json-stringify-safe@^5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== +jsonc-parser@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76" + integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w== + jsonparse@^1.2.0: version "1.3.1" resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== +lazy-ass@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" + integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw== + levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -1392,7 +1913,7 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash@^4.17.20: +lodash@^4.17.20, lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -1411,6 +1932,26 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lunr@^2.3.9: + version "2.3.9" + resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" + integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== + +map-stream@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" + integrity sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g== + +marked@^4.0.19: + version "4.2.4" + resolved "https://registry.yarnpkg.com/marked/-/marked-4.2.4.tgz#5a4ce6c7a1ae0c952601fce46376ee4cf1797e1c" + integrity sha512-Wcc9ikX7Q5E4BYDPvh1C6QNSxrjC9tBgz+A/vAhp59KXUgachw++uMvMKiSW8oA85nopmPZcEvBoex/YLMsiyA== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" @@ -1424,6 +1965,23 @@ micromatch@^4.0.4: braces "^3.0.2" picomatch "^2.3.1" +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -1431,7 +1989,14 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" -minimist@^1.2.0, minimist@^1.2.6: +minimatch@^5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.1.tgz#6c9dffcf9927ff2a31e74b5af11adf8b9604b022" + integrity sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g== + dependencies: + brace-expansion "^2.0.1" + +minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: version "1.2.7" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== @@ -1458,6 +2023,16 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +netstats@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/netstats/-/netstats-0.0.6.tgz#57072d42a6c420a931d1db9c4ca3379717305069" + integrity sha512-j5sdwaoLX/0x74+8bFdnoVEo3RUQexm5Lw615MVrgx7/FSQx88dZj+t5whwrDSrlsiHMYhKpn52p/6oMYHTZ2A== + no-case@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" @@ -1473,11 +2048,33 @@ node-fetch@2: dependencies: whatwg-url "^5.0.0" +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-gyp-build@^4.3.0: version "4.5.0" resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +numeral@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/numeral/-/numeral-2.0.6.tgz#4ad080936d443c2561aed9f2197efffe25f4e506" + integrity sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA== + +object-assign@^4: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + object-inspect@^1.12.2, object-inspect@^1.9.0: version "1.12.2" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" @@ -1513,6 +2110,13 @@ once@^1.3.0: dependencies: wrappy "1" +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + optionator@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" @@ -1556,7 +2160,7 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-key@^3.1.0: +path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== @@ -1571,11 +2175,26 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +pause-stream@0.0.11: + version "0.0.11" + resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" + integrity sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A== + dependencies: + through "~2.3" + picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +port-pid@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/port-pid/-/port-pid-0.0.7.tgz#382cb46632e2efbda230765882aaa0029edf8144" + integrity sha512-2QkdCeuGLfu1jDxiiwOnafCgVFsMQXMEWvUVx9SMNq+4k6HDKiyA9YVRhmoMvJGg5ZhqEjblpj+1wdWMqn7scw== + dependencies: + netstats "0.0.6" + selective-whitespace "^1.0.0" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -1593,6 +2212,13 @@ prettier@^2.5.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.0.tgz#c7df58393c9ba77d6fba3921ae01faf994fb9dc9" integrity sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA== +ps-tree@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd" + integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA== + dependencies: + event-stream "=3.3.4" + punycode@^2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" @@ -1622,6 +2248,11 @@ regexpp@^3.2.0: resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + resolve-from@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" @@ -1682,6 +2313,13 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +rxjs@^7.5.4: + version "7.6.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.6.0.tgz#361da5362b6ddaa691a2de0b4f2d32028f1eb5a2" + integrity sha512-DDa7d8TFNUalGC9VqXvQ1euWNN7sc63TrUCuM9J998+ViviahMIjKSOU7rfcgFOF+FCD71BhDRv4hrFz+ImDLQ== + dependencies: + tslib "^2.1.0" + safe-buffer@^5.0.1: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -1696,6 +2334,13 @@ safe-regex-test@^1.0.0: get-intrinsic "^1.1.3" is-regex "^1.1.4" +selective-whitespace@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/selective-whitespace/-/selective-whitespace-1.0.4.tgz#02d428b46b2c770398be9a538eb442b1e842fc5c" + integrity sha512-DyL/pLbb9poQNQOVndVohclAO8lq+6gEBW8q3H9c2fX+ODkugQMvjBilPjw09lrIuVRFRQ/nwhLdzn60sFh9lQ== + dependencies: + tokenize-whitespace "0.0.1" + semver@^7.3.7: version "7.3.8" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" @@ -1715,6 +2360,15 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +shiki@^0.11.1: + version "0.11.1" + resolved "https://registry.yarnpkg.com/shiki/-/shiki-0.11.1.tgz#df0f719e7ab592c484d8b73ec10e215a503ab8cc" + integrity sha512-EugY9VASFuDqOexOgXR18ZV+TbFrQHeCpEYaXamO+SZlsnT/2LxuLBX25GGtIrwaEVFXUAbUQ601SWE2rMwWHA== + dependencies: + jsonc-parser "^3.0.0" + vscode-oniguruma "^1.6.1" + vscode-textmate "^6.0.0" + side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -1724,6 +2378,11 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" +signal-exit@^3.0.3: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" @@ -1737,6 +2396,61 @@ snake-case@^3.0.4: dot-case "^3.0.4" tslib "^2.0.3" +socket.io-adapter@~2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz#b50a4a9ecdd00c34d4c8c808224daa1a786152a6" + integrity sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg== + +socket.io-client@^4.4.1: + version "4.5.4" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.5.4.tgz#d3cde8a06a6250041ba7390f08d2468ccebc5ac9" + integrity sha512-ZpKteoA06RzkD32IbqILZ+Cnst4xewU7ZYK12aS1mzHftFFjpoMz69IuhP/nL25pJfao/amoPI527KnuhFm01g== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.2" + engine.io-client "~6.2.3" + socket.io-parser "~4.2.1" + +socket.io-parser@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.1.tgz#01c96efa11ded938dcb21cbe590c26af5eff65e5" + integrity sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.1" + +socket.io@^4.4.1: + version "4.5.4" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.5.4.tgz#a4513f06e87451c17013b8d13fdfaf8da5a86a90" + integrity sha512-m3GC94iK9MfIEeIBfbhJs5BqFibMtkRk8ZpKwG2QwxV0m/eEhPIV4ara6XCF1LWNAus7z58RodiZlAH71U3EhQ== + dependencies: + accepts "~1.3.4" + base64id "~2.0.0" + debug "~4.3.2" + engine.io "~6.2.1" + socket.io-adapter "~2.4.0" + socket.io-parser "~4.2.1" + +source-map-support@0.5.21: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +split@0.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" + integrity sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA== + dependencies: + through "2" + spok@^1.4.3: version "1.4.3" resolved "https://registry.yarnpkg.com/spok/-/spok-1.4.3.tgz#8516234e6bd8caf0e10567bd675e15fd03b5ceb8" @@ -1744,6 +2458,36 @@ spok@^1.4.3: dependencies: ansicolors "~0.3.2" +start-server-and-test@^1.14.0: + version "1.15.2" + resolved "https://registry.yarnpkg.com/start-server-and-test/-/start-server-and-test-1.15.2.tgz#3c4f9b358a0dc5ae03a96dd7d7ae9e25a3b24165" + integrity sha512-t5xJX04Hg7hqxiKHMJBz/n4zIMsE6G7hpAcerFAH+4Vh9le/LeyFcJERJM7WLiPygWF9TOg33oroJF1XOzJtYQ== + dependencies: + arg "^5.0.2" + bluebird "3.7.2" + check-more-types "2.24.0" + debug "4.3.4" + execa "5.1.1" + lazy-ass "1.6.0" + ps-tree "1.2.0" + wait-on "6.0.1" + +stream-combiner@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" + integrity sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw== + dependencies: + duplexer "~0.1.1" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string.prototype.trim@^1.2.6: version "1.2.7" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" @@ -1771,13 +2515,18 @@ string.prototype.trimstart@^1.0.5: define-properties "^1.1.4" es-abstract "^1.20.4" -strip-ansi@^6.0.1: +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -1842,11 +2591,16 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== -"through@>=2.2.7 <3", through@^2.3.8, through@~2.3.4: +through@2, "through@>=2.2.7 <3", through@^2.3.8, through@~2.3, through@~2.3.1, through@~2.3.4: version "2.3.8" resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== +timeago.js@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/timeago.js/-/timeago.js-4.0.2.tgz#724e8c8833e3490676c7bb0a75f5daf20e558028" + integrity sha512-a7wPxPdVlQL7lqvitHGGRsofhdwtkoSXPGATFuSOA2i1ZNQEPLrGnj68vOp2sOJTCFAQVXPeNMX/GctBaO9L2w== + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -1854,6 +2608,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +tokenize-whitespace@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tokenize-whitespace/-/tokenize-whitespace-0.0.1.tgz#3c207ab58948113225246285200563e3b4b5c6ff" + integrity sha512-OxbdMHTjVs0ccBMfjo1v5OH6bq8yhydewCd9n6xihgtZvI3wSqR00EsM86DjELLl6VtMkY2z99r5q+uNW79E+Q== + toml@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee" @@ -1864,12 +2623,22 @@ tr46@~0.0.3: resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== +ts-essentials@^9.3.0: + version "9.3.0" + resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-9.3.0.tgz#7e639c1a76b1805c3c60d6e1b5178da2e70aea02" + integrity sha512-XeiCboEyBG8UqXZtXl59bWEi4ZgOqRsogFDI6WDGIF1LmzbYiAkIwjkXN6zZWWl4re/lsOqMlYfe8KA0XiiEPw== + +tslib@2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + tslib@^1.8.1: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.3: +tslib@^2.0.3, tslib@^2.1.0: version "2.4.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== @@ -1881,6 +2650,11 @@ tsutils@^3.21.0: dependencies: tslib "^1.8.1" +tweetnacl@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -1893,6 +2667,16 @@ type-fest@^0.20.2: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== +typedoc@^0.23.16: + version "0.23.21" + resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.23.21.tgz#2a6b0e155f91ffa9689086706ad7e3e4bc11d241" + integrity sha512-VNE9Jv7BgclvyH9moi2mluneSviD43dCE9pY8RWkO88/DrEgJZk9KpUk7WO468c9WWs/+aG6dOnoH7ccjnErhg== + dependencies: + lunr "^2.3.9" + marked "^4.0.19" + minimatch "^5.1.0" + shiki "^0.11.1" + typescript@^4.9.3: version "4.9.3" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.3.tgz#3aea307c1746b8c384435d8ac36b8a2e580d85db" @@ -1927,6 +2711,32 @@ uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +vary@^1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +vscode-oniguruma@^1.6.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b" + integrity sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA== + +vscode-textmate@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-6.0.0.tgz#a3777197235036814ac9a92451492f2748589210" + integrity sha512-gu73tuZfJgu+mvCSy4UZwd2JXykjK9zAZsfmDeut5dx/1a7FeTk0XwJsSuqQn+cuMCGVbIBfl+s53X4T19DnzQ== + +wait-on@6.0.1, wait-on@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-6.0.1.tgz#16bbc4d1e4ebdd41c5b4e63a2e16dbd1f4e5601e" + integrity sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw== + dependencies: + axios "^0.25.0" + joi "^17.6.0" + lodash "^4.17.21" + minimist "^1.2.5" + rxjs "^7.5.4" + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -1985,6 +2795,15 @@ word-wrap@^1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -2000,11 +2819,44 @@ ws@^8.5.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== +ws@~8.2.3: + version "8.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" + integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA== + +xmlhttprequest-ssl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67" + integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.3.0: + version "17.6.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541" + integrity sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" diff --git a/token-metadata/macro/Cargo.toml b/token-metadata/macro/Cargo.toml new file mode 100644 index 0000000000..091524241d --- /dev/null +++ b/token-metadata/macro/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "mpl-token-metadata-context-derive" +version = "0.2.1" +edition = "2021" +description = "Metaplex Metadata" +authors = ["Metaplex Developers "] +repository = "https://github.com/metaplex-foundation/metaplex-program-library" +license-file = "../../LICENSE" +readme = "README.md" + +[lib] +proc-macro = true + +[dependencies] +quote = "1.0.21" +syn = { version = "1.0.103", features = ["extra-traits", "full"] } diff --git a/token-metadata/macro/README.md b/token-metadata/macro/README.md new file mode 100644 index 0000000000..dea360ea2e --- /dev/null +++ b/token-metadata/macro/README.md @@ -0,0 +1 @@ +A macro file for creating instruction builders on vanilla Solana programs to be used by shank and solita. \ No newline at end of file diff --git a/token-metadata/macro/src/lib.rs b/token-metadata/macro/src/lib.rs new file mode 100644 index 0000000000..1249e66ac6 --- /dev/null +++ b/token-metadata/macro/src/lib.rs @@ -0,0 +1,514 @@ +use proc_macro::TokenStream; +use quote::quote; +use std::collections::HashMap; +use syn::{ + self, parse_macro_input, DeriveInput, Expr, ExprPath, GenericArgument, Lit, Meta, MetaList, + MetaNameValue, NestedMeta, Path, PathArguments, Type, TypePath, +}; + +#[derive(Default)] +struct Variant { + pub name: String, + pub tuple: Option, + pub accounts: Vec, + // (name, type, generic type) + pub args: Vec<(String, String, Option)>, +} + +#[derive(Debug)] +struct Account { + pub name: String, + pub optional: bool, +} + +// Helper account attribute (reusing from shank annotation). +const ACCOUNT_ATTRIBUTE: &str = "account"; +// Helper args attribute. +const ARGS_ATTRIBUTE: &str = "args"; +// Name property in the account attribute. +const NAME_PROPERTY: &str = "name"; +// Optional property in the account attribute. +const OPTIONAL_PROPERTY: &str = "optional"; + +#[proc_macro_derive(AccountContext, attributes(account, args))] +pub fn account_context_derive(input: TokenStream) -> TokenStream { + let ast = parse_macro_input!(input as DeriveInput); + + // identifies the accounts associated with each enum variant + + let variants = if let syn::Data::Enum(syn::DataEnum { ref variants, .. }) = ast.data { + let mut enum_variants = Vec::new(); + + for v in variants { + // extract the enum data (if there is one present) + let mut variant = Variant { + tuple: if let syn::Fields::Unnamed(syn::FieldsUnnamed { unnamed, .. }) = &v.fields { + match unnamed.first() { + Some(syn::Field { + ty: + Type::Path(TypePath { + path: Path { segments, .. }, + .. + }), + .. + }) => Some(segments.first().unwrap().ident.to_string()), + _ => None, + } + } else { + None + }, + name: v.ident.to_string(), + ..Default::default() + }; + + // parse the attribute of the variant + for a in &v.attrs { + let syn::Attribute { + path: syn::Path { segments, .. }, + .. + } = &a; + let mut skip = true; + let mut attribute = String::new(); + + for path in segments { + let ident = path.ident.to_string(); + // we are only interested in #[account] and #[args] attributes + if ident == ACCOUNT_ATTRIBUTE || ident == ARGS_ATTRIBUTE { + attribute = ident; + skip = false; + } + } + + if !skip { + if attribute == ACCOUNT_ATTRIBUTE { + let meta_tokens = a.parse_meta().unwrap(); + let nested_meta = if let Meta::List(MetaList { nested, .. }) = &meta_tokens + { + nested + } else { + panic!("#[account] requires attributes account name"); + }; + + // (name, optional) + let mut property: (Option, Option) = (None, None); + + for element in nested_meta { + match element { + // name = value (ignores any other attribute) + NestedMeta::Meta(Meta::NameValue(MetaNameValue { + path, + lit, + .. + })) => { + let ident = path.get_ident(); + if let Some(ident) = ident { + if *ident == NAME_PROPERTY { + let token = match lit { + // removes the surrounding "'s from string values" + Lit::Str(lit) => { + lit.token().to_string().replace('\"', "") + } + _ => panic!("Invalid value for property {ident}"), + }; + property.0 = Some(token); + } + } + } + // optional + NestedMeta::Meta(Meta::Path(path)) => { + let name = path.get_ident().map(|x| x.to_string()); + if let Some(name) = name { + if name == OPTIONAL_PROPERTY { + property.1 = Some(name); + } + } + } + _ => {} + } + } + variant.accounts.push(Account { + name: property.0.unwrap(), + optional: property.1.is_some(), + }); + } else if attribute == ARGS_ATTRIBUTE { + let args_tokens: syn::ExprType = a.parse_args().unwrap(); + // name + let name = match *args_tokens.expr { + Expr::Path(ExprPath { + path: Path { segments, .. }, + .. + }) => segments.first().unwrap().ident.to_string(), + _ => panic!("#[args] requires an expression 'name: type'"), + }; + // type + match *args_tokens.ty { + Type::Path(TypePath { + path: Path { segments, .. }, + .. + }) => { + let segment = segments.first().unwrap(); + + // check whether we are dealing with a generic type + let generic_ty = match &segment.arguments { + PathArguments::AngleBracketed(arguments) => { + if let Some(GenericArgument::Type(Type::Path(ty))) = + arguments.args.first() + { + Some( + ty.path.segments.first().unwrap().ident.to_string(), + ) + } else { + None + } + } + _ => None, + }; + + let ty = segment.ident.to_string(); + variant.args.push((name, ty, generic_ty)); + } + _ => panic!("#[args] requires an expression 'name: type'"), + } + } + } + } + + enum_variants.push(variant); + } + + enum_variants + } else { + panic!("No enum variants found"); + }; + + let mut account_structs = generate_accounts(&variants); + account_structs.extend(generate_builders(&variants)); + + account_structs +} + +/// Generates a struct for each enum variant. +/// +/// The struct will contain all shank annotated accounts and the impl block +/// will initialize them using the accounts iterators. It support the use of +/// optional accounts, which would generate an account field with an +/// `Option>` type. +/// +/// ```ignore +/// pub struct MyAccount<'a> { +/// my_first_account: solana_program::account_info::AccountInfo<'a>, +/// my_second_optional_account: Option>, +/// .. +/// } +/// impl<'a> MyAccount<'a> { +/// pub fn to_context( +/// accounts: &'a [solana_program::account_info::AccountInfo<'a>] +/// ) -> Result, solana_program::sysvar::slot_history::ProgramError> { +/// let account_info_iter = &mut accounts.iter(); +/// +/// let my_first_account = solana_program::account_info::next_account_info(account_info_iter)?; +/// +/// .. +/// +/// } +/// } +/// ``` +fn generate_accounts(variants: &[Variant]) -> TokenStream { + // build the trait implementation + let variant_structs = variants.iter().map(|variant| { + let name = syn::parse_str::(&variant.name).unwrap(); + // accounts names + let fields = variant.accounts.iter().map(|account| { + let account_name = syn::parse_str::(format!("{}_info", &account.name).as_str()).unwrap(); + quote! { #account_name } + }); + // accounts fields + let struct_fields = variant.accounts.iter().map(|account| { + let account_name = syn::parse_str::(format!("{}_info", &account.name).as_str()).unwrap(); + if account.optional { + quote! { + pub #account_name: Option<&'a solana_program::account_info::AccountInfo<'a>> + } + } else { + quote! { + pub #account_name:&'a solana_program::account_info::AccountInfo<'a> + } + } + }); + // accounts initialization for the impl block + let impl_fields = variant.accounts.iter().map(|account| { + let account_name = syn::parse_str::(format!("{}_info", &account.name).as_str()).unwrap(); + if account.optional { + quote! { + let #account_name = crate::processor::next_optional_account_info(account_info_iter)?; + } + } else { + quote! { + let #account_name = solana_program::account_info::next_account_info(account_info_iter)?; + } + } + }); + + quote! { + pub struct #name<'a> { + #(#struct_fields,)* + } + impl<'a> #name<'a> { + pub fn to_context(accounts: &'a [solana_program::account_info::AccountInfo<'a>]) -> Result, solana_program::sysvar::slot_history::ProgramError> { + let account_info_iter = &mut accounts.iter(); + + #(#impl_fields)* + + let accounts = Self { + #(#fields,)* + }; + + Ok(Context { + accounts, + remaining_accounts: Vec::<&'a AccountInfo<'a>>::from_iter(account_info_iter), + }) + } + } + } + }); + + TokenStream::from(quote! { + #(#variant_structs)* + }) +} + +fn generate_builders(variants: &[Variant]) -> TokenStream { + let mut default_pubkeys = HashMap::new(); + default_pubkeys.insert( + "system_program".to_string(), + syn::parse_str::("solana_program::system_program::ID").unwrap(), + ); + default_pubkeys.insert( + "spl_token_program".to_string(), + syn::parse_str::("spl_token::ID").unwrap(), + ); + default_pubkeys.insert( + "spl_ata_program".to_string(), + syn::parse_str::("spl_associated_token_account::ID").unwrap(), + ); + default_pubkeys.insert( + "sysvar_instructions".to_string(), + syn::parse_str::("solana_program::sysvar::instructions::ID").unwrap(), + ); + default_pubkeys.insert( + "authorization_rules_program".to_string(), + syn::parse_str::("mpl_token_auth_rules::ID").unwrap(), + ); + + // build the trait implementation + let variant_structs = variants.iter().map(|variant| { + let name = syn::parse_str::(&variant.name).unwrap(); + + // struct block for the builder: this will contain both accounts and + // args for the builder + + // accounts + let struct_accounts = variant.accounts.iter().map(|account| { + let account_name = syn::parse_str::(&account.name).unwrap(); + if account.optional { + quote! { + pub #account_name: Option + } + } else { + quote! { + pub #account_name: solana_program::pubkey::Pubkey + } + } + }); + + // args + let struct_args = variant.args.iter().map(|(name, ty, generic_ty)| { + let ident_ty = syn::parse_str::(ty).unwrap(); + let arg_ty = if let Some(genetic_ty) = generic_ty { + let arg_generic_ty = syn::parse_str::(genetic_ty).unwrap(); + quote! { #ident_ty<#arg_generic_ty> } + } else { + quote! { #ident_ty } + }; + let arg_name = syn::parse_str::(name).unwrap(); + + quote! { + pub #arg_name: #arg_ty + } + }); + + // builder block: this will have all accounts and args as optional fields + // that need to be set before the build method is called + + // accounts + let builder_accounts = variant.accounts.iter().map(|account| { + let account_name = syn::parse_str::(&account.name).unwrap(); + quote! { + pub #account_name: Option + } + }); + + // accounts initialization + let builder_initialize_accounts = variant.accounts.iter().map(|account| { + let account_name = syn::parse_str::(&account.name).unwrap(); + quote! { + #account_name: None + } + }); + + // args + let builder_args = variant.args.iter().map(|(name, ty, generic_ty)| { + let ident_ty = syn::parse_str::(ty).unwrap(); + let arg_ty = if let Some(genetic_ty) = generic_ty { + let arg_generic_ty = syn::parse_str::(genetic_ty).unwrap(); + quote! { #ident_ty<#arg_generic_ty> } + } else { + quote! { #ident_ty } + }; + let arg_name = syn::parse_str::(name).unwrap(); + + quote! { + pub #arg_name: Option<#arg_ty> + } + }); + + // args initialization + let builder_initialize_args = variant.args.iter().map(|(name, _ty, _generi_ty)| { + let arg_name = syn::parse_str::(name).unwrap(); + quote! { + #arg_name: None + } + }); + + // account setter methods + let builder_accounts_methods = variant.accounts.iter().map(|account| { + let account_name = syn::parse_str::(&account.name).unwrap(); + quote! { + pub fn #account_name(&mut self, #account_name: solana_program::pubkey::Pubkey) -> &mut Self { + self.#account_name = Some(#account_name); + self + } + } + }); + + // args setter methods + let builder_args_methods = variant.args.iter().map(|(name, ty, generic_ty)| { + let ident_ty = syn::parse_str::(ty).unwrap(); + let arg_ty = if let Some(genetic_ty) = generic_ty { + let arg_generic_ty = syn::parse_str::(genetic_ty).unwrap(); + quote! { #ident_ty<#arg_generic_ty> } + } else { + quote! { #ident_ty } + }; + let arg_name = syn::parse_str::(name).unwrap(); + + quote! { + pub fn #arg_name(&mut self, #arg_name: #arg_ty) -> &mut Self { + self.#arg_name = Some(#arg_name); + self + } + } + }); + + // required accounts + let required_accounts = variant.accounts.iter().map(|account| { + let account_name = syn::parse_str::(&account.name).unwrap(); + + if account.optional { + quote! { + #account_name: self.#account_name + } + } else { + // are we dealing with a default pubkey? + if default_pubkeys.contains_key(&account.name) { + let pubkey = default_pubkeys.get(&account.name).unwrap(); + // we add the default key as the fallback value + quote! { + #account_name: self.#account_name.unwrap_or(#pubkey) + } + } + else { + // if not a default pubkey, we will need to have it set + quote! { + #account_name: self.#account_name.ok_or(concat!(stringify!(#account_name), " is not set"))? + } + } + } + }); + + // required args + let required_args = variant.args.iter().map(|(name, _ty, _generic_ty)| { + let arg_name = syn::parse_str::(name).unwrap(); + quote! { + #arg_name: self.#arg_name.clone().ok_or(concat!(stringify!(#arg_name), " is not set"))? + } + }); + + // args parameter list + let args = if let Some(args) = &variant.tuple { + let arg_ty = syn::parse_str::(args).unwrap(); + quote! { &mut self, args: #arg_ty } + } else { + quote! { &mut self } + }; + + // instruction args + let instruction_args = if let Some(args) = &variant.tuple { + let arg_ty = syn::parse_str::(args).unwrap(); + quote! { pub args: #arg_ty, } + } else { + quote! { } + }; + + // required instruction args + let required_instruction_args = if variant.tuple.is_some() { + quote! { args, } + } else { + quote! { } + }; + + // builder name + let builder_name = syn::parse_str::(&format!("{}Builder", name)).unwrap(); + + quote! { + pub struct #name { + #(#struct_accounts,)* + #(#struct_args,)* + #instruction_args + } + + pub struct #builder_name { + #(#builder_accounts,)* + #(#builder_args,)* + } + + impl #builder_name { + pub fn new() -> Box<#builder_name> { + Box::new(#builder_name { + #(#builder_initialize_accounts,)* + #(#builder_initialize_args,)* + }) + } + + #(#builder_accounts_methods)* + #(#builder_args_methods)* + + pub fn build(#args) -> Result, Box> { + Ok(Box::new(#name { + #(#required_accounts,)* + #(#required_args,)* + #required_instruction_args + })) + } + } + } + }); + + TokenStream::from(quote! { + pub mod builders { + use super::*; + + #(#variant_structs)* + } + }) +} diff --git a/token-metadata/program/Cargo.lock b/token-metadata/program/Cargo.lock deleted file mode 100644 index b7c18e49dc..0000000000 --- a/token-metadata/program/Cargo.lock +++ /dev/null @@ -1,3253 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" - -[[package]] -name = "addr2line" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom 0.2.4", - "once_cell", - "version_check", -] - -[[package]] -name = "aho-corasick" -version = "0.7.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" -dependencies = [ - "memchr", -] - -[[package]] -name = "aliasable" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" - -[[package]] -name = "anyhow" -version = "1.0.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0" - -[[package]] -name = "arrayref" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" - -[[package]] -name = "arrayvec" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" - -[[package]] -name = "ascii" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab1c04a571841102f5345a8fc0f6bb3d31c315dec879b5c6e42e40ce7ffa34e" - -[[package]] -name = "assert_matches" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" - -[[package]] -name = "async-trait" -version = "0.1.52" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061a7acccaa286c011ddc30970520b98fa40e00c9d644633fb26b5fc63a265e3" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - -[[package]] -name = "backtrace" -version = "0.3.63" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base64" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" - -[[package]] -name = "base64" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" - -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "blake3" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08e53fc5a564bb15bfe6fae56bd71522205f1f91893f9c0116edad6496c183f" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if", - "constant_time_eq", - "digest 0.10.1", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "block-padding", - "generic-array", -] - -[[package]] -name = "block-buffer" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-padding" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" - -[[package]] -name = "borsh" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c9d0958efb8301e1626692ea879cbff622ef45cf731807ec8d488b34be98cb8" -dependencies = [ - "borsh-derive", - "hashbrown", -] - -[[package]] -name = "borsh-derive" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "325164710ad57bae6d32455ce3bd384f95768464a927ce145626dc3390a7f9fe" -dependencies = [ - "borsh-derive-internal", - "borsh-schema-derive-internal", - "proc-macro-crate 0.1.5", - "proc-macro2", - "syn", -] - -[[package]] -name = "borsh-derive-internal" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f74159f43b231f4af8c4ce4967fef76e4e59725acf51706ddb9268c94348d15c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "borsh-schema-derive-internal" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99b2a77771907a820a860d200d193a0787c79a7890c8e253c462fa0f51ad58b6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "bs58" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3" - -[[package]] -name = "bumpalo" -version = "3.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899" - -[[package]] -name = "bv" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8834bb1d8ee5dc048ee3124f2c7c1afcc6bc9aed03f11e9dfd8c69470a5db340" -dependencies = [ - "feature-probe", - "serde", -] - -[[package]] -name = "bytemuck" -version = "1.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439989e6b8c38d1b6570a384ef1e49c8848128f5a97f3914baef02920842712f" -dependencies = [ - "bytemuck_derive", -] - -[[package]] -name = "bytemuck_derive" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e215f8c2f9f79cb53c8335e687ffd07d5bfcb6fe5fc80723762d0be46e7cc54" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" - -[[package]] -name = "bzip2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0" -dependencies = [ - "bzip2-sys", - "libc", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.11+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "cc" -version = "1.0.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" -dependencies = [ - "jobserver", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" -dependencies = [ - "libc", - "num-integer", - "num-traits", - "serde", - "time", - "winapi", -] - -[[package]] -name = "chrono-humanize" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eddc119501d583fd930cb92144e605f44e0252c38dd89d9247fffa1993375cb" -dependencies = [ - "chrono", -] - -[[package]] -name = "combine" -version = "3.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3da6baa321ec19e1cc41d31bf599f00c783d0517095cdaf0332e3fe8d20680" -dependencies = [ - "ascii", - "byteorder", - "either", - "memchr", - "unreachable", -] - -[[package]] -name = "console_error_panic_hook" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06aeb73f470f66dcdbf7223caeebb85984942f22f1adb2a088cf9668146bbbc" -dependencies = [ - "cfg-if", - "wasm-bindgen", -] - -[[package]] -name = "console_log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501a375961cef1a0d44767200e66e4a559283097e91d0730b1d75dfb2f8a1494" -dependencies = [ - "log", - "web-sys", -] - -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - -[[package]] -name = "cpufeatures" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2209c310e29876f7f0b2721e7e26b84aff178aa3da5d091f9bfbf47669e60e3" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa" -dependencies = [ - "cfg-if", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" -dependencies = [ - "cfg-if", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97242a70df9b89a65d0b6df3c4bf5b9ce03c5b7309019777fbde37e7537f8762" -dependencies = [ - "cfg-if", - "crossbeam-utils", - "lazy_static", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38" -dependencies = [ - "cfg-if", - "lazy_static", -] - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-common" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d6b536309245c849479fba3da410962a43ed8e51c26b729208ec0ac2798d0" -dependencies = [ - "generic-array", -] - -[[package]] -name = "crypto-mac" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "crypto-mac" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58bcd97a54c7ca5ce2f6eb16f6bede5b0ab5f0055fedc17d2f0b4466e21671ca" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "crypto-mac" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "curve25519-dalek" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" -dependencies = [ - "byteorder", - "digest 0.9.0", - "rand_core 0.5.1", - "subtle", - "zeroize", -] - -[[package]] -name = "dashmap" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" -dependencies = [ - "cfg-if", - "num_cpus", - "rayon", -] - -[[package]] -name = "derivation-path" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193388a8c8c75a490b604ff61775e236541b8975e98e5ca1f6ea97d122b7e2db" -dependencies = [ - "failure", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "digest" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b697d66081d42af4fba142d56918a3cb21dc8eb63372c6b85d14f44fb9c5979b" -dependencies = [ - "block-buffer 0.10.0", - "crypto-common", - "generic-array", - "subtle", -] - -[[package]] -name = "dir-diff" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2860407d7d7e2e004bb2128510ad9e8d669e76fa005ccf567977b5d71b8b4a0b" -dependencies = [ - "walkdir", -] - -[[package]] -name = "ed25519" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74e1069e39f1454367eb2de793ed062fac4c35c2934b76a81d90dd9abcd28816" -dependencies = [ - "signature", -] - -[[package]] -name = "ed25519-dalek" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" -dependencies = [ - "curve25519-dalek", - "ed25519", - "rand 0.7.3", - "serde", - "sha2", - "zeroize", -] - -[[package]] -name = "ed25519-dalek-bip32" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057f328f31294b5ab432e6c39642f54afd1531677d6d4ba2905932844cc242f3" -dependencies = [ - "derivation-path", - "ed25519-dalek", - "failure", - "hmac 0.9.0", - "sha2", -] - -[[package]] -name = "educe" -version = "0.4.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86b50932a01e7ec5c06160492ab660fb19b6bb2a7878030dd6cd68d21df9d4d" -dependencies = [ - "enum-ordinalize", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" - -[[package]] -name = "encoding_rs" -version = "0.8.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "enum-ordinalize" -version = "3.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b166c9e378360dd5a6666a9604bb4f54ae0cac39023ffbac425e917a2a04fef" -dependencies = [ - "num-bigint", - "num-traits", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "env_logger" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2cf0344971ee6c64c31be0d530793fba457d322dfec2810c453d0ef228f9c3" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "failure" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" -dependencies = [ - "backtrace", - "failure_derive", -] - -[[package]] -name = "failure_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "fastrand" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" -dependencies = [ - "instant", -] - -[[package]] -name = "feature-probe" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "835a3dc7d1ec9e75e2b5fb4ba75396837112d2060b03f7d43bc1897c7f7211da" - -[[package]] -name = "filetime" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "winapi", -] - -[[package]] -name = "flate2" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" -dependencies = [ - "cfg-if", - "crc32fast", - "libc", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "form_urlencoded" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" -dependencies = [ - "matches", - "percent-encoding", -] - -[[package]] -name = "fs_extra" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" - -[[package]] -name = "futures" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28560757fe2bb34e79f907794bb6b22ae8b0e5c669b638a1132f2592b19035b4" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3dda0b6588335f360afc675d0564c17a77a2bda81ca178a4b6081bd86c7f0b" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c8ff0461b82559810cdccfde3215c3f373807f5e5232b71479bff7bb2583d7" - -[[package]] -name = "futures-executor" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29d6d2ff5bb10fb95c85b8ce46538a2e5f5e7fdc755623a7d4529ab8a4ed9d2a" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f9d34af5a1aac6fb380f735fe510746c38067c5bf16c7fd250280503c971b2" - -[[package]] -name = "futures-macro" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbd947adfffb0efc70599b3ddcf7b5597bb5fa9e245eb99f62b3a5f7bb8bd3c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "futures-sink" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3055baccb68d74ff6480350f8d6eb8fcfa3aa11bdc1a1ae3afdd0514617d508" - -[[package]] -name = "futures-task" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ee7c6485c30167ce4dfb83ac568a849fe53274c831081476ee13e0dce1aad72" - -[[package]] -name = "futures-util" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b5cf40b47a271f77a8b1bec03ca09044d99d2372c0de244e66430761127164" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" -dependencies = [ - "serde", - "typenum", - "version_check", -] - -[[package]] -name = "gethostname" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4addc164932852d066774c405dbbdb7914742d2b39e39e1a7ca949c856d054d1" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.10.2+wasi-snapshot-preview1", -] - -[[package]] -name = "gimli" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78cc372d058dcf6d5ecd98510e7fbc9e5aec4d21de70f65fea8fecebcd881bd4" - -[[package]] -name = "goblin" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32401e89c6446dcd28185931a01b1093726d0356820ac744023e6850689bf926" -dependencies = [ - "log", - "plain", - "scroll", -] - -[[package]] -name = "h2" -version = "0.3.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f1f717ddc7b2ba36df7e871fd88db79326551d3d6f1fc406fbfd28b582ff8e" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hash32" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc" -dependencies = [ - "byteorder", -] - -[[package]] -name = "hashbrown" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash", -] - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hmac" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126888268dcc288495a26bf004b38c5fdbb31682f992c84ceb046a1f0fe38840" -dependencies = [ - "crypto-mac 0.8.0", - "digest 0.9.0", -] - -[[package]] -name = "hmac" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deae6d9dbb35ec2c502d62b8f7b1c000a0822c3b0794ba36b3149c0a1c840dff" -dependencies = [ - "crypto-mac 0.9.1", - "digest 0.9.0", -] - -[[package]] -name = "hmac" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" -dependencies = [ - "crypto-mac 0.11.1", - "digest 0.9.0", -] - -[[package]] -name = "hmac-drbg" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ea0a1394df5b6574da6e0c1ade9e78868c9fb0a4e5ef4428e32da4676b85b1" -dependencies = [ - "digest 0.9.0", - "generic-array", - "hmac 0.8.1", -] - -[[package]] -name = "http" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f4c6746584866f0feabcc69893c5b51beef3831656a968ed7ae254cdc4fd03" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.1", -] - -[[package]] -name = "http-body" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" - -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "hyper" -version = "0.14.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7ec3e62bdc98a2f0393a5048e4c30ef659440ea6e0e572965103e72bd836f55" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa 0.4.8", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d87c48c02e0dc5e3b849a2041db3029fd066650f8f717c07bf8ed78ccb895cac" -dependencies = [ - "http", - "hyper", - "rustls", - "tokio", - "tokio-rustls", -] - -[[package]] -name = "idna" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "index_list" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9d968042a4902e08810946fc7cd5851eb75e80301342305af755ca06cb82ce" - -[[package]] -name = "indexmap" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "ipnet" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" - -[[package]] -name = "itertools" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" -dependencies = [ - "either", -] - -[[package]] -name = "itoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" - -[[package]] -name = "itoa" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" - -[[package]] -name = "jobserver" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "keccak" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.115" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a8d982fa7a96a000f6ec4cfe966de9703eccde29750df2bb8949da91b0e818d" - -[[package]] -name = "libloading" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" -dependencies = [ - "cfg-if", - "winapi", -] - -[[package]] -name = "libsecp256k1" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9d220bc1feda2ac231cb78c3d26f27676b8cf82c96971f7aeef3d0cf2797c73" -dependencies = [ - "arrayref", - "base64 0.12.3", - "digest 0.9.0", - "hmac-drbg", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", - "rand 0.7.3", - "serde", - "sha2", - "typenum", -] - -[[package]] -name = "libsecp256k1-core" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f6ab710cec28cef759c5f18671a27dae2a5f952cdaaee1d8e2908cb2478a80" -dependencies = [ - "crunchy", - "digest 0.9.0", - "subtle", -] - -[[package]] -name = "libsecp256k1-gen-ecmult" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccab96b584d38fac86a83f07e659f0deafd0253dc096dab5a36d53efe653c5c3" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "libsecp256k1-gen-genmult" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67abfe149395e3aa1c48a2beb32b068e2334402df8181f818d3aee2b304c4f5d" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "lock_api" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "matches" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - -[[package]] -name = "memchr" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" - -[[package]] -name = "memmap2" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe3179b85e1fd8b14447cbebadb75e45a1002f541b925f0bfec366d56a81c56d" -dependencies = [ - "libc", -] - -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg", -] - -[[package]] -name = "mio" -version = "0.7.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" -dependencies = [ - "libc", - "log", - "miow", - "ntapi", - "winapi", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi", -] - -[[package]] -name = "mpl-token-metadata" -version = "1.2.10" -dependencies = [ - "arrayref", - "borsh", - "mpl-token-vault", - "num-derive", - "num-traits", - "shank", - "solana-program", - "solana-program-test", - "solana-sdk", - "spl-associated-token-account", - "spl-token", - "thiserror", -] - -[[package]] -name = "mpl-token-vault" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ade4ef15bc06a6033076c4ff28cba9b42521df5ec61211d6f419415ace2746a" -dependencies = [ - "borsh", - "num-derive", - "num-traits", - "solana-program", - "spl-token", - "thiserror", -] - -[[package]] -name = "ntapi" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" -dependencies = [ - "winapi", -] - -[[package]] -name = "num-bigint" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-derive" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "num-integer" -version = "0.1.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "720d3ea1055e4e4574c0c0b0f8c3fd4f24c4cdaf465948206dea090b57b526ad" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d992b768490d7fe0d8586d9b5745f6c49f557da6d81dc982b1d167ad4edbb21" -dependencies = [ - "proc-macro-crate 1.1.0", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "object" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5" - -[[package]] -name = "opaque-debug" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" - -[[package]] -name = "opentelemetry" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf9b1c4e9a6c4de793c632496fa490bdc0e1eea73f0c91394f7b6990935d22" -dependencies = [ - "async-trait", - "crossbeam-channel", - "futures", - "js-sys", - "lazy_static", - "percent-encoding", - "pin-project", - "rand 0.8.4", - "thiserror", -] - -[[package]] -name = "ouroboros" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f357ef82d1b4db66fbed0b8d542cbd3c22d0bf5b393b3c257b9ba4568e70c9c3" -dependencies = [ - "aliasable", - "ouroboros_macro", - "stable_deref_trait", -] - -[[package]] -name = "ouroboros_macro" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44a0b52c2cbaef7dffa5fec1a43274afe8bd2a644fa9fc50a9ef4ff0269b1257" -dependencies = [ - "Inflector", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall", - "smallvec", - "winapi", -] - -[[package]] -name = "pbkdf2" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05894bce6a1ba4be299d0c5f29563e08af2bc18bb7d48313113bed71e904739" -dependencies = [ - "crypto-mac 0.11.1", -] - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - -[[package]] -name = "pin-project" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e280fbe77cc62c91527259e9442153f4688736748d24660126286329742b4c6c" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" - -[[package]] -name = "plain" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" - -[[package]] -name = "ppv-lite86" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" - -[[package]] -name = "proc-macro-crate" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" -dependencies = [ - "toml", -] - -[[package]] -name = "proc-macro-crate" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ebace6889caf889b4d3f76becee12e90353f2b8c7d875534a71e5742f8f6f83" -dependencies = [ - "thiserror", - "toml", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "qstring" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d464fae65fff2680baf48019211ce37aaec0c78e9264c84a3e484717f965104e" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "quote" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc 0.2.0", - "rand_pcg", -] - -[[package]] -name = "rand" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.3", - "rand_hc 0.3.1", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.3", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", -] - -[[package]] -name = "rand_core" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" -dependencies = [ - "getrandom 0.2.4", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core 0.6.3", -] - -[[package]] -name = "rand_pcg" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429" -dependencies = [ - "rand_core 0.5.1", -] - -[[package]] -name = "rayon" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" -dependencies = [ - "autocfg", - "crossbeam-deque", - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" -dependencies = [ - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-utils", - "lazy_static", - "num_cpus", -] - -[[package]] -name = "redox_syscall" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex" -version = "1.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83f127d94bdbcda4c8cc2e50f6f84f4b611f69c902699ca385a39c3a75f9ff1" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b3de9ec5dc0a3417da371aab17d729997c15010e7fd24ff707773a33bddb64" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - -[[package]] -name = "reqwest" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f242f1488a539a79bac6dbe7c8609ae43b7914b7736210f239a37cccb32525" -dependencies = [ - "base64 0.13.0", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-rustls", - "ipnet", - "js-sys", - "lazy_static", - "log", - "mime", - "percent-encoding", - "pin-project-lite", - "rustls", - "rustls-pemfile", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-rustls", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots", - "winreg", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "rustls" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d37e5e2290f3e040b594b1a9e04377c2c671f1a1cfd9bfdef82106ac1c113f84" -dependencies = [ - "log", - "ring", - "sct", - "webpki", -] - -[[package]] -name = "rustls-pemfile" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eebeaeb360c87bfb72e84abdb3447159c0eaececf1bef2aecd65a8be949d1c9" -dependencies = [ - "base64 0.13.0", -] - -[[package]] -name = "rustversion" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cc38e8fa666e2de3c4aba7edeb5ffc5246c1c2ed0e3d17e560aeeba736b23f" - -[[package]] -name = "ryu" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" - -[[package]] -name = "same-file" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "scroll" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda28d4b4830b807a8b43f7b0e6b5df875311b3e7621d84577188c175b6ec1ec" -dependencies = [ - "scroll_derive", -] - -[[package]] -name = "scroll_derive" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaaae8f38bb311444cfb7f1979af0bc9240d95795f75f9ceddf6a59b79ceffa0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "sct" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "semver" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" - -[[package]] -name = "serde" -version = "1.0.136" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_bytes" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.136" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23c1ba4cf0efd44be32017709280b32d1cea5c3f1275c3b6d9e8bc54f758085" -dependencies = [ - "itoa 1.0.1", - "ryu", - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa 1.0.1", - "ryu", - "serde", -] - -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - -[[package]] -name = "sha3" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f81199417d4e5de3f04b1e871023acea7389672c4135918f05aa9cbf2f2fa809" -dependencies = [ - "block-buffer 0.9.0", - "digest 0.9.0", - "keccak", - "opaque-debug", -] - -[[package]] -name = "shank" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9cfd616747d1ce69f15e62ea30589e393115e4b884d7c1cbceb751b8753e95d" -dependencies = [ - "shank_macro", -] - -[[package]] -name = "shank_macro" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a27d3b536acc60a3ff359181d3a912d10b91539f85909447ca7d33ea0a91d56" -dependencies = [ - "proc-macro2", - "quote", - "shank_macro_impl", - "syn", -] - -[[package]] -name = "shank_macro_impl" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7466a15559fd11aeb5fe12a700923f75bb4eaa7bc90962f2bd822708698ab5d" -dependencies = [ - "anyhow", - "proc-macro2", - "quote", - "serde", - "syn", -] - -[[package]] -name = "sharded-slab" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "900fba806f70c630b0a382d0d825e17a0f19fcd059a2ade1ff237bcddf446b31" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" -dependencies = [ - "libc", -] - -[[package]] -name = "signature" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f054c6c1a6e95179d6f23ed974060dcefb2d9388bb7256900badad682c499de4" - -[[package]] -name = "slab" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" - -[[package]] -name = "smallvec" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83" - -[[package]] -name = "socket2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "solana-address-lookup-table-program" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9508c839fd7cce94b21678ad28a6529cec2ea08acaa3514554ad99b564eed2f9" -dependencies = [ - "bincode", - "bytemuck", - "log", - "num-derive", - "num-traits", - "rustc_version", - "serde", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-program-runtime", - "solana-sdk", - "thiserror", -] - -[[package]] -name = "solana-banks-client" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b02cda34ec70e84a9cf6636d8a2529531fb8cca75d31e6ebf0947662f7ee9b12" -dependencies = [ - "borsh", - "futures", - "solana-banks-interface", - "solana-program", - "solana-sdk", - "tarpc", - "thiserror", - "tokio", - "tokio-serde", -] - -[[package]] -name = "solana-banks-interface" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2fac396ff86d7c8b5a3d086c4884c9b365440857933f6557114cc5d640eb4f9" -dependencies = [ - "serde", - "solana-sdk", - "tarpc", -] - -[[package]] -name = "solana-banks-server" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9fc996ae6c27db5af34b8144102ea2e6efbdd973c201fc08a917550ef74e50" -dependencies = [ - "bincode", - "futures", - "solana-banks-interface", - "solana-runtime", - "solana-sdk", - "solana-send-transaction-service", - "tarpc", - "tokio", - "tokio-serde", - "tokio-stream", -] - -[[package]] -name = "solana-bloom" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d16ac280328dabe13f77fc15be1e8e3c719a01ae90309e9f31df6dc24011af2" -dependencies = [ - "bv", - "fnv", - "log", - "rand 0.7.3", - "rayon", - "rustc_version", - "serde", - "serde_derive", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-sdk", -] - -[[package]] -name = "solana-bpf-loader-program" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fb1e3df53df7bacecd261c0cdf0b1c6f4a84a724a1312d216ca3ee796c119b8" -dependencies = [ - "bincode", - "byteorder", - "libsecp256k1", - "log", - "solana-measure", - "solana-metrics", - "solana-program-runtime", - "solana-sdk", - "solana_rbpf", - "thiserror", -] - -[[package]] -name = "solana-bucket-map" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a97c11377eb1059d3fdddd02ad0fe0c480434f1df812f6b5d16ae7087af44a0" -dependencies = [ - "fs_extra", - "log", - "memmap2", - "rand 0.7.3", - "rayon", - "solana-logger", - "solana-measure", - "solana-sdk", - "tempfile", -] - -[[package]] -name = "solana-compute-budget-program" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe51d03e2899e00b8c32c6bd9930fc105be88b04243aa2d398b9825c9b09f34b" -dependencies = [ - "solana-program-runtime", - "solana-sdk", -] - -[[package]] -name = "solana-config-program" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416632936e7ac85e3a24925299e8c8ebc89ed190e090348e99a7158fb84551ff" -dependencies = [ - "bincode", - "chrono", - "serde", - "serde_derive", - "solana-program-runtime", - "solana-sdk", -] - -[[package]] -name = "solana-frozen-abi" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e30fc7f860ff75b2916735189534c5353db8d84953af7842f8dd9a6982dbcaaf" -dependencies = [ - "bs58", - "bv", - "generic-array", - "log", - "memmap2", - "rustc_version", - "serde", - "serde_derive", - "sha2", - "solana-frozen-abi-macro", - "solana-logger", - "thiserror", -] - -[[package]] -name = "solana-frozen-abi-macro" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e0c0121908ff5df45308b11eef48f696876064884d6aa12b70424dae7459d6" -dependencies = [ - "proc-macro2", - "quote", - "rustc_version", - "syn", -] - -[[package]] -name = "solana-logger" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd76a790f207ea9b523fb051c9d446e424e2e0b9fd539bb76c3ea797abf8ea5" -dependencies = [ - "env_logger", - "lazy_static", - "log", -] - -[[package]] -name = "solana-measure" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "405913a0367af42c58b34266d04cd8d3653f07e7ad95015e598aebdd84066169" -dependencies = [ - "log", - "solana-sdk", -] - -[[package]] -name = "solana-metrics" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c4fd520d3360d0e8c90930da09512c5e896754c66faa05763b0ca516bf6a28c" -dependencies = [ - "env_logger", - "gethostname", - "lazy_static", - "log", - "reqwest", - "solana-sdk", -] - -[[package]] -name = "solana-program" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c86be9edb9a0cb3fc44f776c6bef21d19bc5f69b0f83b3999d0d9d103e1c61" -dependencies = [ - "base64 0.13.0", - "bincode", - "bitflags", - "blake3", - "borsh", - "borsh-derive", - "bs58", - "bv", - "bytemuck", - "console_error_panic_hook", - "console_log", - "curve25519-dalek", - "getrandom 0.1.16", - "itertools", - "js-sys", - "lazy_static", - "libsecp256k1", - "log", - "num-derive", - "num-traits", - "parking_lot", - "rand 0.7.3", - "rustc_version", - "rustversion", - "serde", - "serde_bytes", - "serde_derive", - "sha2", - "sha3", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-logger", - "solana-sdk-macro", - "thiserror", - "wasm-bindgen", -] - -[[package]] -name = "solana-program-runtime" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63b54e692cc189e105f30875b2ac4279da1ffccef8fb138e55e7339a94c1e9e5" -dependencies = [ - "base64 0.13.0", - "bincode", - "itertools", - "libc", - "libloading", - "log", - "num-derive", - "num-traits", - "rustc_version", - "serde", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-logger", - "solana-measure", - "solana-sdk", - "thiserror", -] - -[[package]] -name = "solana-program-test" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4342cdd61b179a74df61f9055b4e870959966a2207f50e9584d60f9c43b74a4" -dependencies = [ - "async-trait", - "base64 0.12.3", - "bincode", - "chrono-humanize", - "log", - "serde", - "solana-banks-client", - "solana-banks-server", - "solana-bpf-loader-program", - "solana-logger", - "solana-program-runtime", - "solana-runtime", - "solana-sdk", - "solana-vote-program", - "thiserror", - "tokio", -] - -[[package]] -name = "solana-rayon-threadlimit" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c481ccd3319d37ebe6ba72c8b097247c8387912f8615702daf58919c0f86a2f9" -dependencies = [ - "lazy_static", - "num_cpus", -] - -[[package]] -name = "solana-runtime" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18bbc5aee342aeb48d03600b07b7908090376a8a66ecd6d0d2195818273cb088" -dependencies = [ - "arrayref", - "bincode", - "blake3", - "bv", - "bytemuck", - "byteorder", - "bzip2", - "crossbeam-channel", - "dashmap", - "dir-diff", - "flate2", - "fnv", - "index_list", - "itertools", - "lazy_static", - "log", - "memmap2", - "num-derive", - "num-traits", - "num_cpus", - "ouroboros", - "rand 0.7.3", - "rayon", - "regex", - "rustc_version", - "serde", - "serde_derive", - "solana-address-lookup-table-program", - "solana-bloom", - "solana-bucket-map", - "solana-compute-budget-program", - "solana-config-program", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-logger", - "solana-measure", - "solana-metrics", - "solana-program-runtime", - "solana-rayon-threadlimit", - "solana-sdk", - "solana-stake-program", - "solana-vote-program", - "symlink", - "tar", - "tempfile", - "thiserror", - "zstd", -] - -[[package]] -name = "solana-sdk" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1dc0a8e4f1dede7f0f91879a67459723e30c39018c34a1fdc30da2c4cd292fa" -dependencies = [ - "assert_matches", - "base64 0.13.0", - "bincode", - "bitflags", - "borsh", - "bs58", - "bytemuck", - "byteorder", - "chrono", - "derivation-path", - "digest 0.9.0", - "ed25519-dalek", - "ed25519-dalek-bip32", - "generic-array", - "hmac 0.11.0", - "itertools", - "js-sys", - "lazy_static", - "libsecp256k1", - "log", - "memmap2", - "num-derive", - "num-traits", - "pbkdf2", - "qstring", - "rand 0.7.3", - "rand_chacha 0.2.2", - "rustc_version", - "rustversion", - "serde", - "serde_bytes", - "serde_derive", - "serde_json", - "sha2", - "sha3", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-logger", - "solana-program", - "solana-sdk-macro", - "thiserror", - "uriparse", - "wasm-bindgen", -] - -[[package]] -name = "solana-sdk-macro" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17d66726cf3324c91601f047d34b7f9d9bf26982775f0f673655bb55df00ec87" -dependencies = [ - "bs58", - "proc-macro2", - "quote", - "rustversion", - "syn", -] - -[[package]] -name = "solana-send-transaction-service" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdee500d68920df86128854a52816afd59d137ed5d4c59979d6fa6b36ef670dd" -dependencies = [ - "log", - "solana-logger", - "solana-metrics", - "solana-runtime", - "solana-sdk", -] - -[[package]] -name = "solana-stake-program" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af6311ce5695dbc1a6cdcbffb13f1f3a0b2ccfd08442352cb487d7bf52c99b17" -dependencies = [ - "bincode", - "log", - "num-derive", - "num-traits", - "rustc_version", - "serde", - "serde_derive", - "solana-config-program", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-metrics", - "solana-program-runtime", - "solana-sdk", - "solana-vote-program", - "thiserror", -] - -[[package]] -name = "solana-vote-program" -version = "1.9.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "554d67ee6ef9b559fe14d339baec35cf465ecc0f47bbd2622a03a0169c00b0eb" -dependencies = [ - "bincode", - "log", - "num-derive", - "num-traits", - "rustc_version", - "serde", - "serde_derive", - "solana-frozen-abi", - "solana-frozen-abi-macro", - "solana-logger", - "solana-metrics", - "solana-program-runtime", - "solana-sdk", - "thiserror", -] - -[[package]] -name = "solana_rbpf" -version = "0.2.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41e138f6d6d4eb6a65f8e9f01ca620bc9907d79648d5038a69dd3f07b6ed3f1f" -dependencies = [ - "byteorder", - "combine", - "goblin", - "hash32", - "libc", - "log", - "rand 0.7.3", - "rustc-demangle", - "scroll", - "thiserror", - "time", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "spl-associated-token-account" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "393e2240d521c3dd770806bff25c2c00d761ac962be106e14e22dd912007f428" -dependencies = [ - "solana-program", - "spl-token", -] - -[[package]] -name = "spl-token" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93bfdd5bd7c869cb565c7d7635c4fafe189b988a0bdef81063cd9585c6b8dc01" -dependencies = [ - "arrayref", - "num-derive", - "num-traits", - "num_enum", - "solana-program", - "thiserror", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - -[[package]] -name = "symlink" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a" - -[[package]] -name = "syn" -version = "1.0.86" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", -] - -[[package]] -name = "tar" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" -dependencies = [ - "filetime", - "libc", - "xattr", -] - -[[package]] -name = "tarpc" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b85d0a9369a919ba0db919b142a2b704cd207dfc676f7a43c2d105d0bc225487" -dependencies = [ - "anyhow", - "fnv", - "futures", - "humantime", - "opentelemetry", - "pin-project", - "rand 0.8.4", - "serde", - "static_assertions", - "tarpc-plugins", - "thiserror", - "tokio", - "tokio-serde", - "tokio-util", - "tracing", - "tracing-opentelemetry", -] - -[[package]] -name = "tarpc-plugins" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee42b4e559f17bce0385ebf511a7beb67d5cc33c12c96b7f4e9789919d9c10f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tempfile" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" -dependencies = [ - "cfg-if", - "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", -] - -[[package]] -name = "termcolor" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thiserror" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thread_local" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5516c27b78311c50bf42c071425c560ac799b11c30b31f87e3081965fe5e0180" -dependencies = [ - "once_cell", -] - -[[package]] -name = "time" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "tinyvec" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - -[[package]] -name = "tokio" -version = "1.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d0183f6f6001549ab68f8c7585093bb732beefbcf6d23a10b9b95c73a1dd49" -dependencies = [ - "autocfg", - "bytes", - "libc", - "memchr", - "mio", - "num_cpus", - "once_cell", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "tokio-macros", - "winapi", -] - -[[package]] -name = "tokio-macros" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tokio-rustls" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a27d5f2b839802bd8267fa19b0530f5a08b9c08cd417976be2a65d130fe1c11b" -dependencies = [ - "rustls", - "tokio", - "webpki", -] - -[[package]] -name = "tokio-serde" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "911a61637386b789af998ee23f50aa30d5fd7edcec8d6d3dedae5e5815205466" -dependencies = [ - "bincode", - "bytes", - "educe", - "futures-core", - "futures-sink", - "pin-project", - "serde", - "serde_json", -] - -[[package]] -name = "tokio-stream" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" -dependencies = [ - "futures-core", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "log", - "pin-project-lite", - "slab", - "tokio", -] - -[[package]] -name = "toml" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" -dependencies = [ - "serde", -] - -[[package]] -name = "tower-service" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" - -[[package]] -name = "tracing" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" -dependencies = [ - "cfg-if", - "log", - "pin-project-lite", - "tracing-attributes", - "tracing-core", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "tracing-core" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "tracing-opentelemetry" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "599f388ecb26b28d9c1b2e4437ae019a7b336018b45ed911458cd9ebf91129f6" -dependencies = [ - "opentelemetry", - "tracing", - "tracing-core", - "tracing-subscriber", -] - -[[package]] -name = "tracing-subscriber" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" -dependencies = [ - "sharded-slab", - "thread_local", - "tracing-core", -] - -[[package]] -name = "try-lock" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" - -[[package]] -name = "typenum" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" - -[[package]] -name = "unicode-bidi" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" - -[[package]] -name = "unicode-normalization" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-xid" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "unreachable" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" -dependencies = [ - "void", -] - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "uriparse" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e515b1ada404168e145ac55afba3c42f04cf972201a8552d42e2abb17c1b7221" -dependencies = [ - "fnv", - "lazy_static", -] - -[[package]] -name = "url" -version = "2.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" -dependencies = [ - "form_urlencoded", - "idna", - "matches", - "percent-encoding", -] - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - -[[package]] -name = "walkdir" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" -dependencies = [ - "same-file", - "winapi", - "winapi-util", -] - -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" - -[[package]] -name = "wasm-bindgen" -version = "0.2.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca" -dependencies = [ - "bumpalo", - "lazy_static", - "log", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2" - -[[package]] -name = "web-sys" -version = "0.3.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.22.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "552ceb903e957524388c4d3475725ff2c8b7960922063af6ce53c9a43da07449" -dependencies = [ - "webpki", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "winreg" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" -dependencies = [ - "winapi", -] - -[[package]] -name = "xattr" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" -dependencies = [ - "libc", -] - -[[package]] -name = "zeroize" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4062c749be08d90be727e9c5895371c3a0e49b90ba2b9592dc7afda95cc2b719" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81e8f13fef10b63c06356d65d416b070798ddabcadc10d3ece0c5be9b3c7eddb" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "synstructure", -] - -[[package]] -name = "zstd" -version = "0.9.2+zstd.1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2390ea1bf6c038c39674f22d95f0564725fc06034a47129179810b2fc58caa54" -dependencies = [ - "zstd-safe", -] - -[[package]] -name = "zstd-safe" -version = "4.1.3+zstd.1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e99d81b99fb3c2c2c794e3fe56c305c63d5173a16a46b5850b07c935ffc7db79" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-sys" -version = "1.6.2+zstd.1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2daf2f248d9ea44454bfcb2516534e8b8ad2fc91bf818a1885495fc42bc8ac9f" -dependencies = [ - "cc", - "libc", -] diff --git a/token-metadata/program/Cargo.toml b/token-metadata/program/Cargo.toml index f7c6429360..9837094bac 100644 --- a/token-metadata/program/Cargo.toml +++ b/token-metadata/program/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "mpl-token-metadata" -version = "1.6.5" +version = "1.7.0-beta.2" description = "Metaplex Metadata" authors = ["Metaplex Developers "] repository = "https://github.com/metaplex-foundation/metaplex-program-library" @@ -17,23 +17,24 @@ serde-feature = ["serde", "serde_with"] num-derive = "0.3" arrayref = "0.3.6" num-traits = "0.2" -solana-program = "1.10" -mpl-token-vault = { version = "0.1.0", features = ["no-entrypoint"] } +solana-program = "1.14" spl-token = { version = "3.2.0", features = ["no-entrypoint"] } -spl-associated-token-account = { version = "~1.0.5", features = ["no-entrypoint"] } +spl-associated-token-account = { version = "1.1.1", features = ["no-entrypoint"] } +mpl-token-auth-rules = { version = "1.0", features = ["no-entrypoint"] } thiserror = "1.0" borsh = "0.9.2" shank = { version = "0.0.11" } -serde = { version = "1.0.136", optional = true } -serde_with = { version = "1.12.0", optional = true } -mpl-utils = "0.0.5" +serde = { version = "1.0.149", optional = true } +serde_with = { version = "1.14.0", optional = true } +mpl-utils = { version = "0.0.6", path="../../core/rust/utils" } +mpl-token-metadata-context-derive = { version = "0.2.1", path = "../macro" } [dev-dependencies] -solana-sdk = "1.10" -solana-program-test = "1.11.5" +rmp-serde = "1.1.1" +rooster = { git = "https://github.com/metaplex-foundation/rooster" } +solana-sdk = "1.14" +solana-program-test = "1.14" +serde = { version = "1.0.147", features = ["derive"]} [lib] crate-type = ["cdylib", "lib"] - -[profile.release] -overflow-checks = true # Enable integer overflow checks. diff --git a/token-metadata/program/ProgrammableNFTGuide.md b/token-metadata/program/ProgrammableNFTGuide.md new file mode 100644 index 0000000000..858e0d259a --- /dev/null +++ b/token-metadata/program/ProgrammableNFTGuide.md @@ -0,0 +1,451 @@ +# Programmable NFT Guide + +> **Warning** +> This is an alpha release, currently only available on devnet. +> +> * :crab: Rust crate: [v1.7.0-beta.2](https://crates.io/crates/mpl-token-metadata/1.7.0-beta.2) +> * :package: NPM package: [v2.7.0-beta.2](https://www.npmjs.com/package/@metaplex-foundation/mpl-token-metadata/v/2.7.0-beta.2) +> +> **Note:** The instructions are subject to changes. + +## 📄 Technical Summary + +In order to support assets that can have customizable behavior, a new asset class will be introduced into Token Metadata’s `Token Standard` struct. This new token standard will allow for flexible configuration of various lifecycle rules, which will be triggered at specific actions: + +* [ ] `Burn` +* [x] [`Create`](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/program/src/instruction/mod.rs#L518-L536) ([TypeScript](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/js/test/create.test.ts) | [Rust](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/program/tests/create.rs)) +* [x] [`Delegate`](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/program/src/instruction/mod.rs#L562-L585) ([TypeScript](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/js/test/delegate.test.ts) | [Rust](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/program/tests/delegate.rs)) +* [x] [`Lock`](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/program/src/instruction/mod.rs#L607-L624) ([TypeScript](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/js/test/lock.test.ts) | [Rust](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/program/tests/lock.rs)) +* [x] `Migrate` +* [ ] `Print` +* [x] [`Mint`](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/program/src/instruction/mod.rs#L538-L560) ([TypeScript](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/js/test/mint.test.ts) | [Rust](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/program/tests/mint.rs)) +* [x] [`Revoke`](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/program/src/instruction/mod.rs#L587-L605) ([TypeScript](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/js/test/revoke.test.ts) | [Rust](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/program/tests/revoke.rs)) +* [x] [`Transfer`](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/program/src/instruction/mod.rs#L661-L683) ([TypeScript](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/js/test/transfer.test.ts) | [Rust](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/program/tests/transfer.rs)) +* [x] [`Unlock`](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/program/src/instruction/mod.rs#L626-L643) ([TypeScript](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/js/test/unlock.test.ts) | [Rust](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/program/tests/unlock.rs)) +* [x] [`Update`](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/program/src/instruction/mod.rs#L685-L702) ([TypeScript](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/js/test/update.test.ts) | [Rust](https://github.com/metaplex-foundation/metaplex-program-library/blob/feat/programmable-asset/token-metadata/program/tests/update.rs)) +* [ ] `Use` +* [ ] `Verify` + +These lifecycle rules will be configured by creators – e.g., creators may choose to include rules for transfer restrictions (e.g., for royalties enforcement) or only allow updates with an additional signer present in the transaction. + +Interaction with assets will be provided by Token Metadata: + +1. Transfer instructions (and other spl-token instructions) are now sent to Token Metadata instead. +2. Token Metadata will expose new versioned instructions under an unified and simplified API. Spl-token proxy instructions are close to the existing instruction interface with the addition of a new required `authorization_rules` account argument. E.g., `CreateMetadataAccount` and `UpdateMetadata` are replaced with `Create` and `Update`. +3. The `authorization_rules` account can be easily discovered on-chain using account derivation or via the Metaplex Read API, an RPC indexing extension run by many existing RPC providers. + +## 🚛 Extending the `TokenStandard` + +Programmable Non-Fungibles (`pNFT`) are represented as a new asset class on the Token Metadata's `TokenStandard` struct: + +```rust +pub enum TokenStandard { + /// This is a master edition. + NonFungible, + /// A token with metadata that can also have attrributes. + FungibleAsset, + /// A token with simple metadata. + Fungible, + /// This is a limited edition. + NonFungibleEdition, + /// [NEW] An NFT with customizale behaviour for lifecycle events + /// (e.g. transfers, updates, etc.). + ProgrammableNonFungible, +} +``` + +When a `ProgrammableNonFungible` asset is created, it can have a `RuleSet` associated. These rules are then validated at each lifecycle action and the action is only performed if the validation succeeds. Since these rules are customizable, the tokens have a *programmable* behavior. + +## ⛩️ Unified instructions + +To interact with the new asset class, a new set of instructions will be added to Token Metadata. Note that current instructions will continue to work using the existing token standards – the new instructions are required for interacting with `ProgrammableNonFungible` assets. At the same time, the **new instructions will support all asset classes** so all interaction can happen via an unified interface regardless of the asset class. + +Token Metadata instruction will be expanded to include: + +```rust +pub enum MetadataInstruction { + .., + // Closes the accounts of an asset + Burn(BurnArgs), + // Create the metadata and associated accounts + Create(CreateArgs), + // Create a delegate + Delegate(DelegateArgs), + // Change the asset type of an asset + Migrate(MigrateArgs), + // Locks a token + Lock(LockArgs), + // Mint a token + Mint(MintArgs), + // Mint copies of a fungible asset + Print(PrintArgs), + // Revokes a delegate + Revoke(RevokeArgs), + // Transfer an asset + Transfer(TransferArgs), + // Unlocks a token + Unlock(LockArgs), + // Updates the metadata of an asset + Update(UpdateArgs), + // Authorizes the use of a token + Use(UseArgs), + // Verifies creator/collection for an asset + Verify(VerifyArgs), +} +``` + +Each of these instructions will use versioned `*Args` structs to facilitate future updates, and in turn, not require additional instructions. Operations supported under each instruction are as follows: + +- `Burn` + +- `Create` + * [x] Creation of Programmable Non-Fungibles tokens (pNFT) + * [x] Creation of Non-Fungibles tokens (NFT) + * [x] Creation of Fungible Assets (*semi-fungible tokens*) + * [x] Creation of Fungible Tokens (*fungible tokens*) + +- `Delegate` + See Delegates Section below for more information on what the difference is between types of delegates. + * [ ] Creation of `Authority` delegates + * [x] Creation of `Collection` delegates + * [x] Creation of `Sale` delegates + * [x] Creation of `Transfer` delegates + * [x] Creation of `Update` delegates + * [ ] Creation of `Use` delegates + * [x] Creation of `Utility` delegates + +- `Lock` + * [x] Lock Programmable Non-Fungibles + * [x] Lock Non-Fungibles + * [x] Lock Fungible Assets + * [x] Lock Fungibles + +- `Migrate` + * [x] Migrate Non-Fungibles to Programmable Non-Fungibles + +- `Mint` + * [x] Mint Programmable Non-Fungibles tokens (pNFT) + * [x] Mint of Non-Fungibles tokens (NFT) + * [x] Mint Fungible Assets (*semi-fungible tokens*) + * [x] Mint Fungible Tokens (*fungible tokens*) + +- `Print` + * [ ] Print of editions + +- `Revoke` + * [ ] Revoke of `Authority` delegates + * [x] Revoke of `Collection` delegates + * [x] Revoke of `Sale` delegates + * [x] Revoke of `Transfer` delegates + * [x] Revoke of `Update` delegates + * [ ] Revoke of `Use` delegates + * [x] Revoke of `Utility` delegates + +- `Transfer` + * [x] wallet-to-wallet transfers + * [x] wallet-to-program transfers + * [x] program-to-wallet transfers + * [x] program-to-program transfers + +- `Update` + * [x] Update metadata details for Programmable Non-Fungibles + * [x] Update metadata details for Non-Fungibles + * [x] Update metadata details for Fungibles Assets + * [x] Update metadata details for Fungibles + +- `Unlock` + * [x] Unlock Programmable Non-Fungibles + * [x] Unlock Non-Fungibles + * [x] Unlock Fungible Assets + * [x] Unlock Fungibles + +- `Use` + +- `Verify` + * [ ] Verify collection items + * [ ] Verify creators + +## 🏗️ Positional Optional Accounts + +The new instruction handlers support positional optional accounts, where an account can be present or not in a transaction. When a instruction is created, it is necessary to provide a list of `PublicKeys` for the instruction accounts – e.g.: +```javascript +const mintAcccounts: MintInstructionAccounts = { + token, + tokenOwner, + metadata, + masterEdition, + mint, + payer, + ... +}; +``` +In general, the accounts will be added to the transaction following a pre-defined position: +```javascript +// Accounts relative position: +0. token +1. tokenOwner +2. metadata +3. masterEdition +4. mint +5. payer +... +``` +When you are minting from a semi-fungible token, there is no need to pass a `masterEdition` account (semi-fungibles do not have a master edition account associated). If we simply omit the `masterEdition` account, the relative position of the remaining accounts (the accounts after the master edition) would change, resulting in the program logic to be inconsistent. One way to address this is to set another `PublicKey` value to represent a "not-set-value" to maintain the position but at the same time indicate that the master edition account was not set. This is accomplished by setting the Token Metadata program key as the `PublicKey` for any account that should be ommited. This is an efficient approach since: +1. The (Token Metadata) program id is already included in the transaction by default so adding another reference to it does not take the full 32 bytes of `PublicKey` – only a single byte is used in this case; +2. The relative position of accounts is maintained since there is a public key value for the account; +3. The program can easily determine if the account key represent a "valid" public key or a "not-set-value". + +Using this approach, the same handler supports a positional optinal account by just ommiting the `masterEdition`: +```javascript +const mintAcccounts: MintInstructionAccounts = { + token, + tokenOwner, + metadata, + mint, + payer, + ... +}; +``` +Under the hood, you set the Token Metadata's `PROGRAM_ID` is set as the master edition account `PublicKey`. This will inform the program that the `masterEdition` account was not set and still maintain the relative position of the remaining accounts. Token Metadata includes a Rust crate and an NPM package with instruction builders that support positional optional accounts – you only need to set the "required" accounts using these builders. + +> **Note** +> This is a similar approach used by Anchor v0.26 to support positional optional accounts + +## 🧱 Instruction Builders (Rust) + +> **Warning** +> The instruction builders examples below are a draft specification. + +Each instruction will include an instruction builder to facilitate its creation. Each instruction has an associated builder, which will validate that all required accounts are provided and set the default values for any of the optional accounts that are not set. + +### Example: `Create` instruction builder + +Creates the metadata account for a `ProgrammableNonFungible` asset, initializing the mint account if needed. + +```rust +use mpl_token_metadata::instruction::builders::CreateBuilder; + +... + +let name = puffed_out_string("Programmable NFT", MAX_NAME_LENGTH); +let symbol = puffed_out_string("PRG", MAX_SYMBOL_LENGTH); +let uri = puffed_out_string("http://first.programmable.nft/", MAX_URI_LENGTH); + +let mut asset = AssetData::new( + TokenStandard::ProgrammableNonFungible, + name.clone(), + symbol.clone(), + uri.clone(), + context.payer.pubkey(), +); +asset.seller_fee_basis_points = 500; +asset.programmable_config = Some(ProgrammableConfig { + rule_set: Pubkey::from_str("Cex6GAMtCwD9E17VsEK4rQTbmcVtSdHxWcxhwdwXkuAN").unwrap(), +}); + +... + +let create_ix = CreateBuilder::new() + .metadata(metadata) + .master_edition(master_edition) + .mint(mint) + .mint_authority(payer_pubkey) + .payer(payer_pubkey) + .update_authority(payer_pubkey) + .initialize_mint(true) + .update_authority_as_signer(true) + .build(CreateArgs::V1 { + asset_data: asset, + decimals: Some(0), + max_supply: Some(0), + })? + .instruction(); +``` + +## 👤 Delegates + +The new unified api of token metadata exposes a system of delegations where other actors can be 'delegated' powers to do specific actions on the assets or asset grouping (collection). + +> **Note:** +> For programmable NFTS, auth rules manages which actors can become any of these types of delegates. + +### Delegate Types + +There are two types of delegates on Token Metadata: `TokenDelegate` and `MetadataDelegate`. + +#### Token Delegate + +`TokenDelegate`s are delegates that operate at the token level – i.e., they are spl-token delegates. This allows the delegate to perform operations on the token account (burn, transfer, freeze). There can only be one token delegate at a time and they do not have an individual delegate account associated – their information is stored on the `TokenRecord` account. The token record holds information about a particular token account (PDA seeds `[metadata, program id, mint id, "token_record", token account id]`): +```rust +pub struct TokenRecord { + pub key: Key, + pub bump: u8, + pub state: TokenState, + pub rule_set_revision: Option, + pub delegate: Option, + pub delegate_role: Option, +} +``` + +`TokenState` has three different values and instrution are restricted depending on the token state value: + +| **Token State** | 🔓 `Unlocked` | 🔐 `Locked` | 🏠 `Listed` | +| ------------------ | --- | --- | --- | +| *Owner Transfer* | ✅ | ❌ | ❌ | +| *Delegate Transfer* | 🟠 only: Sale, Transfer | ❌ | 🟠 only: Sale | +| *Owner Burn* | ✅ | ❌ | ❌ | +| *Delegate Burn* | 🟠 only: Utility | ❌ | ❌ | +| *Owner Revoke* | ✅ | ❌ | ✅ → 🔓 `Unlocked` | +| *Owner Approve* | ✅ if Sale → 🏠 `Listed` | ❌ | ❌ | +| *Owner Unlock* | ❌ | ❌ | ❌ | +| *Delegate Unlock* | ❌ | ✅ → 🔓 `Unlocked` | ❌ | +| *Owner Lock* | ❌ | ❌ | ❌ | +| *Delegate Lock* | ✅ if Utility or Staking → 🔐 `Locked` | ❌ | ❌ | +| *Mint (destination)* | ✅ | ❌ | ✅ | + +`TokenDelegateRole` represents the different delegates types. There are six different values and instrution are restricted depending on the token delegate role and token state values: + +| **Delegate** | None | `Sale` | `Transfer` | `Utility` | `Staking` | `Migration` | `Standard` (SPL) | +| --------------------- | --- | --- | --- | --- | --- | --- | --- | +| 🔵 `NFT` or 🟣 `pNFT` | 🔵 🟣 | 🟣 | 🟣 | 🟣 | 🟣 | 🟣 (only once) | 🔵 | +| **Token State** | 🔓 `Unlocked` | 🏠 `Listed` | 🔓 `Unlocked` | 🔐 `Locked`
🔓 `Unlocked` | 🔐 `Locked`
🔓 `Unlocked` | 🔐 `Locked`
🔓 `Unlocked`| *Analogous to:* ❄️ `Frozen`
☀️ `Thawn` | +| *Owner Transfer* | ✅ | ❌ | ✅ → None | 🔓 if `Unlocked` → None |🔓 if Unlocked → None |🔓 if `Unlocked` → None|☀️ if `Thawn` → None| +| *Delegate Transfer* | N/A | ✅ → None | ✅ → None | ❌ | ❌ | 🔓 if `Unlocked` → None |☀️ if `Thawn` → None| +| *Owner Burn* | ✅ | ❌ | ✅ | 🔓 if `Unlocked` | 🔓 if `Unlocked` | 🔓 if `Unlocked` | ☀️ if `Thawn` (full burn) | +| *Delegate Burn* | N/A | ❌ | ❌ | 🔓 if `Unlocked` | ❌ | 🔓 if `Unlocked` | ☀️ if `Thawn` (only SPL token) | +| *Owner Revoke* | ❌ | ✅ → None | ✅ → None | 🔓 if `Unlocked` → None |🔓 if `Unlocked` → None|🔓 if `Unlocked` → None|☀️ if `Thawn`| +| *Owner Approve* | ✅ → `Sale`, `Transfer`, `Staking` or `Utility` | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ → `Standard` (SPL) | +| *Owner Unlock* | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| *Delegate Unlock* | N/A | ❌ | ❌ | 🔐 if `Locked` | 🔐 if `Locked` | 🔐 if `Locked` | ☀️ if `Frozen` | +| *Owner Lock* | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | +| *Delegate Lock* | N/A | ❌ | ❌ | 🔓 if `Unlocked` | 🔓 if `Unlocked` | 🔓 if `Unlocked` | ☀️ if `Thawn` | +| *Mint (destination)* | ✅ | ✅ | ✅ | 🔓 if `Unlocked` | 🔓 if `Unlocked` | 🔓 if `Unlocked` | ☀️ if `Thawn` | + +The `Migration` delegate type is a temporary delegate that is only created by the migration from `NFT` to `pNFT` and cannot be otherwise created through the `Delegate` handler. This special delegate has the same functionality as the `Utility` delegate except that it can also transfer. This allows us to assign all escrowless-style programs this delegate to preserve whatever current functionality they have. Once used, it is cleared and cannot replaced, and programs will then need to select one of the normal delegate types for future actions. + +#### Metadata Delegates + +`MetadataDelegate`s are delegates that operate at the metadata level. These delegates are represented by `MetadataDelegateRecord` PDA (seeds `["metadata", program id, mint id, delegate role, update authority id, delegate id]`) and do not have an associated spl-token delegate. There can be multiple instances of the same delegate. +```rust +pub struct MetadataDelegateRecord { + pub key: Key, + pub bump: u8, + pub mint: Pubkey, + pub delegate: Pubkey, + pub update_authority: Pubkey, +} +``` + +Currently, we have three types of metadata delegates: + +- `Collection`: can set and verify NFTs to a collection. +- `Update`: can perform updates on the metadata account. +- `Use`: allows an Actor to "use" the asset and decrement the uses counter on-chain, which is how applications can implement specific limited or tracking behaviors. + +| Delegate Type | `Collection` | `Use` | `Update` | +| --------------------- | --- | --- | --- | +| *Delegate Collection* | ✅ | ❌ | ❌ | +| *Delegate Use* | ❌ | ✅ | ❌ | +| *Delegate Update* | ❌ | ❌ | ✅ | + + +### Handling Auth Rule Set Updates with Delegates + +**Problem:** When interacting with programs, `pNFT`s have a configurable rule set that indicates which programs are allowed to interact with the asset. Given that a rule set can be edited at any point, this can cause issues for programs when rules change after they have become a delegate. The end result of this is that a `pNFT` could end up “stuck” in a contract, since the auth rules may have changed and the program has not changed to accomplish the requirements d to interact with the asset. + +**Solution:** Rule sets are stored with a revision number associated – i.e., each time an edit is performed, a new revision of the rule set is created. When a delegate is set on a `pNFT`, the rule set revision on the `pNFT` will be “locked” at the current (latest) revision and it will remain locked until the `pNFT` is transferred or the delegate is revoked. This will guarantee that the delegated program will be able to interact with the `pNFT` – the revision at the delegate point will be used to validate the actions. The “lock” on the rule set revision will also be released when a `Transfer` happens, since the delegate information gets cleared, and any further interaction will then use the latest revision of the rule set. + +## 📦 JS SDK + +Token Metadata includes a low-leve Solita-based SDK, which can be used to interact with the new API. The NPM package can be found [here](https://www.npmjs.com/package/@metaplex-foundation/mpl-token-metadata/v/2.6.0-alpha.1). + +## 🏛️ Token Authorization Rules + +There will be a separate Token Authorization Rules program that provides the ability to create and execute rules to restrict the token operations discussed above. + +### Overview + +Authorization rules are variants of a `Rule` enum that implements a `validate()` function. + +There are **Primitive Rules** and **Composed Rules** that are created by combining of one or more primitive rules: + +- **Primitive Rules:** store any accounts or data needed for evaluation, and at runtime will produce a `true` or `false` output based on accounts and a well-defined `Payload` that are passed into the `validate()` function. +- **Composed Rules:** return a `true` or `false` based on whether any or all of the primitive rules return `true`. Composed rules can then be combined into higher-level composed rules that implement more complex boolean logic. Because of the recursive definition of the `Rule` enum, calling `validate()` on a top-level composed rule will start at the top and validate at every level, down to the component primitive rules. + +More details of the Token Authorization Rules program, including examples, can be found [here](https://github.com/metaplex-foundation/mpl-token-auth-rules/blob/main/README.md). + +## 🎬 Local Setup for Testing + +The repository contains both Rust BPF and JavaScript/TypeScript. In order to setup the environment to run the tests, you will need to first clone the required repositories: + +* Token Metadata: `https://github.com/metaplex-foundation/metaplex-program-library.git` branch `feat/programmable-asset` +* Token Authorization Rules: `https://github.com/metaplex-foundation/mpl-token-auth-rules.git` +* Rooster (for BPF tests): `https://github.com/metaplex-foundation/rooster` + +This guide will assume that these repositories were cloned into a folder `$PROJECTS`. + +### BPF tests (Rust) + +To get Rust BPF tests working, you will first need to build both Token Auth Rules and Rooster programs: + +* In the folder `$PROJECTS/mpl-token-auth-rules/program` execute: + ``` + cargo build-bpf + ``` + Then, copy the generated `.so` file from `$PROJECTS/mpl-token-auth-rules/program/target/deploy` into `$PROJECTS/metaplex-program-library/token-metadata/target/deploy/` + +* In the folder `$PROJECTS/rooster/program` execute: + ``` + cargo build-bpf + ``` + Then, copy the generated `.so` file from `$PROJECTS/rooster/program/target/deploy` into `$PROJECTS/metaplex-program-library/token-metadata/target/deploy/` + +> **Note:** +> The `$PROJECTS/rooster/program/target/deploy` into `$PROJECTS/metaplex-program-library/token-metadata/target/deploy/` might not exist. In this case, you will first need to build the token metadata program. + +After building the programs, the BPF tests can be run from the folder `$PROJECTS/rooster/program/target/deploy` into `$PROJECTS/metaplex-program-library/token-metadata/program` by executing: +``` +cargo test-bpf +``` + +### JavaScript/TypeScript tests + +The JavaScript/TypeScript use [Amman](https://github.com/metaplex-foundation/amman) to start a local validatior. The first step required is to build the required programs: + +* In the folder `$PROJECTS/mpl-token-auth-rules/program` execute: + ``` + cargo build-bpf + ``` + Then, copy the generated `.so` file from `$PROJECTS/mpl-token-auth-rules/program/target/deploy` into `$PROJECTS/metaplex-program-library/test-programs/` + +* In the folder `$PROJECTS/metaplex-program-library/` execute: + ``` + ./build.sh token-metadata + ``` + This will compile Token Metadata and copy the `.so` file into `$PROJECTS/metaplex-program-library/test-programs/`. + +Then, you will need to navigate to the folder `$PROJECTS/metaplex-program-library/token-metadata/js/` and install the required dependencies: +``` +yarn install +``` +After all dependencies are installed, open a new terminal and start the Amman process. In the folder `$PROJECTS/metaplex-program-library/token-metadata/js/` execute: +``` +yarn amman:start +``` +The output will be similar to: +``` +$ amman start +Loading config from /Users/febo/Projects/metaplex-program-library/token-metadata/js/.ammanrc.js +Running validator with 2 custom program(s) and 0 remote account(s) preloaded +Launching new solana-test-validator with programs predeployed and ledger at /var/folders/xr/5y4m3g8s49qgwr1r88ctz00m0000gn/T/amman-ledger +Successfully launched Relay at :::50474 +ws error: connect ECONNREFUSED 127.0.0.1:8900 +Successfully launched MockStorageServer at :::50475 +ws error: connect ECONNREFUSED 127.0.0.1:8900 +up and running +Waiting for fees to stabilize 1... +``` +In your second terminal, navigate to the folder `$PROJECTS/metaplex-program-library/token-metadata/js/` and execute: +``` +yarn build && yarn test +``` diff --git a/token-metadata/program/src/assertions/edition.rs b/token-metadata/program/src/assertions/edition.rs index a0f936e732..6904c080c0 100644 --- a/token-metadata/program/src/assertions/edition.rs +++ b/token-metadata/program/src/assertions/edition.rs @@ -7,11 +7,11 @@ use spl_token::state::Mint; use crate::{ error::MetadataError, pda::find_master_edition_account, - state::{MasterEditionV1, EDITION, PREFIX}, + state::{TokenStandard, EDITION, PREFIX, TOKEN_STANDARD_INDEX}, }; pub fn assert_edition_is_not_mint_authority(mint_account_info: &AccountInfo) -> ProgramResult { - let mint = Mint::unpack_from_slice(*mint_account_info.try_borrow_mut_data()?)?; + let mint = Mint::unpack_from_slice(&mint_account_info.try_borrow_data()?)?; let (edition_pda, _) = find_master_edition_account(mint_account_info.key); @@ -22,6 +22,19 @@ pub fn assert_edition_is_not_mint_authority(mint_account_info: &AccountInfo) -> Ok(()) } +/// Checks that the `master_edition` is not a pNFT master edition. +pub fn assert_edition_is_not_programmable(master_edition_info: &AccountInfo) -> ProgramResult { + let edition_data = master_edition_info.data.borrow(); + + if edition_data.len() > TOKEN_STANDARD_INDEX + && edition_data[TOKEN_STANDARD_INDEX] == TokenStandard::ProgrammableNonFungible as u8 + { + return Err(MetadataError::InvalidTokenStandard.into()); + } + + Ok(()) +} + // Todo deprecate this for assert derivation pub fn assert_edition_valid( program_id: &Pubkey, @@ -41,26 +54,3 @@ pub fn assert_edition_valid( Ok(()) } - -pub fn assert_supply_invariance( - master_edition: &MasterEditionV1, - printing_mint: &Mint, - new_supply: u64, -) -> ProgramResult { - // The supply of printed tokens and the supply of the master edition should, when added, never exceed max supply. - // Every time a printed token is burned, master edition.supply goes up by 1. - if let Some(max_supply) = master_edition.max_supply { - let current_supply = printing_mint - .supply - .checked_add(master_edition.supply) - .ok_or(MetadataError::NumericalOverflowError)?; - let new_proposed_supply = current_supply - .checked_add(new_supply) - .ok_or(MetadataError::NumericalOverflowError)?; - if new_proposed_supply > max_supply { - return Err(MetadataError::PrintingWouldBreachMaximumSupply.into()); - } - } - - Ok(()) -} diff --git a/token-metadata/program/src/assertions/metadata.rs b/token-metadata/program/src/assertions/metadata.rs index 79a8ff0680..e6ba1889b2 100644 --- a/token-metadata/program/src/assertions/metadata.rs +++ b/token-metadata/program/src/assertions/metadata.rs @@ -6,9 +6,10 @@ use spl_token::state::Account; use crate::{ assertions::{assert_initialized, assert_owned_by}, error::MetadataError, + pda::PREFIX, state::{ - Creator, Data, Metadata, MAX_CREATOR_LIMIT, MAX_NAME_LENGTH, MAX_SYMBOL_LENGTH, - MAX_URI_LENGTH, + Creator, Data, Metadata, TokenRecord, TokenState, MAX_CREATOR_LIMIT, MAX_NAME_LENGTH, + MAX_SYMBOL_LENGTH, MAX_URI_LENGTH, }, }; @@ -173,6 +174,26 @@ pub fn assert_currently_holding( metadata: &Metadata, mint_info: &AccountInfo, token_account_info: &AccountInfo, +) -> ProgramResult { + assert_holding_amount( + program_id, + owner_info, + metadata_info, + metadata, + mint_info, + token_account_info, + 1, + ) +} + +pub fn assert_holding_amount( + program_id: &Pubkey, + owner_info: &AccountInfo, + metadata_info: &AccountInfo, + metadata: &Metadata, + mint_info: &AccountInfo, + token_account_info: &AccountInfo, + amount: u64, ) -> ProgramResult { assert_owned_by(metadata_info, program_id)?; assert_owned_by(mint_info, &spl_token::id())?; @@ -189,7 +210,7 @@ pub fn assert_currently_holding( return Err(MetadataError::MintMismatch.into()); } - if token_account.amount < 1 { + if token_account.amount < amount { return Err(MetadataError::NotEnoughTokens.into()); } @@ -198,3 +219,43 @@ pub fn assert_currently_holding( } Ok(()) } + +pub fn assert_metadata_valid( + program_id: &Pubkey, + mint_pubkey: &Pubkey, + metadata_account_info: &AccountInfo, +) -> ProgramResult { + let seeds = &[PREFIX.as_bytes(), program_id.as_ref(), mint_pubkey.as_ref()]; + let (metadata_pubkey, _) = Pubkey::find_program_address(seeds, program_id); + if metadata_pubkey != *metadata_account_info.key { + return Err(MetadataError::InvalidMetadataKey.into()); + } + + Ok(()) +} + +pub fn assert_state(token_record: &TokenRecord, state: TokenState) -> ProgramResult { + match state { + TokenState::Locked => { + if !token_record.is_locked() { + return Err(MetadataError::UnlockedToken.into()); + } + } + TokenState::Unlocked => { + if token_record.is_locked() { + return Err(MetadataError::LockedToken.into()); + } + } + TokenState::Listed => { + if !matches!(token_record.state, TokenState::Listed) { + return Err(MetadataError::IncorrectTokenState.into()); + } + } + } + + Ok(()) +} + +pub fn assert_not_locked(token_record: &TokenRecord) -> ProgramResult { + assert_state(token_record, TokenState::Unlocked) +} diff --git a/token-metadata/program/src/assertions/misc.rs b/token-metadata/program/src/assertions/misc.rs index cc6eab6586..17aaf9dacc 100644 --- a/token-metadata/program/src/assertions/misc.rs +++ b/token-metadata/program/src/assertions/misc.rs @@ -1,3 +1,4 @@ +use mpl_utils::cmp_pubkeys; use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, @@ -9,7 +10,18 @@ use solana_program::{ }; use spl_token::state::Account; -use crate::error::MetadataError; +use crate::{ + error::MetadataError, + state::{TokenDelegateRole, TokenRecord}, +}; + +pub fn assert_keys_equal(key1: &Pubkey, key2: &Pubkey) -> Result<(), ProgramError> { + if !cmp_pubkeys(key1, key2) { + Err(MetadataError::KeyMismatch.into()) + } else { + Ok(()) + } +} /// assert initialized account pub fn assert_initialized( @@ -107,3 +119,22 @@ pub fn assert_token_program_matches_package(token_program_info: &AccountInfo) -> pub fn assert_rent_exempt(rent: &Rent, account_info: &AccountInfo) -> ProgramResult { mpl_utils::assert_rent_exempt(rent, account_info, MetadataError::NotRentExempt) } + +pub fn assert_delegate( + delegate: &Pubkey, + role: TokenDelegateRole, + token_record: &TokenRecord, +) -> ProgramResult { + if let TokenRecord { + delegate: Some(token_delegate), + delegate_role: Some(delegate_role), + .. + } = token_record + { + if cmp_pubkeys(delegate, token_delegate) && role == *delegate_role { + return Ok(()); + } + } + + Err(MetadataError::InvalidDelegate.into()) +} diff --git a/token-metadata/program/src/assertions/mod.rs b/token-metadata/program/src/assertions/mod.rs index d503e55ca3..27f9717abf 100644 --- a/token-metadata/program/src/assertions/mod.rs +++ b/token-metadata/program/src/assertions/mod.rs @@ -4,4 +4,5 @@ pub mod collection; pub mod edition; pub mod metadata; mod misc; +pub(crate) mod programmable; pub mod uses; diff --git a/token-metadata/program/src/assertions/programmable.rs b/token-metadata/program/src/assertions/programmable.rs new file mode 100644 index 0000000000..ce91aa05a8 --- /dev/null +++ b/token-metadata/program/src/assertions/programmable.rs @@ -0,0 +1,31 @@ +use mpl_utils::cmp_pubkeys; +use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult}; + +use crate::{error::MetadataError, state::ProgrammableConfig}; + +/// If a programmable rule set is present, then we need: +/// 1. authorization rules and data +/// 2. edition account +/// 3. rule_set passed in by the user to match that stored in the metadata +pub(crate) fn assert_valid_authorization<'info>( + authorization_rules: Option<&AccountInfo<'info>>, + config: &ProgrammableConfig, +) -> ProgramResult { + let rules = match authorization_rules { + Some(rules) => rules, + None => { + return Err(MetadataError::MissingAuthorizationRules.into()); + } + }; + + if let ProgrammableConfig::V1 { + rule_set: Some(rule_set), + } = config + { + if cmp_pubkeys(rule_set, rules.key) { + return Ok(()); + } + } + + Err(MetadataError::InvalidAuthorizationRules.into()) +} diff --git a/token-metadata/program/src/error.rs b/token-metadata/program/src/error.rs index f917a3cad5..0e022bb9e6 100644 --- a/token-metadata/program/src/error.rs +++ b/token-metadata/program/src/error.rs @@ -309,7 +309,7 @@ pub enum MetadataError { #[error("Is Mutable can only be flipped to false")] IsMutableCanOnlyBeFlippedToFalse, - #[error("Cannont Verify Collection in this Instruction")] + #[error("Collection cannot be verified in this instruction")] CollectionCannotBeVerifiedInThisInstruction, #[error("This instruction was deprecated in a previous release and is now removed")] @@ -442,7 +442,7 @@ pub enum MetadataError { BorshDeserializationError, /// 112 - Cannot update a verified colleciton in this command - #[error("Cannot update a verified colleciton in this command")] + #[error("Cannot update a verified collection in this command")] CannotUpdateVerifiedCollection, /// 113 - Edition Account Doesnt Match Collection @@ -524,10 +524,149 @@ pub enum MetadataError { /// 132 #[error("Invalid bubblegum signer")] InvalidBubblegumSigner, - - /// 132 + /// 133 #[error("Escrow parent cannot have a delegate")] EscrowParentHasDelegate, + + /// 134 + #[error("Mint needs to be signer to initialize the account")] + MintIsNotSigner, + + /// 135 + #[error("Invalid token standard")] + InvalidTokenStandard, + + /// 136 + #[error("Invalid mint account for specified token standard")] + InvalidMintForTokenStandard, + + /// 137 + #[error("Invalid authorization rules account")] + InvalidAuthorizationRules, + + /// 138 + #[error("Missing authorization rules account")] + MissingAuthorizationRules, + + /// 139 + #[error("Missing programmable configuration")] + MissingProgrammableConfig, + + /// 140 + #[error("Invalid programmable configuration")] + InvalidProgrammableConfig, + + /// 141 + #[error("Delegate already exists")] + DelegateAlreadyExists, + + /// 142 + #[error("Delegate not found")] + DelegateNotFound, + + /// 143 + #[error("Required account not set in instruction builder")] + MissingAccountInBuilder, + + /// 144 + #[error("Required argument not set in instruction builder")] + MissingArgumentInBuilder, + + /// 145 + #[error("Feature not supported currently")] + FeatureNotSupported, + + /// 146 + #[error("Invalid system wallet")] + InvalidSystemWallet, + + /// 147 + #[error("Only the sale delegate can transfer while its set")] + OnlySaleDelegateCanTransfer, + + /// 148 + #[error("Missing token account")] + MissingTokenAccount, + + /// 149 + #[error("Missing SPL token program")] + MissingSplTokenProgram, + + /// 150 + #[error("Missing authorization rules program")] + MissingAuthorizationRulesProgram, + + /// 151 + #[error("Invalid delegate role for transfer")] + InvalidDelegateRoleForTransfer, + + /// 152 + #[error("Invalid transfer authority")] + InvalidTransferAuthority, + + /// 153 + #[error("Instruction not supported for ProgrammableNonFungible assets")] + InstructionNotSupported, + + /// 154 + #[error("Public key does not match expected value")] + KeyMismatch, + + /// 155 + #[error("Token is locked")] + LockedToken, + + /// 156 + #[error("Token is unlocked")] + UnlockedToken, + + /// 157 + #[error("Missing delegate role")] + MissingDelegateRole, + + /// 158 + #[error("Invalid authority type")] + InvalidAuthorityType, + + /// 159 + #[error("Missing token record account")] + MissingTokenRecord, + + /// 160 + #[error("Mint supply must be zero for programmable assets")] + MintSupplyMustBeZero, + + /// 161 + #[error("Data is empty or zeroed")] + DataIsEmptyOrZeroed, + + /// 162 + #[error("Missing token owner")] + MissingTokenOwnerAccount, + + /// 163 + #[error("Master edition account has an invalid length")] + InvalidMasterEditionAccountLength, + + /// 164 + #[error("Incorrect token state")] + IncorrectTokenState, + + /// 165 + #[error("Invalid delegate role")] + InvalidDelegateRole, + + /// 166 + #[error("Print supply is required for non-fungibles")] + MissingPrintSupply, + + /// 167 + #[error("Missing master edition account")] + MissingMasterEditionAccount, + + /// 168 + #[error("Amount must be greater than zero")] + AmountMustBeGreaterThanZero, } impl PrintProgramError for MetadataError { diff --git a/token-metadata/program/src/instruction/burn.rs b/token-metadata/program/src/instruction/burn.rs index e077fbcea5..4f27ff40c3 100644 --- a/token-metadata/program/src/instruction/burn.rs +++ b/token-metadata/program/src/instruction/burn.rs @@ -1,10 +1,12 @@ -use borsh::BorshSerialize; +use borsh::{BorshDeserialize, BorshSerialize}; +#[cfg(feature = "serde-feature")] +use serde::{Deserialize, Serialize}; use solana_program::{ instruction::{AccountMeta, Instruction}, pubkey::Pubkey, }; -use crate::instruction::MetadataInstruction; +use crate::{instruction::MetadataInstruction, processor::AuthorizationData}; ///# Burn Edition NFT /// @@ -95,3 +97,13 @@ pub fn burn_nft( data: MetadataInstruction::BurnNft.try_to_vec().unwrap(), } } + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub enum BurnArgs { + V1 { + /// Required authorization data to validate the request. + authorization_data: Option, + }, +} diff --git a/token-metadata/program/src/instruction/collection.rs b/token-metadata/program/src/instruction/collection.rs index da757eef95..eaf5205050 100644 --- a/token-metadata/program/src/instruction/collection.rs +++ b/token-metadata/program/src/instruction/collection.rs @@ -6,7 +6,7 @@ use solana_program::{ pubkey::Pubkey, }; -use crate::instruction::MetadataInstruction; +use crate::{instruction::MetadataInstruction, processor::AuthorizationData}; ///# Approve Collection Authority /// @@ -406,3 +406,13 @@ pub fn verify_sized_collection_item( .unwrap(), } } + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub enum VerifyArgs { + V1 { + /// Required authorization data to validate the request. + authorization_data: Option, + }, +} diff --git a/token-metadata/program/src/instruction/delegate.rs b/token-metadata/program/src/instruction/delegate.rs new file mode 100644 index 0000000000..17aae18f1c --- /dev/null +++ b/token-metadata/program/src/instruction/delegate.rs @@ -0,0 +1,198 @@ +use std::fmt; + +use borsh::{BorshDeserialize, BorshSerialize}; +#[cfg(feature = "serde-feature")] +use serde::{Deserialize, Serialize}; +use solana_program::instruction::{AccountMeta, Instruction}; + +use super::InstructionBuilder; +use crate::{instruction::MetadataInstruction, processor::AuthorizationData}; + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub enum DelegateArgs { + CollectionV1 { + /// Required authorization data to validate the request. + authorization_data: Option, + }, + SaleV1 { + amount: u64, + /// Required authorization data to validate the request. + authorization_data: Option, + }, + TransferV1 { + amount: u64, + /// Required authorization data to validate the request. + authorization_data: Option, + }, + UpdateV1 { + /// Required authorization data to validate the request. + authorization_data: Option, + }, + UtilityV1 { + amount: u64, + /// Required authorization data to validate the request. + authorization_data: Option, + }, + StakingV1 { + amount: u64, + /// Required authorization data to validate the request. + authorization_data: Option, + }, + StandardV1 { + amount: u64, + }, +} + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub enum RevokeArgs { + CollectionV1, + SaleV1, + TransferV1, + UpdateV1, + UtilityV1, + StakingV1, + StandardV1, +} + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone, Copy)] +pub enum MetadataDelegateRole { + Authority, + Collection, + Use, + Update, +} + +impl fmt::Display for MetadataDelegateRole { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let message = match self { + Self::Authority => "authority_delegate".to_string(), + Self::Collection => "collection_delegate".to_string(), + Self::Use => "use_delegate".to_string(), + Self::Update => "update_delegate".to_string(), + }; + + write!(f, "{}", message) + } +} + +/// Delegates an action over an asset to a specific account. +/// +/// # Accounts: +/// +/// 0. `[writable]` Delegate record account +/// 1. `[]` Delegated owner +/// 2. `[writable]` Metadata account +/// 3. `[optional]` Master Edition account +/// 4. `[]` Mint account +/// 5. `[optional, writable]` Token account +/// 6. `[signer]` Approver (update authority or token owner) to approve the delegation +/// 7. `[signer, writable]` Payer +/// 8. `[]` System Program +/// 9. `[]` Instructions sysvar account +/// 10. `[optional]` SPL Token Program +/// 11. `[optional]` Token Authorization Rules program +/// 12. `[optional]` Token Authorization Rules account +impl InstructionBuilder for super::builders::Delegate { + fn instruction(&self) -> solana_program::instruction::Instruction { + let accounts = vec![ + if let Some(delegate_record) = self.delegate_record { + AccountMeta::new(delegate_record, false) + } else { + AccountMeta::new_readonly(crate::ID, false) + }, + AccountMeta::new_readonly(self.delegate, false), + AccountMeta::new(self.metadata, false), + AccountMeta::new_readonly(self.master_edition.unwrap_or(crate::ID), false), + if let Some(token_record) = self.token_record { + AccountMeta::new(token_record, false) + } else { + AccountMeta::new_readonly(crate::ID, false) + }, + AccountMeta::new_readonly(self.mint, false), + if let Some(token) = self.token { + AccountMeta::new(token, false) + } else { + AccountMeta::new_readonly(crate::ID, false) + }, + AccountMeta::new_readonly(self.authority, true), + AccountMeta::new(self.payer, true), + AccountMeta::new_readonly(self.system_program, false), + AccountMeta::new_readonly(self.sysvar_instructions, false), + AccountMeta::new_readonly(self.spl_token_program.unwrap_or(crate::ID), false), + AccountMeta::new_readonly(self.authorization_rules_program.unwrap_or(crate::ID), false), + AccountMeta::new_readonly(self.authorization_rules.unwrap_or(crate::ID), false), + ]; + + Instruction { + program_id: crate::ID, + accounts, + data: MetadataInstruction::Delegate(self.args.clone()) + .try_to_vec() + .unwrap(), + } + } +} + +/// Revokes a delegate. +/// +/// # Accounts: +/// +/// 0. `[writable]` Delegate record account +/// 1. `[]` Delegated owner +/// 2. `[writable]` Metadata account +/// 3. `[optional]` Master Edition account +/// 4. `[]` Mint account +/// 5. `[optional, writable]` Token account +/// 6. `[signer]` Authority (update authority, token owner or delegate) of the revoke +/// 7. `[signer, writable]` Payer +/// 8. `[]` System Program +/// 9. `[]` Instructions sysvar account +/// 10. `[optional]` SPL Token Program +/// 11. `[optional]` Token Authorization Rules program +/// 12. `[optional]` Token Authorization Rules account +impl InstructionBuilder for super::builders::Revoke { + fn instruction(&self) -> solana_program::instruction::Instruction { + let accounts = vec![ + if let Some(delegate_record) = self.delegate_record { + AccountMeta::new(delegate_record, false) + } else { + AccountMeta::new_readonly(crate::ID, false) + }, + AccountMeta::new_readonly(self.delegate, false), + AccountMeta::new(self.metadata, false), + AccountMeta::new_readonly(self.master_edition.unwrap_or(crate::ID), false), + if let Some(token_record) = self.token_record { + AccountMeta::new(token_record, false) + } else { + AccountMeta::new_readonly(crate::ID, false) + }, + AccountMeta::new_readonly(self.mint, false), + if let Some(token) = self.token { + AccountMeta::new(token, false) + } else { + AccountMeta::new_readonly(crate::ID, false) + }, + AccountMeta::new_readonly(self.authority, true), + AccountMeta::new(self.payer, true), + AccountMeta::new_readonly(self.system_program, false), + AccountMeta::new_readonly(self.sysvar_instructions, false), + AccountMeta::new_readonly(self.spl_token_program.unwrap_or(crate::ID), false), + AccountMeta::new_readonly(self.authorization_rules_program.unwrap_or(crate::ID), false), + AccountMeta::new_readonly(self.authorization_rules.unwrap_or(crate::ID), false), + ]; + + Instruction { + program_id: crate::ID, + accounts, + data: MetadataInstruction::Revoke(self.args.clone()) + .try_to_vec() + .unwrap(), + } + } +} diff --git a/token-metadata/program/src/instruction/metadata.rs b/token-metadata/program/src/instruction/metadata.rs index e20d572c3f..9b1ad44995 100644 --- a/token-metadata/program/src/instruction/metadata.rs +++ b/token-metadata/program/src/instruction/metadata.rs @@ -9,11 +9,20 @@ use { serde_with::{As, DisplayFromStr}, }; +use super::InstructionBuilder; use crate::{ instruction::MetadataInstruction, - state::{Collection, CollectionDetails, Creator, DataV2, Uses}, + processor::AuthorizationData, + state::{ + AssetData, Collection, CollectionDetails, Creator, Data, DataV2, MigrationType, + PrintSupply, Uses, + }, }; +//----------------------+ +// Instruction args | +//----------------------+ + #[repr(C)] #[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] #[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] @@ -27,6 +36,264 @@ pub struct CreateMetadataAccountArgsV3 { pub collection_details: Option, } +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub enum CreateArgs { + V1 { + asset_data: AssetData, + decimals: Option, + print_supply: Option, + }, +} + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub enum MintArgs { + V1 { + amount: u64, + /// Required authorization data to validate the request. + authorization_data: Option, + }, +} + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub enum TransferArgs { + V1 { + amount: u64, + /// Required authorization data to validate the request. + authorization_data: Option, + }, +} + +/// Struct representing the values to be updated for an `update` instructions. +/// +/// Values that are set to 'None' are not changed; any value set to `Some(_)` will +/// have its value updated. There are properties that have three valid states, which +/// allow the value to remaing the same, to be cleared or to set a new value. +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub enum UpdateArgs { + V1 { + /// The new update authority. + new_update_authority: Option, + /// The metadata details. + data: Option, + /// Indicates whether the primary sale has happened or not (once set to `true`, it cannot be + /// changed back). + primary_sale_happened: Option, + // Indicates Whether the data struct is mutable or not (once set to `true`, it cannot be + /// changed back). + is_mutable: Option, + /// Collection information. + collection: CollectionToggle, + /// Additional details of the collection. + collection_details: CollectionDetailsToggle, + /// Uses information. + uses: UsesToggle, + // Programmable rule set configuration (only applicable to `Programmable` asset types). + rule_set: RuleSetToggle, + /// Required authorization data to validate the request. + authorization_data: Option, + }, +} + +impl Default for UpdateArgs { + fn default() -> Self { + Self::V1 { + authorization_data: None, + new_update_authority: None, + data: None, + primary_sale_happened: None, + is_mutable: None, + collection: CollectionToggle::None, + uses: UsesToggle::None, + collection_details: CollectionDetailsToggle::None, + rule_set: RuleSetToggle::None, + } + } +} + +//-- Toggle implementations + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub enum CollectionToggle { + None, + Clear, + Set(Collection), +} + +impl CollectionToggle { + pub fn is_some(&self) -> bool { + matches!(self, CollectionToggle::Clear | CollectionToggle::Set(_)) + } + + pub fn is_none(&self) -> bool { + matches!(self, CollectionToggle::None) + } + + pub fn is_clear(&self) -> bool { + matches!(self, CollectionToggle::Clear) + } + + pub fn is_set(&self) -> bool { + matches!(self, CollectionToggle::Set(_)) + } + + pub fn to_option(self) -> Option { + match self { + CollectionToggle::Set(value) => Some(value), + CollectionToggle::Clear => None, + CollectionToggle::None => panic!("Tried to convert 'None' value"), + } + } +} + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub enum UsesToggle { + None, + Clear, + Set(Uses), +} + +impl UsesToggle { + pub fn is_some(&self) -> bool { + matches!(self, UsesToggle::Clear | UsesToggle::Set(_)) + } + + pub fn is_none(&self) -> bool { + matches!(self, UsesToggle::None) + } + + pub fn is_clear(&self) -> bool { + matches!(self, UsesToggle::Clear) + } + + pub fn is_set(&self) -> bool { + matches!(self, UsesToggle::Set(_)) + } + + pub fn to_option(self) -> Option { + match self { + UsesToggle::Set(value) => Some(value), + UsesToggle::Clear => None, + UsesToggle::None => panic!("Tried to convert 'None' value"), + } + } +} + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub enum CollectionDetailsToggle { + None, + Clear, + Set(CollectionDetails), +} + +impl CollectionDetailsToggle { + pub fn is_some(&self) -> bool { + matches!( + self, + CollectionDetailsToggle::Clear | CollectionDetailsToggle::Set(_) + ) + } + + pub fn is_none(&self) -> bool { + matches!(self, CollectionDetailsToggle::None) + } + + pub fn is_clear(&self) -> bool { + matches!(self, CollectionDetailsToggle::Clear) + } + + pub fn is_set(&self) -> bool { + matches!(self, CollectionDetailsToggle::Set(_)) + } + + pub fn to_option(self) -> Option { + match self { + CollectionDetailsToggle::Set(value) => Some(value), + CollectionDetailsToggle::Clear => None, + CollectionDetailsToggle::None => panic!("Tried to convert 'None' value"), + } + } +} + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub enum RuleSetToggle { + None, + Clear, + Set(Pubkey), +} + +impl RuleSetToggle { + pub fn is_some(&self) -> bool { + matches!(self, RuleSetToggle::Clear | RuleSetToggle::Set(_)) + } + + pub fn is_none(&self) -> bool { + matches!(self, RuleSetToggle::None) + } + + pub fn is_clear(&self) -> bool { + matches!(self, RuleSetToggle::Clear) + } + + pub fn is_set(&self) -> bool { + matches!(self, RuleSetToggle::Set(_)) + } + + pub fn to_option(self) -> Option { + match self { + RuleSetToggle::Set(t) => Some(t), + RuleSetToggle::Clear => None, + RuleSetToggle::None => panic!("Tried to convert 'None' value"), + } + } +} + +//-- End Toggle implementation + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub enum MigrateArgs { + V1 { + migration_type: MigrationType, + rule_set: Option, + }, +} + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +/// Args for update call +pub struct UpdateMetadataAccountArgsV2 { + pub data: Option, + #[cfg_attr( + feature = "serde-feature", + serde(with = "As::>") + )] + pub update_authority: Option, + pub primary_sale_happened: Option, + pub is_mutable: Option, +} + +//----------------------+ +// Instruction builders | +//----------------------+ + ///# Create Metadata Accounts V3 -- Supports v1.3 Collection Details /// ///Create a new Metadata Account @@ -155,21 +422,6 @@ pub fn sign_metadata(program_id: Pubkey, metadata: Pubkey, creator: Pubkey) -> I } } -#[repr(C)] -#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] -#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] -/// Args for update call -pub struct UpdateMetadataAccountArgsV2 { - pub data: Option, - #[cfg_attr( - feature = "serde-feature", - serde(with = "As::>") - )] - pub update_authority: Option, - pub primary_sale_happened: Option, - pub is_mutable: Option, -} - // update metadata account v2 instruction pub fn update_metadata_accounts_v2( program_id: Pubkey, @@ -217,3 +469,255 @@ pub fn update_primary_sale_happened_via_token( .unwrap(), } } + +//-- Instruction Builders trait implementation + +/// Builds the instruction to create metadata and associated accounts. +/// +/// # Accounts: +/// +/// 0. `[writable]` Metadata account +/// 1. `[optional, writable]` Master edition account +/// 2. `[writable]` Mint account +/// 3. `[signer]` Mint authority +/// 4. `[signer]` Payer +/// 5. `[signer]` Update authority +/// 6. `[]` System program +/// 7. `[]` Instructions sysvar account +/// 8. `[]` SPL Token program +impl InstructionBuilder for super::builders::Create { + fn instruction(&self) -> solana_program::instruction::Instruction { + let accounts = vec![ + AccountMeta::new(self.metadata, false), + // checks whether we have a master edition + if let Some(master_edition) = self.master_edition { + AccountMeta::new(master_edition, false) + } else { + AccountMeta::new_readonly(crate::ID, false) + }, + AccountMeta::new(self.mint, self.initialize_mint), + AccountMeta::new_readonly(self.authority, true), + AccountMeta::new(self.payer, true), + AccountMeta::new_readonly(self.update_authority, self.update_authority_as_signer), + AccountMeta::new_readonly(self.system_program, false), + AccountMeta::new_readonly(self.sysvar_instructions, false), + AccountMeta::new_readonly(self.spl_token_program, false), + ]; + + Instruction { + program_id: crate::ID, + accounts, + data: MetadataInstruction::Create(self.args.clone()) + .try_to_vec() + .unwrap(), + } + } +} + +impl InstructionBuilder for super::builders::Migrate { + fn instruction(&self) -> solana_program::instruction::Instruction { + let mut accounts = vec![ + AccountMeta::new(self.metadata, false), + AccountMeta::new(self.edition, false), + AccountMeta::new(self.token, false), + AccountMeta::new_readonly(self.token_owner, false), + AccountMeta::new_readonly(self.mint, false), + AccountMeta::new(self.payer, true), + AccountMeta::new_readonly(self.authority, true), + AccountMeta::new_readonly(self.collection_metadata, false), + AccountMeta::new_readonly(self.delegate_record, false), + AccountMeta::new(self.token_record, false), + AccountMeta::new_readonly(self.system_program, false), + AccountMeta::new_readonly(self.sysvar_instructions, false), + AccountMeta::new_readonly(self.spl_token_program, false), + ]; + + // Optional authorization rules accounts + if let Some(rules) = &self.authorization_rules { + accounts.push(AccountMeta::new_readonly(mpl_token_auth_rules::ID, false)); + accounts.push(AccountMeta::new_readonly(*rules, false)); + } else { + accounts.push(AccountMeta::new_readonly(crate::ID, false)); + accounts.push(AccountMeta::new_readonly(crate::ID, false)); + } + + Instruction { + program_id: crate::ID, + accounts, + data: MetadataInstruction::Migrate(self.args.clone()) + .try_to_vec() + .unwrap(), + } + } +} + +/// Builds the instruction to mint a token. +/// +/// # Accounts: +/// +/// 0. `[writable]` Token account key +/// 1. `[optional]` Owner of the token account +/// 2. `[]` Metadata account key (pda of ['metadata', program id, mint id])")] +/// 3. `[optional]` Master Edition account +/// 4. `[writable]` Mint of token asset +/// 5. `[signer]` Authority (mint authority or metadata's update authority for NonFungible asests) +/// 6. `[signer, writable]` Payer +/// 7. `[]` System program +/// 8. `[]` Instructions sysvar account +/// 9. `[]` SPL Token program +/// 10. `[]` SPL Associated Token Account program +/// 11. `[optional]` Token Authorization Rules program +/// 12. `[optional]` Token Authorization Rules account +impl InstructionBuilder for super::builders::Mint { + fn instruction(&self) -> solana_program::instruction::Instruction { + let mut accounts = vec![ + AccountMeta::new(self.token, false), + AccountMeta::new_readonly(self.token_owner.unwrap_or(crate::ID), false), + AccountMeta::new_readonly(self.metadata, false), + AccountMeta::new_readonly(self.master_edition.unwrap_or(crate::ID), false), + if let Some(token_record) = self.token_record { + AccountMeta::new(token_record, false) + } else { + AccountMeta::new_readonly(crate::ID, false) + }, + AccountMeta::new(self.mint, false), + AccountMeta::new_readonly(self.authority, true), + AccountMeta::new_readonly(self.delegate_record.unwrap_or(crate::ID), false), + AccountMeta::new(self.payer, true), + AccountMeta::new_readonly(self.system_program, false), + AccountMeta::new_readonly(self.sysvar_instructions, false), + AccountMeta::new_readonly(self.spl_token_program, false), + AccountMeta::new_readonly(self.spl_ata_program, false), + ]; + // Optional authorization rules accounts + if let Some(rules) = &self.authorization_rules { + accounts.push(AccountMeta::new_readonly(mpl_token_auth_rules::ID, false)); + accounts.push(AccountMeta::new_readonly(*rules, false)); + } else { + accounts.push(AccountMeta::new_readonly(crate::ID, false)); + accounts.push(AccountMeta::new_readonly(crate::ID, false)); + } + + Instruction { + program_id: crate::ID, + accounts, + data: MetadataInstruction::Mint(self.args.clone()) + .try_to_vec() + .unwrap(), + } + } +} + +/// Transfer tokens from a token account. +/// +/// # Accounts: +/// +/// 0. `[writable]` Token account +/// 1. `[]` Token account owner +/// 2. `[writable]` Destination token account +/// 3. `[]` Destination token account owner +/// 4. `[]` Mint of token asset +/// 5. `[writable]` Metadata account +/// 6. `[optional]` Edition of token asset +/// 7. `[signer] Transfer authority (token or delegate owner) +/// 8. `[optional, writable]` Delegate record PDA +/// 9. `[signer, writable]` Payer +/// 10. `[]` System Program +/// 11. `[]` Instructions sysvar account +/// 12. `[]` SPL Token Program +/// 13. `[]` SPL Associated Token Account program +/// 14. `[optional]` Token Authorization Rules Program +/// 15. `[optional]` Token Authorization Rules account +impl InstructionBuilder for super::builders::Transfer { + fn instruction(&self) -> solana_program::instruction::Instruction { + let mut accounts = vec![ + AccountMeta::new(self.token, false), + AccountMeta::new_readonly(self.token_owner, false), + AccountMeta::new(self.destination, false), + AccountMeta::new_readonly(self.destination_owner, false), + AccountMeta::new_readonly(self.mint, false), + AccountMeta::new(self.metadata, false), + AccountMeta::new_readonly(self.edition.unwrap_or(crate::ID), false), + if let Some(owner_token_record) = self.owner_token_record { + AccountMeta::new(owner_token_record, false) + } else { + AccountMeta::new_readonly(crate::ID, false) + }, + if let Some(destination_token_record) = self.destination_token_record { + AccountMeta::new(destination_token_record, false) + } else { + AccountMeta::new_readonly(crate::ID, false) + }, + AccountMeta::new_readonly(self.authority, true), + AccountMeta::new(self.payer, true), + AccountMeta::new_readonly(self.system_program, false), + AccountMeta::new_readonly(self.sysvar_instructions, false), + AccountMeta::new_readonly(self.spl_token_program, false), + AccountMeta::new_readonly(self.spl_ata_program, false), + ]; + // Optional authorization rules accounts + if let Some(rules) = &self.authorization_rules { + accounts.push(AccountMeta::new_readonly( + self.authorization_rules_program.unwrap_or(crate::ID), + false, + )); + accounts.push(AccountMeta::new_readonly(*rules, false)); + } else { + accounts.push(AccountMeta::new_readonly(crate::ID, false)); + accounts.push(AccountMeta::new_readonly(crate::ID, false)); + } + + Instruction { + program_id: crate::ID, + accounts, + data: MetadataInstruction::Transfer(self.args.clone()) + .try_to_vec() + .unwrap(), + } + } +} + +impl InstructionBuilder for super::builders::Update { + fn instruction(&self) -> solana_program::instruction::Instruction { + let mut accounts = vec![ + AccountMeta::new_readonly(self.authority, true), + if let Some(record) = self.delegate_record { + AccountMeta::new(record, false) + } else { + AccountMeta::new_readonly(crate::ID, false) + }, + if let Some(token) = self.token { + AccountMeta::new(token, false) + } else { + AccountMeta::new_readonly(crate::ID, false) + }, + AccountMeta::new_readonly(self.mint, false), + AccountMeta::new(self.metadata, false), + if let Some(edition) = self.edition { + AccountMeta::new(edition, false) + } else { + AccountMeta::new_readonly(crate::ID, false) + }, + AccountMeta::new(self.payer, true), + AccountMeta::new_readonly(self.system_program, false), + AccountMeta::new_readonly(self.sysvar_instructions, false), + ]; + + // Optional authorization rules accounts + if let Some(rules) = &self.authorization_rules { + accounts.push(AccountMeta::new_readonly(mpl_token_auth_rules::ID, false)); + accounts.push(AccountMeta::new_readonly(*rules, false)); + } else { + accounts.push(AccountMeta::new_readonly(crate::ID, false)); + accounts.push(AccountMeta::new_readonly(crate::ID, false)); + } + + Instruction { + program_id: crate::ID, + accounts, + data: MetadataInstruction::Update(self.args.clone()) + .try_to_vec() + .unwrap(), + } + } +} diff --git a/token-metadata/program/src/instruction/mod.rs b/token-metadata/program/src/instruction/mod.rs index 9cad72bc33..4fdb5260e4 100644 --- a/token-metadata/program/src/instruction/mod.rs +++ b/token-metadata/program/src/instruction/mod.rs @@ -1,24 +1,30 @@ mod bubblegum; mod burn; mod collection; +mod delegate; pub(crate) mod deprecated; mod edition; pub(crate) mod escrow; mod freeze; mod metadata; +mod state; mod uses; use borsh::{BorshDeserialize, BorshSerialize}; pub use bubblegum::*; pub use burn::*; pub use collection::*; +pub use delegate::*; pub use edition::*; pub use escrow::*; pub use freeze::*; pub use metadata::*; +use mpl_token_metadata_context_derive::AccountContext; #[cfg(feature = "serde-feature")] use serde::{Deserialize, Serialize}; use shank::ShankInstruction; +use solana_program::account_info::AccountInfo; +pub use state::*; pub use uses::*; #[allow(deprecated)] @@ -29,9 +35,10 @@ pub use crate::deprecated_instruction::{ }; use crate::deprecated_instruction::{MintPrintingTokensViaTokenArgs, SetReservationListArgs}; -/// Instructions supported by the Metadata program. +#[repr(C)] #[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] -#[derive(BorshSerialize, BorshDeserialize, Clone, ShankInstruction)] +/// Instructions supported by the Metadata program. +#[derive(BorshSerialize, BorshDeserialize, Clone, ShankInstruction, AccountContext)] #[rustfmt::skip] pub enum MetadataInstruction { /// Create Metadata object. @@ -366,7 +373,7 @@ pub enum MetadataInstruction { #[account(2, writable, name="mint", desc="Mint of the NFT")] #[account(3, writable, name="token_account", desc="Token account to close")] #[account(4, writable, name="master_edition_account", desc="MasterEdition2 of the NFT")] - #[account(5, name="spl token program", desc="SPL Token Program")] + #[account(5, name="spl_token_program", desc="SPL Token Program")] #[account(6, optional, writable, name="collection_metadata", desc="Metadata of the Collection")] BurnNft, @@ -448,7 +455,7 @@ pub enum MetadataInstruction { #[account(6, writable, name="master_edition_account", desc="MasterEdition2 of the original NFT")] #[account(7, writable, name="print_edition_account", desc="Print Edition account of the NFT")] #[account(8, writable, name="edition_marker_account", desc="Edition Marker PDA of the NFT")] - #[account(9, name="spl token program", desc="SPL Token Program")] + #[account(9, name="spl_token_program", desc="SPL Token Program")] BurnEditionNft, /// Create an escrow account to hold tokens. @@ -489,4 +496,257 @@ pub enum MetadataInstruction { #[account(11, name="sysvar_instructions", desc="Instructions sysvar account")] #[account(12, optional, signer, name="authority", desc="Authority/creator of the escrow account")] TransferOutOfEscrow(TransferOutOfEscrowArgs), + + //---- New API + + /// Burns an asset, closing associated accounts. + /// + /// The configurable `authorization_rules` only apply to `ProgrammableNonFungible` assets and + /// it may require additional accounts to validate the rules. + #[account(0, writable, name="metadata", desc="Metadata (pda of ['metadata', program id, mint id])")] + #[account(1, signer, writable, name="owner", desc="Asset owner")] + #[account(2, writable, name="mint", desc="Mint of token asset")] + #[account(3, writable, name="token_account", desc="Token account to close")] + #[account(4, writable, name="master_edition_account", desc="MasterEdition of the asset")] + #[account(5, name="spl_token_program", desc="SPL Token Program")] + #[account(6, optional, writable, name="collection_metadata", desc="Metadata of the Collection")] + #[account(7, optional, name="authorization_rules", desc="Token Authorization Rules account")] + #[account(8, optional, name="authorization_rules_program", desc="Token Authorization Rules Program")] + #[default_optional_accounts] + Burn(BurnArgs), + + /// Creates the metadata and associated accounts for a new or existing mint account. + /// + /// This instruction will initialize a mint account if it does not exist and + /// the mint key is a signer on the transaction. + /// + /// When creating a non-fungible assert, the `master_edition` needs to be specified. + #[account(0, writable, name="metadata", desc="Unallocated metadata account with address as pda of ['metadata', program id, mint id]")] + #[account(1, optional, writable, name="master_edition", desc="Unallocated edition account with address as pda of ['metadata', program id, mint, 'edition']")] + #[account(2, writable, name="mint", desc="Mint of token asset")] + #[account(3, signer, name="authority", desc="Mint authority")] + #[account(4, signer, writable, name="payer", desc="Payer")] + #[account(5, name="update_authority", desc="Update authority for the metadata account")] + #[account(6, name="system_program", desc="System program")] + #[account(7, name="sysvar_instructions", desc="Instructions sysvar account")] + #[account(8, name="spl_token_program", desc="SPL Token program")] + #[args(initialize_mint: bool)] + #[args(update_authority_as_signer: bool)] + #[default_optional_accounts] + Create(CreateArgs), + + /// Mints tokens from a mint account into the specified token account. + /// + /// This instruction will also initialized the associated token account if it does not exist – in + /// this case the `token_owner` will be required. When minting `*NonFungible` assets, the `authority` + /// must be the update authority; in all other cases, it must be the mint authority from the mint + /// account. + #[account(0, writable, name="token", desc="Token or Associated Token account")] + #[account(1, optional, name="token_owner", desc="Owner of the token account")] + #[account(2, name="metadata", desc="Metadata account (pda of ['metadata', program id, mint id])")] + #[account(3, optional, name="master_edition", desc="Master Edition account")] + #[account(4, optional, writable, name="token_record", desc="Token record account")] + #[account(5, writable, name="mint", desc="Mint of token asset")] + #[account(6, signer, name="authority", desc="(Mint or Update) authority")] + #[account(7, optional, name="delegate_record", desc="Metadata delegate record")] + #[account(8, signer, writable, name="payer", desc="Payer")] + #[account(9, name="system_program", desc="System program")] + #[account(10, name="sysvar_instructions", desc="Instructions sysvar account")] + #[account(11, name="spl_token_program", desc="SPL Token program")] + #[account(12, name="spl_ata_program", desc="SPL Associated Token Account program")] + #[account(13, optional, name="authorization_rules_program", desc="Token Authorization Rules program")] + #[account(14, optional, name="authorization_rules", desc="Token Authorization Rules account")] + #[default_optional_accounts] + Mint(MintArgs), + + /// Creates a delegate for an asset. + /// + /// A delegate has a role associated, which determines what actions the delegate can perform. There are + /// two types of delegate: + /// 1. Persistent delegate: only one delegate can exist at the same time for `Transfer`, `Sale` and + /// `Utility` actions (pda of ["metadata", program id, mint id, "persistent_delegate", token owner id]) + /// 2. Multiple delegates: for `Authority`, `Collection`, `Update` and `Uses` actions (pda of ["metadata", + /// program id, mint id, role, update authority id, delegate owner id]) + #[account(0, optional, writable, name="delegate_record", desc="Delegate record account")] + #[account(1, name="delegate", desc="Owner of the delegated account")] + #[account(2, writable, name="metadata", desc="Metadata account")] + #[account(3, optional, name="master_edition", desc="Master Edition account")] + #[account(4, optional, writable, name="token_record", desc="Token record account")] + #[account(5, name="mint", desc="Mint of metadata")] + #[account(6, optional, writable, name="token", desc="Token account of mint")] + #[account(7, signer, name="authority", desc="Update authority or token owner")] + #[account(8, signer, writable, name="payer", desc="Payer")] + #[account(9, name="system_program", desc="System Program")] + #[account(10, name="sysvar_instructions", desc="Instructions sysvar account")] + #[account(11, optional, name="spl_token_program", desc="SPL Token Program")] + #[account(12, optional, name="authorization_rules_program", desc="Token Authorization Rules Program")] + #[account(13, optional, name="authorization_rules", desc="Token Authorization Rules account")] + #[default_optional_accounts] + Delegate(DelegateArgs), + + /// Revokes a delegate. + /// + /// A delegate can revoke itself by signing the transaction as the 'approver'. + #[account(0, optional, writable, name="delegate_record", desc="Delegate record account")] + #[account(1, name="delegate", desc="Owner of the delegated account")] + #[account(2, writable, name="metadata", desc="Metadata account")] + #[account(3, optional, name="master_edition", desc="Master Edition account")] + #[account(4, optional, writable, name="token_record", desc="Token record account")] + #[account(5, name="mint", desc="Mint of metadata")] + #[account(6, optional, writable, name="token", desc="Token account of mint")] + #[account(7, signer, name="authority", desc="Update authority or token owner")] + #[account(8, signer, writable, name="payer", desc="Payer")] + #[account(9, name="system_program", desc="System Program")] + #[account(10, name="sysvar_instructions", desc="Instructions sysvar account")] + #[account(11, optional, name="spl_token_program", desc="SPL Token Program")] + #[account(12, optional, name="authorization_rules_program", desc="Token Authorization Rules Program")] + #[account(13, optional, name="authorization_rules", desc="Token Authorization Rules account")] + #[default_optional_accounts] + Revoke(RevokeArgs), + + /// Locks an asset. For non-programmable assets, this will also freeze the token account. + /// + /// The configurable `authorization_rules` only apply to `ProgrammableNonFungible` assets and + /// it may require additional accounts to validate the rules. + #[account(0, signer, name="authority", desc="Delegate account")] + #[account(1, optional, name="token_owner", desc="Token owner account")] + #[account(2, writable, name="token", desc="Token account")] + #[account(3, name="mint", desc="Mint account")] + #[account(4, writable, name="metadata", desc="Metadata account")] + #[account(5, optional, name="edition", desc="Edition account")] + #[account(6, optional, writable, name="token_record", desc="Token record account")] + #[account(7, signer, writable, name="payer", desc="Payer")] + #[account(8, name="system_program", desc="System program")] + #[account(9, name="sysvar_instructions", desc="System program")] + #[account(10, optional, name="spl_token_program", desc="SPL Token Program")] + #[account(11, optional, name="authorization_rules_program", desc="Token Authorization Rules Program")] + #[account(12, optional, name="authorization_rules", desc="Token Authorization Rules account")] + #[default_optional_accounts] + Lock(LockArgs), + + /// Unlocks an asset. For non-programmable assets, this will also thaw the token account. + /// + /// The configurable `authorization_rules` only apply to `ProgrammableNonFungible` assets and + /// it may require additional accounts to validate the rules. + #[account(0, signer, name="authority", desc="Delegate account")] + #[account(1, optional, name="token_owner", desc="Token owner account")] + #[account(2, writable, name="token", desc="Token account")] + #[account(3, name="mint", desc="Mint account")] + #[account(4, writable, name="metadata", desc="Metadata account")] + #[account(5, optional, name="edition", desc="Edition account")] + #[account(6, optional, writable, name="token_record", desc="Token record account")] + #[account(7, signer, writable, name="payer", desc="Payer")] + #[account(8, name="system_program", desc="System program")] + #[account(9, name="sysvar_instructions", desc="System program")] + #[account(10, optional, name="spl_token_program", desc="SPL Token Program")] + #[account(11, optional, name="authorization_rules_program", desc="Token Authorization Rules Program")] + #[account(12, optional, name="authorization_rules", desc="Token Authorization Rules account")] + #[default_optional_accounts] + Unlock(UnlockArgs), + + /// Migrates an asset to a ProgrammableAsset type. + #[account(0, writable, name="metadata", desc="Metadata account")] + #[account(1, writable, name="edition", desc="Edition account")] + #[account(2, writable, name="token", desc="Token account")] + #[account(3, name="token_owner", desc="Token account owner")] + #[account(4, name="mint", desc="Mint account")] + #[account(5, writable, signer, name="payer", desc="Update authority")] + #[account(6, signer, name="authority", desc="Update authority")] + #[account(7, name="collection_metadata", desc="Collection metadata account")] + #[account(8, name="delegate_record", desc="Delegate record account")] + #[account(9, name="token_record", desc="Token record account")] + #[account(10, name="system_program", desc="System program")] + #[account(11, name="sysvar_instructions", desc="Instruction sysvar account")] + #[account(12, name="spl_token_program", desc="Token Program")] + #[account(13, optional, name="authorization_rules_program", desc="Token Authorization Rules Program")] + #[account(14, optional, name="authorization_rules", desc="Token Authorization Rules account")] + #[default_optional_accounts] + Migrate(MigrateArgs), + + /// Transfer an asset. + /// + /// The configurable `authorization_rules` only apply to `ProgrammableNonFungible` assets and + /// it may require additional accounts to validate the rules. + #[account(0, writable, name="token", desc="Token account")] + #[account(1, name="token_owner", desc="Token account owner")] + #[account(2, writable, name="destination", desc="Destination token account")] + #[account(3, name="destination_owner", desc="Destination token account owner")] + #[account(4, name="mint", desc="Mint of token asset")] + #[account(5, writable, name="metadata", desc="Metadata (pda of ['metadata', program id, mint id])")] + #[account(6, optional, name="edition", desc="Edition of token asset")] + #[account(7, optional, writable, name="owner_token_record", desc="Token record account")] + #[account(8, optional, writable, name="destination_token_record", desc="Token record account")] + #[account(9, signer, name="authority", desc="Transfer authority (token or delegate owner)")] + #[account(10, signer, writable, name="payer", desc="Payer")] + #[account(11, name="system_program", desc="System Program")] + #[account(12, name="sysvar_instructions", desc="Instructions sysvar account")] + #[account(13, name="spl_token_program", desc="SPL Token Program")] + #[account(14, name="spl_ata_program", desc="SPL Associated Token Account program")] + #[account(15, optional, name="authorization_rules_program", desc="Token Authorization Rules Program")] + #[account(16, optional, name="authorization_rules", desc="Token Authorization Rules account")] + #[default_optional_accounts] + Transfer(TransferArgs), + + /// Updates the metadata of an asset. + /// + /// The configurable `authorization_rules` only apply to `ProgrammableNonFungible` assets and + /// it may require additional accounts to validate the rules. + #[account(0, signer, name="authority", desc="Update authority or delegate")] + #[account(1, optional, name="delegate_record", desc="Delegate record PDA")] + #[account(2, optional, name="token", desc="Token account")] + #[account(3, name="mint", desc="Mint account")] + #[account(4, writable, name="metadata", desc="Metadata account")] + #[account(5, optional, writable, name="edition", desc="Edition account")] + #[account(6, signer, writable, name="payer", desc="Payer")] + #[account(7, name="system_program", desc="System program")] + #[account(8, name="sysvar_instructions", desc="System program")] + #[account(9, optional, name="authorization_rules_program", desc="Token Authorization Rules Program")] + #[account(10, optional, name="authorization_rules", desc="Token Authorization Rules account")] + #[default_optional_accounts] + Update(UpdateArgs), + + /// Uses an asset. + /// + /// Use Authority can be the owner of the asset or a delegated use authority. + /// + /// The configurable `authorization_rules` only apply to `ProgrammableNonFungible` assets and + /// it may require additional accounts to validate the rules. + #[account(0, signer, name="authority", desc="Token owner or delegate")] + #[account(1, writable, optional, name="delegate_record", desc="Delegate record PDA")] + #[account(2, writable, optional, name="token", desc="Token account")] + #[account(3, name="mint", desc="Mint account")] + #[account(4, writable, name="metadata", desc="Metadata account")] + #[account(5, optional, writable, name="edition", desc="Edition account")] + #[account(6, signer, name="payer", desc="Payer")] + #[account(7, name="system_program", desc="System program")] + #[account(8, name="sysvar_instructions", desc="System program")] + #[account(9, optional, name="spl_token_program", desc="SPL Token Program")] + #[account(10, optional, name="authorization_rules_program", desc="Token Authorization Rules Program")] + #[account(11, optional, name="authorization_rules", desc="Token Authorization Rules account")] + #[default_optional_accounts] + Use(UseArgs), + + /// Verifies that an asset belongs in an specified collection. + /// + /// The configurable `authorization_rules` only apply to `ProgrammableNonFungible` assets and + /// it may require additional accounts to validate the rules. + /// + /// Depending on the type of verification (e.g., creator or collection), additional accounts + /// are required. + #[account(0, writable, name="metadata", desc="Metadata account")] + #[account(1, signer, writable, name="collection_authority", desc="Collection Update authority")] + #[account(2, signer, writable, name="payer", desc="payer")] + #[account(3, optional, name="authorization_rules", desc="Token Authorization Rules account")] + #[account(4, optional, name="authorization_rules_program", desc="Token Authorization Rules Program")] + #[default_optional_accounts] + Verify(VerifyArgs), +} + +pub struct Context<'a, T> { + pub accounts: T, + pub remaining_accounts: Vec<&'a AccountInfo<'a>>, +} + +pub trait InstructionBuilder { + fn instruction(&self) -> solana_program::instruction::Instruction; } diff --git a/token-metadata/program/src/instruction/scratchpad.rs b/token-metadata/program/src/instruction/scratchpad.rs new file mode 100644 index 0000000000..b5ca3563a0 --- /dev/null +++ b/token-metadata/program/src/instruction/scratchpad.rs @@ -0,0 +1,146 @@ +// This is a module that isn't used anywhere and shouldn't stay in the final version. It is just a +// "scratchpad" for ideas and stuff. + +#[non_exhaustive] +pub struct UpdateAccounts { + metadata_account: Pubkey, + master_edition_account: Pubkey, + mint_account: Pubkey, + new_update_authority: Option, + authority: AuthorityType, + authorization_rules: Option, +} + +impl UpdateAccounts { + pub fn to_account_metas(&self) -> Vec { + let mut infos: Vec = vec![ + AccountMeta::new(self.metadata_account, false), + AccountMeta::new(self.master_edition_account, false), + AccountMeta::new(self.mint_account, false), + AccountMeta::new_readonly(solana_program::system_program::id(), false), + AccountMeta::new_readonly(sysvar::instructions::id(), false), + ]; + if let Some(new_update_authority) = self.new_update_authority { + infos.push(AccountMeta::new_readonly(new_update_authority, false)); + } + + match &self.authority { + AuthorityType::UpdateAuthority(authority) => { + infos.push(AccountMeta::new_readonly(*authority, true)) + } + AuthorityType::Holder { + holder, + token_account, + } => { + infos.push(AccountMeta::new_readonly(*holder, true)); + infos.push(AccountMeta::new_readonly(*token_account, false)); + } + } + if let Some(rules) = &self.authorization_rules { + infos.push(AccountMeta::new_readonly(rules.authorization_rules, false)); + infos.push(AccountMeta::new_readonly(rules.program_id, false)); + } + infos + } +} + +pub struct AuthorizationRules { + authorization_rules: Pubkey, + program_id: Pubkey, +} + +// #[non_exhaustive] +// pub struct TransferAccounts<'info> { +// token_account: &'info AccountInfo<'info>, +// metadata: &'info AccountInfo<'info>, +// mint: &'info AccountInfo<'info>, +// owner: &'info AccountInfo<'info>, +// destination_token_account: &'info AccountInfo<'info>, +// destination_owner: &'info AccountInfo<'info>, +// spl_token_program: &'info AccountInfo<'info>, +// spl_associated_token_program: &'info AccountInfo<'info>, +// system_program: &'info AccountInfo<'info>, +// sysvar_instructions: &'info AccountInfo<'info>, +// authorization_payload: Option>, +// } +// +// impl<'info> TransferAccounts<'info> { +// pub fn new( +// token_account: &'info AccountInfo<'info>, +// metadata: &'info AccountInfo<'info>, +// mint: &'info AccountInfo<'info>, +// owner: &'info AccountInfo<'info>, +// destination_token_account: &'info AccountInfo<'info>, +// destination_owner: &'info AccountInfo<'info>, +// spl_token_program: &'info AccountInfo<'info>, +// spl_associated_token_program: &'info AccountInfo<'info>, +// system_program: &'info AccountInfo<'info>, +// sysvar_instructions: &'info AccountInfo<'info>, +// authorization_payload: Option>, +// ) -> Self { +// Self { +// token_account, +// metadata, +// mint, +// owner, +// destination_token_account, +// destination_owner, +// spl_token_program, +// spl_associated_token_program, +// system_program, +// sysvar_instructions, +// authorization_payload, +// } +// } +// +// pub fn from_args<'a>( +// args: TransferArgs, +// accounts: &'a [AccountInfo<'a>], +// ) -> Result { +// // match args { +// // TransferArgs::V1 { +// // authorization_payload, +// // } => { +// // let token_account = accounts.get(0); +// // let metadata = next_account_info(account_info_iter)?; +// // let mint = next_account_info(account_info_iter)?; +// // let owner = next_account_info(account_info_iter)?; +// // let destination_token_account = next_account_info(account_info_iter)?; +// // let destination_owner = next_account_info(account_info_iter)?; +// // let spl_token_program = next_account_info(account_info_iter)?; +// // let spl_associated_token_program = next_account_info(account_info_iter)?; +// // let system_program = next_account_info(account_info_iter)?; +// // let sysvar_instructions = next_account_info(account_info_iter)?; +// // let authorization_payload = if authorization_payload.is_some() { +// // let authorization_rules = next_account_info(account_info_iter)?; +// // let authorization_rules_program = next_account_info(account_info_iter)?; +// // Some(AuthorizationPayloadAccounts { +// // authorization_rules, +// // authorization_rules_program, +// // }) +// // } else { +// // None +// // }; +// // asso +// // Ok(Self { +// // token_account, +// // metadata, +// // mint, +// // owner, +// // destination_token_account, +// // destination_owner, +// // spl_token_program, +// // spl_associated_token_program, +// // system_program, +// // sysvar_instructions, +// // authorization_payload, +// // }) +// // } +// // } +// todo!() +// } +// +// pub fn validate(&self) -> ProgramResult { +// todo!() +// } +// } diff --git a/token-metadata/program/src/instruction/state.rs b/token-metadata/program/src/instruction/state.rs new file mode 100644 index 0000000000..e7354bd377 --- /dev/null +++ b/token-metadata/program/src/instruction/state.rs @@ -0,0 +1,139 @@ +use borsh::{BorshDeserialize, BorshSerialize}; +#[cfg(feature = "serde-feature")] +use serde::{Deserialize, Serialize}; +use solana_program::instruction::{AccountMeta, Instruction}; + +use super::{InstructionBuilder, MetadataInstruction}; +use crate::processor::AuthorizationData; + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub enum LockArgs { + V1 { + /// Required authorization data to validate the request. + authorization_data: Option, + }, +} + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub enum UnlockArgs { + V1 { + /// Required authorization data to validate the request. + authorization_data: Option, + }, +} + +/// Locks an asset. For non-programmable assets, this will also freeze the token account. +/// +/// # Accounts: +/// +/// 0. `[signer]` Delegate +/// 1. `[optional]` Token owner +/// 2. `[writable]` Token account +/// 3. `[]` Mint account +/// 4. `[writable]` Metadata account +/// 5. `[optional]` Edition account +/// 6. `[optional, writable]` Token record account +/// 7. `[signer, writable]` Payer +/// 8. `[]` System Program +/// 9. `[]` Instructions sysvar account +/// 10. `[optional]` SPL Token Program +/// 11. `[optional]` Token Authorization Rules program +/// 12. `[optional]` Token Authorization Rules account +impl InstructionBuilder for super::builders::Lock { + fn instruction(&self) -> solana_program::instruction::Instruction { + let mut accounts = vec![ + AccountMeta::new_readonly(self.authority, true), + AccountMeta::new_readonly(self.token_owner.unwrap_or(crate::ID), false), + AccountMeta::new(self.token, false), + AccountMeta::new_readonly(self.mint, false), + AccountMeta::new(self.metadata, false), + AccountMeta::new_readonly(self.edition.unwrap_or(crate::ID), false), + if let Some(token_record) = self.token_record { + AccountMeta::new(token_record, false) + } else { + AccountMeta::new_readonly(crate::ID, false) + }, + AccountMeta::new(self.payer, true), + AccountMeta::new_readonly(self.system_program, false), + AccountMeta::new_readonly(self.sysvar_instructions, false), + AccountMeta::new_readonly(self.spl_token_program.unwrap_or(crate::ID), false), + ]; + + // Optional authorization rules accounts + if let Some(rules) = &self.authorization_rules { + accounts.push(AccountMeta::new_readonly(mpl_token_auth_rules::ID, false)); + accounts.push(AccountMeta::new_readonly(*rules, false)); + } else { + accounts.push(AccountMeta::new_readonly(crate::ID, false)); + accounts.push(AccountMeta::new_readonly(crate::ID, false)); + } + + Instruction { + program_id: crate::ID, + accounts, + data: MetadataInstruction::Lock(self.args.clone()) + .try_to_vec() + .unwrap(), + } + } +} + +/// Unlocks an asset. For non-programmable assets, this will also thaw the token account. +/// +/// # Accounts: +/// +/// 0. `[signer]` Delegate +/// 1. `[optional]` Token owner +/// 2. `[writable]` Token account +/// 3. `[]` Mint account +/// 4. `[writable]` Metadata account +/// 5. `[optional]` Edition account +/// 6. `[optional, writable]` Token record account +/// 7. `[signer, writable]` Payer +/// 8. `[]` System Program +/// 9. `[]` Instructions sysvar account +/// 10. `[optional]` SPL Token Program +/// 11. `[optional]` Token Authorization Rules program +/// 12. `[optional]` Token Authorization Rules account +impl InstructionBuilder for super::builders::Unlock { + fn instruction(&self) -> solana_program::instruction::Instruction { + let mut accounts = vec![ + AccountMeta::new_readonly(self.authority, true), + AccountMeta::new_readonly(self.token_owner.unwrap_or(crate::ID), false), + AccountMeta::new(self.token, false), + AccountMeta::new_readonly(self.mint, false), + AccountMeta::new(self.metadata, false), + AccountMeta::new_readonly(self.edition.unwrap_or(crate::ID), false), + if let Some(token_record) = self.token_record { + AccountMeta::new(token_record, false) + } else { + AccountMeta::new_readonly(crate::ID, false) + }, + AccountMeta::new(self.payer, true), + AccountMeta::new_readonly(self.system_program, false), + AccountMeta::new_readonly(self.sysvar_instructions, false), + AccountMeta::new_readonly(self.spl_token_program.unwrap_or(crate::ID), false), + ]; + + // Optional authorization rules accounts + if let Some(rules) = &self.authorization_rules { + accounts.push(AccountMeta::new_readonly(mpl_token_auth_rules::ID, false)); + accounts.push(AccountMeta::new_readonly(*rules, false)); + } else { + accounts.push(AccountMeta::new_readonly(crate::ID, false)); + accounts.push(AccountMeta::new_readonly(crate::ID, false)); + } + + Instruction { + program_id: crate::ID, + accounts, + data: MetadataInstruction::Unlock(self.args.clone()) + .try_to_vec() + .unwrap(), + } + } +} diff --git a/token-metadata/program/src/instruction/uses.rs b/token-metadata/program/src/instruction/uses.rs index d229f0789d..36b4b6ec63 100644 --- a/token-metadata/program/src/instruction/uses.rs +++ b/token-metadata/program/src/instruction/uses.rs @@ -6,7 +6,7 @@ use solana_program::{ pubkey::Pubkey, }; -use crate::instruction::MetadataInstruction; +use crate::{instruction::MetadataInstruction, processor::AuthorizationData}; #[repr(C)] #[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] @@ -22,6 +22,16 @@ pub struct UtilizeArgs { pub number_of_uses: u64, } +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub enum UseArgs { + V1 { + /// Required authorization data to validate the request. + authorization_data: Option, + }, +} + ///# Approve Use Authority /// ///Approve another account to call [utilize] on this NFT diff --git a/token-metadata/program/src/pda.rs b/token-metadata/program/src/pda.rs index 6efa37ad25..f4f323969d 100644 --- a/token-metadata/program/src/pda.rs +++ b/token-metadata/program/src/pda.rs @@ -1,5 +1,7 @@ use solana_program::pubkey::Pubkey; +use crate::{instruction::MetadataDelegateRole, state::TOKEN_RECORD_SEED}; + /// prefix used for PDAs to avoid certain collision attacks: /// https://en.wikipedia.org/wiki/Collision_attack#Chosen-prefix_collision_attack @@ -77,3 +79,35 @@ pub fn find_program_as_burner_account() -> (Pubkey, u8) { &crate::id(), ) } + +pub fn find_metadata_delegate_record_account( + mint: &Pubkey, + role: MetadataDelegateRole, + update_authority: &Pubkey, + delegate: &Pubkey, +) -> (Pubkey, u8) { + Pubkey::find_program_address( + &[ + PREFIX.as_bytes(), + crate::id().as_ref(), + mint.as_ref(), + role.to_string().as_bytes(), + update_authority.as_ref(), + delegate.as_ref(), + ], + &crate::id(), + ) +} + +pub fn find_token_record_account(mint: &Pubkey, token: &Pubkey) -> (Pubkey, u8) { + Pubkey::find_program_address( + &[ + PREFIX.as_bytes(), + crate::id().as_ref(), + mint.as_ref(), + TOKEN_RECORD_SEED.as_bytes(), + token.as_ref(), + ], + &crate::id(), + ) +} diff --git a/token-metadata/program/src/processor/burn/burn.rs b/token-metadata/program/src/processor/burn/burn.rs new file mode 100644 index 0000000000..ffe8f2d405 --- /dev/null +++ b/token-metadata/program/src/processor/burn/burn.rs @@ -0,0 +1,11 @@ +use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey}; + +use crate::{error::MetadataError, instruction::BurnArgs}; + +pub fn burn<'a>( + _program_id: &Pubkey, + _accounts: &'a [AccountInfo<'a>], + _args: BurnArgs, +) -> ProgramResult { + Err(MetadataError::FeatureNotSupported.into()) +} diff --git a/token-metadata/program/src/processor/burn/burn_nft.rs b/token-metadata/program/src/processor/burn/burn_nft.rs index 6384b209e5..8e7c231d8f 100644 --- a/token-metadata/program/src/processor/burn/burn_nft.rs +++ b/token-metadata/program/src/processor/burn/burn_nft.rs @@ -10,16 +10,16 @@ use solana_program::{ pubkey::Pubkey, }; -use crate::pda::find_metadata_account; -use crate::state::Collection; use crate::{ assertions::{ assert_derivation, assert_owned_by, metadata::{assert_currently_holding, assert_verified_member_of_collection}, }, error::MetadataError, + pda::find_metadata_account, state::{ - CollectionDetails, Key, Metadata, TokenMetadataAccount, EDITION, MAX_METADATA_LEN, PREFIX, + Collection, CollectionDetails, Key, Metadata, TokenMetadataAccount, EDITION, + MAX_METADATA_LEN, PREFIX, }, utils::clean_write_metadata, }; diff --git a/token-metadata/program/src/processor/burn/mod.rs b/token-metadata/program/src/processor/burn/mod.rs index 207577684b..0dcd9172d8 100644 --- a/token-metadata/program/src/processor/burn/mod.rs +++ b/token-metadata/program/src/processor/burn/mod.rs @@ -1,5 +1,8 @@ +#![allow(clippy::module_inception)] +mod burn; mod burn_edition_nft; mod burn_nft; +pub use burn::*; pub use burn_edition_nft::*; pub use burn_nft::*; diff --git a/token-metadata/program/src/processor/collection/mod.rs b/token-metadata/program/src/processor/collection/mod.rs index a89328380f..646c8345a8 100644 --- a/token-metadata/program/src/processor/collection/mod.rs +++ b/token-metadata/program/src/processor/collection/mod.rs @@ -5,6 +5,7 @@ mod set_and_verify_sized_collection_item; mod set_collection_size; mod unverify_collection; mod unverify_sized_collection_item; +mod verify; mod verify_collection; mod verify_sized_collection_item; @@ -15,5 +16,6 @@ pub use set_and_verify_sized_collection_item::*; pub use set_collection_size::*; pub use unverify_collection::*; pub use unverify_sized_collection_item::*; +pub use verify::*; pub use verify_collection::*; pub use verify_sized_collection_item::*; diff --git a/token-metadata/program/src/processor/collection/revoke_collection_authority.rs b/token-metadata/program/src/processor/collection/revoke_collection_authority.rs index 4d8340b93f..5ec249cd32 100644 --- a/token-metadata/program/src/processor/collection/revoke_collection_authority.rs +++ b/token-metadata/program/src/processor/collection/revoke_collection_authority.rs @@ -54,7 +54,7 @@ pub fn process_revoke_collection_authority( .checked_add(lamports) .ok_or(MetadataError::NumericalOverflowError)?; sol_memset( - *collection_authority_record.try_borrow_mut_data()?, + &mut collection_authority_record.try_borrow_mut_data()?, 0, USE_AUTHORITY_RECORD_SIZE, ); diff --git a/token-metadata/program/src/processor/collection/verify.rs b/token-metadata/program/src/processor/collection/verify.rs new file mode 100644 index 0000000000..ef55128150 --- /dev/null +++ b/token-metadata/program/src/processor/collection/verify.rs @@ -0,0 +1,11 @@ +use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey}; + +use crate::{error::MetadataError, instruction::VerifyArgs}; + +pub fn verify<'a>( + _program_id: &Pubkey, + _accounts: &'a [AccountInfo<'a>], + _args: VerifyArgs, +) -> ProgramResult { + Err(MetadataError::FeatureNotSupported.into()) +} diff --git a/token-metadata/program/src/processor/delegate/delegate.rs b/token-metadata/program/src/processor/delegate/delegate.rs new file mode 100644 index 0000000000..d4c799411c --- /dev/null +++ b/token-metadata/program/src/processor/delegate/delegate.rs @@ -0,0 +1,386 @@ +use borsh::BorshSerialize; +use mpl_token_auth_rules::utils::get_latest_revision; +use mpl_utils::{assert_signer, create_or_allocate_account_raw}; +use solana_program::{ + account_info::AccountInfo, entrypoint::ProgramResult, program::invoke, program_pack::Pack, + pubkey::Pubkey, system_program, sysvar, +}; +use spl_token::state::Account; + +use crate::{ + assertions::{ + assert_derivation, assert_keys_equal, assert_owned_by, + metadata::assert_update_authority_is_correct, + }, + error::MetadataError, + instruction::{Context, Delegate, DelegateArgs, MetadataDelegateRole}, + pda::{find_token_record_account, PREFIX}, + state::{ + Metadata, MetadataDelegateRecord, ProgrammableConfig, TokenDelegateRole, + TokenMetadataAccount, TokenRecord, TokenStandard, TokenState, + }, + utils::{freeze, thaw}, +}; + +/// Delegates an action over an asset to a specific account. +pub fn delegate<'a>( + program_id: &Pubkey, + accounts: &'a [AccountInfo<'a>], + args: DelegateArgs, +) -> ProgramResult { + let context = Delegate::to_context(accounts)?; + + match args { + DelegateArgs::CollectionV1 { .. } => { + create_delegate_v1(program_id, context, args, MetadataDelegateRole::Collection) + } + DelegateArgs::SaleV1 { amount, .. } => create_persistent_delegate_v1( + program_id, + context, + args, + TokenDelegateRole::Sale, + amount, + ), + DelegateArgs::TransferV1 { amount, .. } => create_persistent_delegate_v1( + program_id, + context, + args, + TokenDelegateRole::Transfer, + amount, + ), + DelegateArgs::UpdateV1 { .. } => { + create_delegate_v1(program_id, context, args, MetadataDelegateRole::Update) + } + DelegateArgs::UtilityV1 { amount, .. } => create_persistent_delegate_v1( + program_id, + context, + args, + TokenDelegateRole::Utility, + amount, + ), + DelegateArgs::StakingV1 { amount, .. } => create_persistent_delegate_v1( + program_id, + context, + args, + TokenDelegateRole::Staking, + amount, + ), + DelegateArgs::StandardV1 { amount } => create_persistent_delegate_v1( + program_id, + context, + args, + TokenDelegateRole::Standard, + amount, + ), + } +} + +/// Creates a `DelegateRole::Collection` delegate. +/// +/// There can be multiple collections delegates set at any time. +fn create_delegate_v1( + program_id: &Pubkey, + ctx: Context, + _args: DelegateArgs, + role: MetadataDelegateRole, +) -> ProgramResult { + // signers + + assert_signer(ctx.accounts.payer_info)?; + assert_signer(ctx.accounts.authority_info)?; + + // ownership + + assert_owned_by(ctx.accounts.metadata_info, program_id)?; + assert_owned_by(ctx.accounts.mint_info, &spl_token::id())?; + + // key match + + assert_keys_equal(ctx.accounts.system_program_info.key, &system_program::ID)?; + assert_keys_equal( + ctx.accounts.sysvar_instructions_info.key, + &sysvar::instructions::ID, + )?; + + // account relationships + + let metadata = Metadata::from_account_info(ctx.accounts.metadata_info)?; + // authority must match update authority + assert_update_authority_is_correct(&metadata, ctx.accounts.authority_info)?; + + if metadata.mint != *ctx.accounts.mint_info.key { + return Err(MetadataError::MintMismatch.into()); + } + + let delegate_record_info = match ctx.accounts.delegate_record_info { + Some(delegate_record_info) => delegate_record_info, + None => { + return Err(MetadataError::MissingTokenAccount.into()); + } + }; + + // process the delegation creation (the derivation is checked + // by the create helper) + + let delegate_role = role.to_string(); + + create_pda_account( + program_id, + delegate_record_info, + ctx.accounts.delegate_info, + ctx.accounts.mint_info, + ctx.accounts.authority_info, + ctx.accounts.payer_info, + ctx.accounts.system_program_info, + &delegate_role, + ) +} + +/// Creates a presistent delegate. For non-programmable assets, this is just a wrapper over +/// spl-token 'approve' delegate. +/// +/// Note that `DelegateRole::Sale` is only available for programmable assets. +fn create_persistent_delegate_v1( + program_id: &Pubkey, + ctx: Context, + _args: DelegateArgs, + role: TokenDelegateRole, + amount: u64, +) -> ProgramResult { + // retrieving required optional accounts + + let token_info = match ctx.accounts.token_info { + Some(token_info) => token_info, + None => { + return Err(MetadataError::MissingTokenAccount.into()); + } + }; + + let spl_token_program_info = match ctx.accounts.spl_token_program_info { + Some(spl_token_program_info) => spl_token_program_info, + None => { + return Err(MetadataError::MissingSplTokenProgram.into()); + } + }; + + // signers + + assert_signer(ctx.accounts.payer_info)?; + assert_signer(ctx.accounts.authority_info)?; + + // ownership + + assert_owned_by(ctx.accounts.metadata_info, program_id)?; + assert_owned_by(ctx.accounts.mint_info, &spl_token::id())?; + assert_owned_by(token_info, &spl_token::id())?; + + // key match + + assert_keys_equal(ctx.accounts.system_program_info.key, &system_program::ID)?; + assert_keys_equal( + ctx.accounts.sysvar_instructions_info.key, + &sysvar::instructions::ID, + )?; + assert_keys_equal(spl_token_program_info.key, &spl_token::ID)?; + + // account relationships + + let metadata = Metadata::from_account_info(ctx.accounts.metadata_info)?; + if metadata.mint != *ctx.accounts.mint_info.key { + return Err(MetadataError::MintMismatch.into()); + } + + // authority must be the owner of the token account: spl-token required the + // token owner to set a delegate + let token = Account::unpack(&token_info.try_borrow_data()?).unwrap(); + if token.owner != *ctx.accounts.authority_info.key { + return Err(MetadataError::IncorrectOwner.into()); + } + + // process the delegation + + // programmables assets can have delegates from any role apart from `Standard` + match metadata.token_standard { + Some(TokenStandard::ProgrammableNonFungible) => { + if matches!(role, TokenDelegateRole::Standard) { + return Err(MetadataError::InvalidDelegateRole.into()); + } + + let (mut token_record, token_record_info) = match ctx.accounts.token_record_info { + Some(token_record_info) => { + let (pda_key, _) = + find_token_record_account(ctx.accounts.mint_info.key, token_info.key); + + assert_keys_equal(&pda_key, token_record_info.key)?; + assert_owned_by(token_record_info, &crate::ID)?; + + ( + TokenRecord::from_account_info(token_record_info)?, + token_record_info, + ) + } + None => { + // token record is required for programmable assets + return Err(MetadataError::MissingTokenRecord.into()); + } + }; + + // we cannot replace an existing delegate, it must be revoked first + if token_record.delegate.is_some() { + return Err(MetadataError::DelegateAlreadyExists.into()); + } + + // if we have a rule set, we need to store its revision; at this point, + // we will validate that we have the correct auth rules PDA + if let Some(ProgrammableConfig::V1 { + rule_set: Some(rule_set), + }) = metadata.programmable_config + { + // valudates that we got the correct rule set + let authorization_rules_info = ctx + .accounts + .authorization_rules_info + .ok_or(MetadataError::MissingAuthorizationRules)?; + assert_keys_equal(authorization_rules_info.key, &rule_set)?; + assert_owned_by(authorization_rules_info, &mpl_token_auth_rules::ID)?; + + // validates auth rules program + let authorization_rules_program_info = ctx + .accounts + .authorization_rules_program_info + .ok_or(MetadataError::MissingAuthorizationRulesProgram)?; + assert_keys_equal( + authorization_rules_program_info.key, + &mpl_token_auth_rules::ID, + )?; + + token_record.rule_set_revision = + get_latest_revision(authorization_rules_info)?.map(|revision| revision as u64); + } + + token_record.state = if matches!(role, TokenDelegateRole::Sale) { + // when a 'Sale' delegate is set, the token state is 'Listed' + // to restrict holder transfers + TokenState::Listed + } else { + TokenState::Unlocked + }; + + token_record.delegate = Some(*ctx.accounts.delegate_info.key); + token_record.delegate_role = Some(role); + token_record.save(*token_record_info.try_borrow_mut_data()?)?; + + if let Some(master_edition_info) = ctx.accounts.master_edition_info { + assert_owned_by(master_edition_info, &crate::ID)?; + // derivation is checked on the thaw function + thaw( + ctx.accounts.mint_info.clone(), + token_info.clone(), + master_edition_info.clone(), + spl_token_program_info.clone(), + )?; + } else { + return Err(MetadataError::MissingEditionAccount.into()); + } + } + Some(_) => { + if !matches!(role, TokenDelegateRole::Standard) { + return Err(MetadataError::InvalidDelegateRole.into()); + } + } + None => { + return Err(MetadataError::CouldNotDetermineTokenStandard.into()); + } + } + + // creates the spl-token delegate + invoke( + &spl_token::instruction::approve( + spl_token_program_info.key, + token_info.key, + ctx.accounts.delegate_info.key, + ctx.accounts.authority_info.key, + &[], + amount, + )?, + &[ + token_info.clone(), + ctx.accounts.delegate_info.clone(), + ctx.accounts.authority_info.clone(), + ], + )?; + + if matches!( + metadata.token_standard, + Some(TokenStandard::ProgrammableNonFungible) + ) { + if let Some(master_edition_info) = ctx.accounts.master_edition_info { + freeze( + ctx.accounts.mint_info.clone(), + token_info.clone(), + master_edition_info.clone(), + spl_token_program_info.clone(), + )?; + } else { + // sanity check: this should not happen at this point since the master + // edition account is validated before the delegation + return Err(MetadataError::MissingEditionAccount.into()); + } + } + + Ok(()) +} + +fn create_pda_account<'a>( + program_id: &Pubkey, + delegate_record_info: &'a AccountInfo<'a>, + delegate_info: &'a AccountInfo<'a>, + mint_info: &'a AccountInfo<'a>, + authority_info: &'a AccountInfo<'a>, + payer_info: &'a AccountInfo<'a>, + system_program_info: &'a AccountInfo<'a>, + delegate_role: &str, +) -> ProgramResult { + // validates the delegate derivation + + let mut signer_seeds = vec![ + PREFIX.as_bytes(), + program_id.as_ref(), + mint_info.key.as_ref(), + delegate_role.as_bytes(), + authority_info.key.as_ref(), + delegate_info.key.as_ref(), + ]; + let bump = &[assert_derivation( + program_id, + delegate_record_info, + &signer_seeds, + )?]; + signer_seeds.push(bump); + + if !delegate_record_info.data_is_empty() { + return Err(MetadataError::DelegateAlreadyExists.into()); + } + + // allocate the delegate account + + create_or_allocate_account_raw( + *program_id, + delegate_record_info, + system_program_info, + payer_info, + MetadataDelegateRecord::size(), + &signer_seeds, + )?; + + let pda = MetadataDelegateRecord { + bump: bump[0], + mint: *mint_info.key, + delegate: *delegate_info.key, + update_authority: *authority_info.key, + ..Default::default() + }; + pda.serialize(&mut *delegate_record_info.try_borrow_mut_data()?)?; + + Ok(()) +} diff --git a/token-metadata/program/src/processor/delegate/mod.rs b/token-metadata/program/src/processor/delegate/mod.rs new file mode 100644 index 0000000000..dd75516f4c --- /dev/null +++ b/token-metadata/program/src/processor/delegate/mod.rs @@ -0,0 +1,6 @@ +#![allow(clippy::module_inception)] +mod delegate; +mod revoke; + +pub use delegate::*; +pub use revoke::*; diff --git a/token-metadata/program/src/processor/delegate/revoke.rs b/token-metadata/program/src/processor/delegate/revoke.rs new file mode 100644 index 0000000000..09771eea03 --- /dev/null +++ b/token-metadata/program/src/processor/delegate/revoke.rs @@ -0,0 +1,312 @@ +use mpl_utils::{assert_signer, close_account_raw, cmp_pubkeys}; +use solana_program::{ + account_info::AccountInfo, entrypoint::ProgramResult, program::invoke, program_option::COption, + program_pack::Pack, pubkey::Pubkey, system_program, sysvar, +}; +use spl_token::state::Account; + +use crate::{ + assertions::{ + assert_keys_equal, assert_owned_by, metadata::assert_update_authority_is_correct, + }, + error::MetadataError, + instruction::{Context, MetadataDelegateRole, Revoke, RevokeArgs}, + pda::{find_metadata_delegate_record_account, find_token_record_account}, + state::{ + Metadata, MetadataDelegateRecord, TokenDelegateRole, TokenMetadataAccount, TokenRecord, + TokenStandard, + }, + utils::{freeze, thaw}, +}; + +/// Revoke a delegation of the token. +pub fn revoke<'a>( + program_id: &Pubkey, + accounts: &'a [AccountInfo<'a>], + args: RevokeArgs, +) -> ProgramResult { + let context = Revoke::to_context(accounts)?; + + match args { + RevokeArgs::CollectionV1 => { + revoke_delegate(program_id, context, MetadataDelegateRole::Collection) + } + RevokeArgs::SaleV1 => { + revoke_persistent_delegate(program_id, context, TokenDelegateRole::Sale) + } + RevokeArgs::TransferV1 => { + revoke_persistent_delegate(program_id, context, TokenDelegateRole::Transfer) + } + RevokeArgs::UpdateV1 => revoke_delegate(program_id, context, MetadataDelegateRole::Update), + RevokeArgs::UtilityV1 => { + revoke_persistent_delegate(program_id, context, TokenDelegateRole::Utility) + } + RevokeArgs::StakingV1 => { + revoke_persistent_delegate(program_id, context, TokenDelegateRole::Staking) + } + RevokeArgs::StandardV1 => { + revoke_persistent_delegate(program_id, context, TokenDelegateRole::Standard) + } + } +} + +fn revoke_delegate( + program_id: &Pubkey, + ctx: Context, + role: MetadataDelegateRole, +) -> ProgramResult { + // signers + + assert_signer(ctx.accounts.payer_info)?; + assert_signer(ctx.accounts.authority_info)?; + + // ownership + + assert_owned_by(ctx.accounts.metadata_info, program_id)?; + assert_owned_by(ctx.accounts.mint_info, &spl_token::id())?; + + // key match + + assert_keys_equal(ctx.accounts.system_program_info.key, &system_program::ID)?; + assert_keys_equal( + ctx.accounts.sysvar_instructions_info.key, + &sysvar::instructions::ID, + )?; + + // account relationships + + let delegate_record_info = match ctx.accounts.delegate_record_info { + Some(delegate_record_info) => delegate_record_info, + None => { + return Err(MetadataError::MissingTokenAccount.into()); + } + }; + + let metadata = Metadata::from_account_info(ctx.accounts.metadata_info)?; + // there are two scenarios here: + // 1. authority is equal to delegate: delegate as a signer is self-revoking + // 2. otherwise we need the update authority as a signer + let approver = if cmp_pubkeys( + ctx.accounts.delegate_info.key, + ctx.accounts.authority_info.key, + ) { + match MetadataDelegateRecord::from_account_info(delegate_record_info) { + Ok(delegate_record) => { + if cmp_pubkeys(&delegate_record.delegate, ctx.accounts.authority_info.key) { + delegate_record.update_authority + } else { + return Err(MetadataError::InvalidDelegate.into()); + } + } + Err(_) => { + return Err(MetadataError::DelegateNotFound.into()); + } + } + } else { + assert_update_authority_is_correct(&metadata, ctx.accounts.authority_info)?; + *ctx.accounts.authority_info.key + }; + + if metadata.mint != *ctx.accounts.mint_info.key { + return Err(MetadataError::MintMismatch.into()); + } + + // closes the delegate record + + close_delegate_record( + role, + delegate_record_info, + ctx.accounts.delegate_info.key, + ctx.accounts.mint_info.key, + &approver, + ctx.accounts.payer_info, + ) +} + +fn revoke_persistent_delegate( + program_id: &Pubkey, + ctx: Context, + role: TokenDelegateRole, +) -> ProgramResult { + // retrieving required optional accounts + + let token_info = match ctx.accounts.token_info { + Some(token_info) => token_info, + None => { + return Err(MetadataError::MissingTokenAccount.into()); + } + }; + + let spl_token_program_info = match ctx.accounts.spl_token_program_info { + Some(spl_token_program_info) => spl_token_program_info, + None => { + return Err(MetadataError::MissingSplTokenProgram.into()); + } + }; + + // signers + + assert_signer(ctx.accounts.payer_info)?; + assert_signer(ctx.accounts.authority_info)?; + + // ownership + + assert_owned_by(ctx.accounts.metadata_info, program_id)?; + assert_owned_by(ctx.accounts.mint_info, &spl_token::id())?; + assert_owned_by(token_info, &spl_token::id())?; + + // key match + + assert_keys_equal(ctx.accounts.system_program_info.key, &system_program::ID)?; + assert_keys_equal( + ctx.accounts.sysvar_instructions_info.key, + &sysvar::instructions::ID, + )?; + assert_keys_equal(spl_token_program_info.key, &spl_token::ID)?; + + // account relationships + + let metadata = Metadata::from_account_info(ctx.accounts.metadata_info)?; + if metadata.mint != *ctx.accounts.mint_info.key { + return Err(MetadataError::MintMismatch.into()); + } + + // authority must be the owner of the token account: spl-token required the + // token owner to revoke a delegate + let token = Account::unpack(&token_info.try_borrow_data()?).unwrap(); + if token.owner != *ctx.accounts.authority_info.key { + return Err(MetadataError::IncorrectOwner.into()); + } + + if let COption::Some(existing) = &token.delegate { + if !cmp_pubkeys(existing, ctx.accounts.delegate_info.key) { + return Err(MetadataError::InvalidDelegate.into()); + } + } else { + return Err(MetadataError::DelegateNotFound.into()); + } + + // process the revoke + + // programmables assets can have delegates from any role apart from `Standard` + match metadata.token_standard { + Some(TokenStandard::ProgrammableNonFungible) => { + if matches!(role, TokenDelegateRole::Standard) { + return Err(MetadataError::InvalidDelegateRole.into()); + } + + let (mut token_record, token_record_info) = match ctx.accounts.token_record_info { + Some(token_record_info) => { + let (pda_key, _) = + find_token_record_account(ctx.accounts.mint_info.key, token_info.key); + + assert_keys_equal(&pda_key, token_record_info.key)?; + assert_owned_by(token_record_info, &crate::ID)?; + + ( + TokenRecord::from_account_info(token_record_info)?, + token_record_info, + ) + } + None => { + // token record is required for programmable assets + return Err(MetadataError::MissingTokenRecord.into()); + } + }; + + if let Some(delegate) = token_record.delegate { + assert_keys_equal(&delegate, ctx.accounts.delegate_info.key)?; + + if token_record.delegate_role == Some(role) { + // resets the token record (state, rule_set_revision and delegate info) + token_record.reset(); + token_record.save(*token_record_info.try_borrow_mut_data()?)?; + } else { + return Err(MetadataError::InvalidDelegate.into()); + } + } + + if let Some(master_edition_info) = ctx.accounts.master_edition_info { + assert_owned_by(master_edition_info, &crate::ID)?; + // derivation is checked on the thaw function + thaw( + ctx.accounts.mint_info.clone(), + token_info.clone(), + master_edition_info.clone(), + spl_token_program_info.clone(), + )?; + } else { + return Err(MetadataError::MissingEditionAccount.into()); + } + } + Some(_) => { + if !matches!(role, TokenDelegateRole::Standard) { + return Err(MetadataError::InvalidDelegateRole.into()); + } + } + None => { + return Err(MetadataError::CouldNotDetermineTokenStandard.into()); + } + } + + // revokes the spl-token delegate + invoke( + &spl_token::instruction::revoke( + spl_token_program_info.key, + token_info.key, + ctx.accounts.authority_info.key, + &[], + )?, + &[ + token_info.clone(), + ctx.accounts.delegate_info.clone(), + ctx.accounts.authority_info.clone(), + ], + )?; + + if matches!( + metadata.token_standard, + Some(TokenStandard::ProgrammableNonFungible) + ) { + if let Some(master_edition_info) = ctx.accounts.master_edition_info { + freeze( + ctx.accounts.mint_info.clone(), + token_info.clone(), + master_edition_info.clone(), + spl_token_program_info.clone(), + )?; + } else { + // sanity check: this should not happen at this point since the master + // edition account is validated before the delegation + return Err(MetadataError::MissingEditionAccount.into()); + } + } + + Ok(()) +} + +/// Closes a delegate PDA. +/// +/// It checks that the derivation is correct before closing +/// the delegate record account. +fn close_delegate_record<'a>( + role: MetadataDelegateRole, + delegate_record_info: &'a AccountInfo<'a>, + delegate: &Pubkey, + mint: &Pubkey, + approver: &Pubkey, + payer_info: &'a AccountInfo<'a>, +) -> ProgramResult { + if delegate_record_info.data_is_empty() { + return Err(MetadataError::Uninitialized.into()); + } + + let (pda_key, _) = find_metadata_delegate_record_account(mint, role, approver, delegate); + + if pda_key != *delegate_record_info.key { + Err(MetadataError::DerivedKeyInvalid.into()) + } else { + // closes the delegate account + close_account_raw(payer_info, delegate_record_info) + } +} diff --git a/token-metadata/program/src/processor/deprecated/mint_new_edition_from_master_edition_via_vault_proxy.rs b/token-metadata/program/src/processor/deprecated/mint_new_edition_from_master_edition_via_vault_proxy.rs deleted file mode 100644 index 5fbd7cbf42..0000000000 --- a/token-metadata/program/src/processor/deprecated/mint_new_edition_from_master_edition_via_vault_proxy.rs +++ /dev/null @@ -1,130 +0,0 @@ -use arrayref::array_ref; -use mpl_token_vault::{error::VaultError, state::VaultState}; -use mpl_utils::{assert_signer, token::get_owner_from_token_account}; -use solana_program::{ - account_info::{next_account_info, AccountInfo}, - entrypoint::ProgramResult, - pubkey::Pubkey, -}; - -use crate::{ - assertions::assert_owned_by, - error::MetadataError, - utils::{ - process_mint_new_edition_from_master_edition_via_token_logic, - MintNewEditionFromMasterEditionViaTokenLogicArgs, - }, -}; - -pub fn process_deprecated_mint_new_edition_from_master_edition_via_vault_proxy<'a>( - program_id: &'a Pubkey, - accounts: &'a [AccountInfo<'a>], - edition: u64, -) -> ProgramResult { - let account_info_iter = &mut accounts.iter(); - - let new_metadata_account_info = next_account_info(account_info_iter)?; - let new_edition_account_info = next_account_info(account_info_iter)?; - let master_edition_account_info = next_account_info(account_info_iter)?; - let mint_info = next_account_info(account_info_iter)?; - let edition_marker_info = next_account_info(account_info_iter)?; - let mint_authority_info = next_account_info(account_info_iter)?; - let payer_info = next_account_info(account_info_iter)?; - let vault_authority_info = next_account_info(account_info_iter)?; - let store_info = next_account_info(account_info_iter)?; - let safety_deposit_info = next_account_info(account_info_iter)?; - let vault_info = next_account_info(account_info_iter)?; - let update_authority_info = next_account_info(account_info_iter)?; - let master_metadata_account_info = next_account_info(account_info_iter)?; - let token_program_account_info = next_account_info(account_info_iter)?; - // we cant do much here to prove that this is the right token vault program except to prove that it matches - // the global one right now. We dont want to force people to use one vault program, - // so there is a bit of trust involved, but the attack vector here is someone provides - // an entirely fake vault program that claims to own token account X via it's pda but in order to spoof X's owner - // and get a free edition. However, we check that the owner of account X is the vault account's pda, so - // not sure how they would get away with it - they'd need to actually own that account! - J. - let token_vault_program_info = next_account_info(account_info_iter)?; - let system_account_info = next_account_info(account_info_iter)?; - - let vault_data = vault_info.data.borrow(); - let safety_deposit_data = safety_deposit_info.data.borrow(); - - // Since we're crunching out borsh for CPU units, do type checks this way - if vault_data[0] != mpl_token_vault::state::Key::VaultV1 as u8 { - return Err(VaultError::DataTypeMismatch.into()); - } - - if safety_deposit_data[0] != mpl_token_vault::state::Key::SafetyDepositBoxV1 as u8 { - return Err(VaultError::DataTypeMismatch.into()); - } - - // skip deserialization to keep things cheap on CPU - let token_program = Pubkey::new_from_array(*array_ref![vault_data, 1, 32]); - let vault_authority = Pubkey::new_from_array(*array_ref![vault_data, 65, 32]); - let store_on_sd = Pubkey::new_from_array(*array_ref![safety_deposit_data, 65, 32]); - let vault_on_sd = Pubkey::new_from_array(*array_ref![safety_deposit_data, 1, 32]); - - let owner = get_owner_from_token_account(store_info)?; - - let seeds = &[ - mpl_token_vault::state::PREFIX.as_bytes(), - token_vault_program_info.key.as_ref(), - vault_info.key.as_ref(), - ]; - let (authority, _) = Pubkey::find_program_address(seeds, token_vault_program_info.key); - - if owner != authority { - return Err(MetadataError::InvalidOwner.into()); - } - - assert_signer(vault_authority_info)?; - - // Since most checks happen next level down in token program, we only need to verify - // that the vault authority signer matches what's expected on vault to authorize - // use of our pda authority, and that the token store is right for the safety deposit. - // Then pass it through. - assert_owned_by(vault_info, token_vault_program_info.key)?; - assert_owned_by(safety_deposit_info, token_vault_program_info.key)?; - assert_owned_by(store_info, token_program_account_info.key)?; - - if &token_program != token_program_account_info.key { - return Err(VaultError::TokenProgramProvidedDoesNotMatchVault.into()); - } - - if !vault_authority_info.is_signer { - return Err(VaultError::AuthorityIsNotSigner.into()); - } - if *vault_authority_info.key != vault_authority { - return Err(VaultError::AuthorityDoesNotMatch.into()); - } - - if vault_data[195] != VaultState::Combined as u8 { - return Err(VaultError::VaultShouldBeCombined.into()); - } - - if vault_on_sd != *vault_info.key { - return Err(VaultError::SafetyDepositBoxVaultMismatch.into()); - } - - if *store_info.key != store_on_sd { - return Err(VaultError::StoreDoesNotMatchSafetyDepositBox.into()); - } - - let args = MintNewEditionFromMasterEditionViaTokenLogicArgs { - new_metadata_account_info, - new_edition_account_info, - master_edition_account_info, - mint_info, - edition_marker_info, - mint_authority_info, - payer_account_info: payer_info, - owner_account_info: vault_authority_info, - token_account_info: store_info, - update_authority_info, - master_metadata_account_info, - token_program_account_info, - system_account_info, - }; - - process_mint_new_edition_from_master_edition_via_token_logic(program_id, args, edition, true) -} diff --git a/token-metadata/program/src/processor/deprecated/mod.rs b/token-metadata/program/src/processor/deprecated/mod.rs index ce3fc4ec09..a0fe90798f 100644 --- a/token-metadata/program/src/processor/deprecated/mod.rs +++ b/token-metadata/program/src/processor/deprecated/mod.rs @@ -1,9 +1,7 @@ mod create_medatata_accounts_v2; mod deprecated_create_metadata_accounts; mod deprecated_update_metadata_accounts; -mod mint_new_edition_from_master_edition_via_vault_proxy; pub use create_medatata_accounts_v2::*; pub use deprecated_create_metadata_accounts::*; pub use deprecated_update_metadata_accounts::*; -pub use mint_new_edition_from_master_edition_via_vault_proxy::*; diff --git a/token-metadata/program/src/processor/escrow/create_escrow_account.rs b/token-metadata/program/src/processor/escrow/create_escrow_account.rs index 76dbdaa358..909cba36da 100644 --- a/token-metadata/program/src/processor/escrow/create_escrow_account.rs +++ b/token-metadata/program/src/processor/escrow/create_escrow_account.rs @@ -115,7 +115,7 @@ pub fn process_create_escrow_account( )?; sol_memcpy( - &mut **escrow_account_info.try_borrow_mut_data().unwrap(), + &mut escrow_account_info.try_borrow_mut_data()?, &serialized_data, serialized_data.len(), ); diff --git a/token-metadata/program/src/processor/escrow/transfer_out.rs b/token-metadata/program/src/processor/escrow/transfer_out.rs index 811484221a..52dee17d00 100644 --- a/token-metadata/program/src/processor/escrow/transfer_out.rs +++ b/token-metadata/program/src/processor/escrow/transfer_out.rs @@ -59,13 +59,14 @@ pub fn process_transfer_out_of_escrow( assert_signer(payer_info)?; // Allocate the target ATA if it doesn't exist. - if !is_initialized_account(*attribute_dst_info.data.borrow()) { + if !is_initialized_account(&attribute_dst_info.data.borrow()) { #[allow(deprecated)] let create_escrow_ata_ix = spl_associated_token_account::instruction::create_associated_token_account( payer_info.key, payer_info.key, attribute_mint_info.key, + &spl_token::ID, ); invoke( diff --git a/token-metadata/program/src/processor/freeze/freeze_delegated_account.rs b/token-metadata/program/src/processor/freeze/freeze_delegated_account.rs index 915dd7f4ac..00ce49ab70 100644 --- a/token-metadata/program/src/processor/freeze/freeze_delegated_account.rs +++ b/token-metadata/program/src/processor/freeze/freeze_delegated_account.rs @@ -10,7 +10,7 @@ use spl_token::{instruction::freeze_account, state::Mint}; use crate::{ assertions::{ assert_delegated_tokens, assert_derivation, assert_freeze_authority_matches_mint, - assert_initialized, assert_owned_by, + assert_initialized, assert_owned_by, edition::assert_edition_is_not_programmable, }, error::MetadataError, state::{EDITION, PREFIX}, @@ -51,6 +51,10 @@ pub fn process_freeze_delegated_account( edition_info, &edition_info_path, )?]; + + // check that we are not dealing with a pNFT master edition + assert_edition_is_not_programmable(edition_info)?; + let mut edition_info_seeds = edition_info_path.clone(); edition_info_seeds.push(edition_info_path_bump_seed); invoke_signed( diff --git a/token-metadata/program/src/processor/freeze/thaw_delegated_account.rs b/token-metadata/program/src/processor/freeze/thaw_delegated_account.rs index 85ed5d9592..23fb665a0f 100644 --- a/token-metadata/program/src/processor/freeze/thaw_delegated_account.rs +++ b/token-metadata/program/src/processor/freeze/thaw_delegated_account.rs @@ -10,7 +10,7 @@ use spl_token::{instruction::thaw_account, state::Mint}; use crate::{ assertions::{ assert_delegated_tokens, assert_derivation, assert_freeze_authority_matches_mint, - assert_initialized, assert_owned_by, + assert_initialized, assert_owned_by, edition::assert_edition_is_not_programmable, }, error::MetadataError, state::{EDITION, PREFIX}, @@ -50,6 +50,10 @@ pub fn process_thaw_delegated_account( edition_info, &edition_info_path, )?]; + + // check that we are not dealing with a pNFT master edition + assert_edition_is_not_programmable(edition_info)?; + let mut edition_info_seeds = edition_info_path.clone(); edition_info_seeds.push(edition_info_path_bump_seed); invoke_signed( diff --git a/token-metadata/program/src/processor/metadata/create.rs b/token-metadata/program/src/processor/metadata/create.rs new file mode 100644 index 0000000000..9f1bbece24 --- /dev/null +++ b/token-metadata/program/src/processor/metadata/create.rs @@ -0,0 +1,208 @@ +use mpl_utils::assert_initialized; +use solana_program::{ + account_info::AccountInfo, entrypoint::ProgramResult, msg, program::invoke, program_pack::Pack, + pubkey::Pubkey, rent::Rent, system_instruction, sysvar::Sysvar, +}; +use spl_token::{native_mint::DECIMALS, state::Mint}; + +use crate::{ + error::MetadataError, + instruction::{Context, Create, CreateArgs}, + state::{ + Metadata, ProgrammableConfig, TokenMetadataAccount, TokenStandard, MAX_MASTER_EDITION_LEN, + TOKEN_STANDARD_INDEX, + }, + utils::{ + create_master_edition, process_create_metadata_accounts_logic, + CreateMetadataAccountsLogicArgs, + }, +}; + +/// Create the associated metadata accounts for a mint. +/// +/// The instruction will also initialize the mint if the account does not +/// exist. For `NonFungible` assets, a `master_edition` account is required. +pub fn create<'a>( + program_id: &Pubkey, + accounts: &'a [AccountInfo<'a>], + args: CreateArgs, +) -> ProgramResult { + let context = Create::to_context(accounts)?; + + match args { + CreateArgs::V1 { .. } => create_v1(program_id, context, args), + } +} + +/// V1 implementation of the create instruction. +fn create_v1(program_id: &Pubkey, ctx: Context, args: CreateArgs) -> ProgramResult { + // get the args for the instruction + let CreateArgs::V1 { + ref asset_data, + decimals, + print_supply, + } = args; + + // cannot create non-fungible editions on this instruction + if matches!(asset_data.token_standard, TokenStandard::NonFungibleEdition) { + return Err(MetadataError::InvalidTokenStandard.into()); + } + + // if the account does not exist, we will allocate a new mint + + if ctx.accounts.mint_info.data_is_empty() { + // mint account must be a signer in the transaction + if !ctx.accounts.mint_info.is_signer { + return Err(MetadataError::MintIsNotSigner.into()); + } + + msg!("Initializing mint account"); + + invoke( + &system_instruction::create_account( + ctx.accounts.payer_info.key, + ctx.accounts.mint_info.key, + Rent::get()?.minimum_balance(spl_token::state::Mint::LEN), + spl_token::state::Mint::LEN as u64, + &spl_token::id(), + ), + &[ + ctx.accounts.payer_info.clone(), + ctx.accounts.mint_info.clone(), + ], + )?; + + let decimals = match asset_data.token_standard { + // for NonFungible variants, we ignore the argument and + // always use 0 decimals + TokenStandard::NonFungible | TokenStandard::ProgrammableNonFungible => 0, + // for Fungile variants, we either use the specified decimals or the default + // DECIMALS from spl-token + TokenStandard::FungibleAsset | TokenStandard::Fungible => match decimals { + Some(decimals) => decimals, + // if decimals not provided, use the default + None => DECIMALS, + }, + _ => { + return Err(MetadataError::InvalidTokenStandard.into()); + } + }; + + // initializing the mint account + invoke( + &spl_token::instruction::initialize_mint2( + ctx.accounts.spl_token_program_info.key, + ctx.accounts.mint_info.key, + ctx.accounts.authority_info.key, + Some(ctx.accounts.authority_info.key), + decimals, + )?, + &[ + ctx.accounts.mint_info.clone(), + ctx.accounts.authority_info.clone(), + ], + )?; + } else { + // validates the existing mint account + + let mint: Mint = assert_initialized(ctx.accounts.mint_info, MetadataError::Uninitialized)?; + // NonFungible assets must have decimals == 0 and supply no greater than 1 + if matches!( + asset_data.token_standard, + TokenStandard::NonFungible | TokenStandard::ProgrammableNonFungible + ) && (mint.decimals > 0 || mint.supply > 1) + { + return Err(MetadataError::InvalidMintForTokenStandard.into()); + } + // Programmable assets must have supply == 0 + if matches!( + asset_data.token_standard, + TokenStandard::ProgrammableNonFungible + ) && (mint.supply > 0) + { + return Err(MetadataError::MintSupplyMustBeZero.into()); + } + } + + // creates the metadata account + + process_create_metadata_accounts_logic( + program_id, + CreateMetadataAccountsLogicArgs { + metadata_account_info: ctx.accounts.metadata_info, + mint_info: ctx.accounts.mint_info, + mint_authority_info: ctx.accounts.authority_info, + payer_account_info: ctx.accounts.payer_info, + update_authority_info: ctx.accounts.update_authority_info, + system_account_info: ctx.accounts.system_program_info, + }, + asset_data.as_data_v2(), + false, + asset_data.is_mutable, + false, + true, + asset_data.collection_details.clone(), + )?; + + // creates the master edition account (only for NonFungible assets) + + if matches!( + asset_data.token_standard, + TokenStandard::NonFungible | TokenStandard::ProgrammableNonFungible + ) { + let print_supply = print_supply.ok_or(MetadataError::MissingPrintSupply)?; + + if let Some(master_edition) = ctx.accounts.master_edition_info { + create_master_edition( + program_id, + master_edition, + ctx.accounts.mint_info, + ctx.accounts.update_authority_info, + ctx.accounts.authority_info, + ctx.accounts.payer_info, + ctx.accounts.metadata_info, + ctx.accounts.spl_token_program_info, + ctx.accounts.system_program_info, + print_supply.to_option(), + )?; + + // for pNFTs, we store the token standard value at the end of the + // master edition account + if matches!( + asset_data.token_standard, + TokenStandard::ProgrammableNonFungible + ) { + let mut data = master_edition.data.borrow_mut(); + + if data.len() < MAX_MASTER_EDITION_LEN { + return Err(MetadataError::InvalidMasterEditionAccountLength.into()); + } + + data[TOKEN_STANDARD_INDEX] = TokenStandard::ProgrammableNonFungible as u8; + } + } else { + return Err(MetadataError::MissingMasterEditionAccount.into()); + } + } else if print_supply.is_some() { + msg!("Ignoring print supply for selected token standard"); + } + + let mut metadata = Metadata::from_account_info(ctx.accounts.metadata_info)?; + metadata.token_standard = Some(asset_data.token_standard); + + // sets the programmable config for programmable assets + + if matches!( + asset_data.token_standard, + TokenStandard::ProgrammableNonFungible + ) { + metadata.programmable_config = Some(ProgrammableConfig::V1 { + rule_set: asset_data.rule_set, + }); + } + + // saves the state + metadata.save(&mut ctx.accounts.metadata_info.try_borrow_mut_data()?)?; + + Ok(()) +} diff --git a/token-metadata/program/src/processor/metadata/migrate.rs b/token-metadata/program/src/processor/metadata/migrate.rs new file mode 100644 index 0000000000..c139f91eb3 --- /dev/null +++ b/token-metadata/program/src/processor/metadata/migrate.rs @@ -0,0 +1,227 @@ +use mpl_token_auth_rules::utils::assert_owned_by; +use mpl_utils::{assert_signer, create_or_allocate_account_raw}; +use solana_program::{ + account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError, + program_option::COption, pubkey, pubkey::Pubkey, system_program, sysvar, +}; +use spl_token::state::{Account, Mint}; + +use crate::{ + assertions::{ + collection::assert_is_collection_delegated_authority, metadata::assert_metadata_valid, + }, + error::MetadataError, + instruction::{Context, Migrate, MigrateArgs}, + pda::PREFIX, + state::{ + CollectionAuthorityRecord, Metadata, MigrationType, ProgrammableConfig, TokenDelegateRole, + TokenMetadataAccount, TokenRecord, TokenStandard, TokenState, TOKEN_RECORD_SEED, + TOKEN_STANDARD_INDEX, + }, + utils::{ + assert_derivation, assert_edition_valid, assert_initialized, clean_write_metadata, freeze, + }, +}; + +pub fn migrate<'a>( + program_id: &Pubkey, + accounts: &'a [AccountInfo<'a>], + args: MigrateArgs, +) -> ProgramResult { + let context = Migrate::to_context(accounts)?; + + match args { + MigrateArgs::V1 { .. } => migrate_v1(program_id, context, args), + } +} + +pub fn migrate_v1(program_id: &Pubkey, ctx: Context, args: MigrateArgs) -> ProgramResult { + let MigrateArgs::V1 { + migration_type, + rule_set, + } = args; + + let payer_info = ctx.accounts.payer_info; + let authority_info = ctx.accounts.authority_info; + let metadata_info = ctx.accounts.metadata_info; + let edition_info = ctx.accounts.edition_info; + let mint_info = ctx.accounts.mint_info; + let collection_metadata_info = ctx.accounts.collection_metadata_info; + let token_info = ctx.accounts.token_info; + let token_record_info = ctx.accounts.token_record_info; + let system_program_info = ctx.accounts.system_program_info; + let sysvar_instructions_info = ctx.accounts.sysvar_instructions_info; + let spl_token_program_info = ctx.accounts.spl_token_program_info; + + // Validate Accounts + + // Check signers + assert_signer(authority_info)?; + assert_signer(payer_info)?; + + // Assert program ownership + assert_owned_by(metadata_info, program_id)?; + assert_owned_by(edition_info, program_id)?; + assert_owned_by(mint_info, &spl_token::ID)?; + assert_owned_by(token_info, &spl_token::ID)?; + + // Check program IDs. + msg!("Check program IDs"); + if spl_token_program_info.key != &spl_token::ID { + return Err(ProgramError::IncorrectProgramId); + } + + if system_program_info.key != &system_program::ID { + return Err(ProgramError::IncorrectProgramId); + } + + if sysvar_instructions_info.key != &sysvar::instructions::ID { + return Err(ProgramError::IncorrectProgramId); + } + msg!("Check auth rules program ID"); + if let Some(auth_rules_program) = ctx.accounts.authorization_rules_program_info { + if auth_rules_program.key != &mpl_token_auth_rules::ID { + return Err(ProgramError::IncorrectProgramId); + } + } + + // Check derivations. + assert_edition_valid(program_id, mint_info.key, edition_info)?; + assert_metadata_valid(program_id, mint_info.key, metadata_info)?; + + // Deserialize metadata. + let mut metadata = Metadata::from_account_info(metadata_info)?; + let collection_metadata = Metadata::from_account_info(collection_metadata_info)?; + + let token_standard = metadata.token_standard.ok_or_else(|| { + >::into( + MetadataError::InvalidTokenStandard, + ) + })?; + + // Can only migrate NFT --> PNFT right now. + if !matches!(token_standard, TokenStandard::NonFungible) { + return Err(MetadataError::InvalidTokenStandard.into()); + } + + match migration_type { + MigrationType::CollectionV1 => return Err(MetadataError::FeatureNotSupported.into()), + MigrationType::ProgrammableV1 => { + // NFT --> PNFT migration requires the mpl-migration-validator + // to CPI into Token Metadata using its program signer key. + // In addition, the collection must have a delegate record set + // with the mpl-migration-validator program signer as the delegate. + let migration_validator_signer = + pubkey!("4fDQAj27ahBfXw3ZQumg5gJrMRUCzPUW6RxrRPFMC8Av"); + + if *authority_info.key != migration_validator_signer { + return Err(MetadataError::UpdateAuthorityIncorrect.into()); + } + + assert_is_collection_delegated_authority( + ctx.accounts.delegate_record_info, + &migration_validator_signer, + &collection_metadata.mint, + )?; + + let delegate_record = + CollectionAuthorityRecord::from_account_info(ctx.accounts.delegate_record_info)?; + + if delegate_record.update_authority != Some(collection_metadata.update_authority) { + return Err(MetadataError::UpdateAuthorityIncorrect.into()); + } + + let token: Account = assert_initialized(token_info)?; + let mint: Mint = assert_initialized(mint_info)?; + + if token.mint != *mint_info.key { + return Err(MetadataError::MintMismatch.into()); + } + + if mint.freeze_authority.is_none() { + return Err(MetadataError::NoFreezeAuthoritySet.into()); + } + + if mint.freeze_authority.unwrap() != *edition_info.key { + return Err(MetadataError::InvalidFreezeAuthority.into()); + } + + // NFT --> PNFT migration must maintain the current level of functionality + // that the token has, but all pNFTs must be frozen. To accomplish this, + // we assign Migration delegate to any pNFTs that have a SPL token delegate + // set. This allows the delegate to freeze the token via the Token Metadata + // Lock abstraction, as well as to transfer it as a normal SPL delegate is + // able to. + // + // Unfrozen tokens are frozen. Already frozen tokens will have the Lock + // flag set to match the current state in the new abstraction. + + // We create the token record if it does not exist, + // but we only serialize once at the end to save on compute. + + let mut token_record = TokenRecord::default(); + + // We check the derivation regardless of whether the account exists. + let mut signer_seeds = Vec::from([ + PREFIX.as_bytes(), + crate::ID.as_ref(), + mint_info.key.as_ref(), + TOKEN_RECORD_SEED.as_bytes(), + token_info.key.as_ref(), + ]); + + let bump = &[assert_derivation( + program_id, + token_record_info, + &signer_seeds, + )?]; + signer_seeds.push(bump); + + if ctx.accounts.token_record_info.data.borrow().is_empty() { + // allocate the delegate account + create_or_allocate_account_raw( + *program_id, + token_record_info, + system_program_info, + payer_info, + TokenRecord::size(), + &signer_seeds, + )?; + + token_record.bump = bump[0]; + } + + // Only freeze if the token is not already frozen, otherwise the call will fail. + // If the token is frozen already AND it has a SPL delegate set, then we + // set the state to Locked. + if !token.is_frozen() { + freeze( + mint_info.clone(), + token_info.clone(), + edition_info.clone(), + spl_token_program_info.clone(), + )?; + } else if token.delegate.is_some() { + token_record.state = TokenState::Locked; + } + + // Set Migration delegate if SPL delegate is set. + if let COption::Some(current_delegate) = token.delegate { + token_record.delegate = Some(current_delegate); + token_record.delegate_role = Some(TokenDelegateRole::Migration); + } + + token_record.save(*ctx.accounts.token_record_info.try_borrow_mut_data()?)?; + + // Migrate the token. + metadata.token_standard = Some(TokenStandard::ProgrammableNonFungible); + metadata.programmable_config = Some(ProgrammableConfig::V1 { rule_set }); + edition_info.data.borrow_mut()[TOKEN_STANDARD_INDEX] = + TokenStandard::ProgrammableNonFungible as u8; + + clean_write_metadata(&mut metadata, metadata_info)?; + } + } + + Ok(()) +} diff --git a/token-metadata/program/src/processor/metadata/mint.rs b/token-metadata/program/src/processor/metadata/mint.rs new file mode 100644 index 0000000000..0ea9bfeab8 --- /dev/null +++ b/token-metadata/program/src/processor/metadata/mint.rs @@ -0,0 +1,286 @@ +use mpl_utils::{assert_signer, cmp_pubkeys}; +use solana_program::{ + account_info::AccountInfo, + entrypoint::ProgramResult, + msg, + program::{invoke, invoke_signed}, + program_error::ProgramError, + pubkey::Pubkey, +}; +use spl_token::state::{Account, Mint as MintAccount}; + +use crate::{ + assertions::{ + assert_derivation, assert_initialized, assert_keys_equal, + assert_mint_authority_matches_mint, assert_owned_by, + }, + error::MetadataError, + instruction::{Context, Mint, MintArgs}, + pda::{find_token_record_account, EDITION, PREFIX}, + state::{Metadata, TokenMetadataAccount, TokenStandard}, + utils::{create_token_record_account, freeze, thaw}, +}; + +/// Mints tokens from a mint account. +/// +/// This instruction will also initialized the associated token account if it does not exist – in +/// this case the `token_owner` will be required. When minting `*NonFungible` assets, the `authority` +/// must be the update authority; in all other cases, it must be the mint authority from the mint +/// account. +pub fn mint<'a>( + program_id: &Pubkey, + accounts: &'a [AccountInfo<'a>], + args: MintArgs, +) -> ProgramResult { + let context = Mint::to_context(accounts)?; + match args { + MintArgs::V1 { .. } => mint_v1(program_id, context, args), + } +} + +pub fn mint_v1(program_id: &Pubkey, ctx: Context, args: MintArgs) -> ProgramResult { + // get the args for the instruction + let MintArgs::V1 { amount, .. } = args; + + if amount == 0 { + return Err(MetadataError::AmountMustBeGreaterThanZero.into()); + } + + // checks that we have the required signers + assert_signer(ctx.accounts.authority_info)?; + assert_signer(ctx.accounts.payer_info)?; + + // validates the accounts + + assert_owned_by(ctx.accounts.metadata_info, program_id)?; + assert_derivation( + program_id, + ctx.accounts.metadata_info, + &[ + PREFIX.as_bytes(), + program_id.as_ref(), + ctx.accounts.mint_info.key.as_ref(), + ], + )?; + + let metadata = Metadata::from_account_info(ctx.accounts.metadata_info)?; + if metadata.mint != *ctx.accounts.mint_info.key { + return Err(MetadataError::MintMismatch.into()); + } + + assert_owned_by(ctx.accounts.mint_info, &spl_token::id())?; + let mint: MintAccount = assert_initialized(ctx.accounts.mint_info)?; + + if !cmp_pubkeys(ctx.accounts.spl_token_program_info.key, &spl_token::id()) { + return Err(ProgramError::IncorrectProgramId); + } + + // validates the authority: + // - NonFungible must have a "valid" master edition + // - Fungible must have the authority as the mint_authority + + match metadata.token_standard { + Some(TokenStandard::ProgrammableNonFungible) | Some(TokenStandard::NonFungible) => { + // for NonFungible assets, the mint authority is the master edition + if let Some(master_edition_info) = ctx.accounts.master_edition_info { + assert_derivation( + program_id, + master_edition_info, + &[ + PREFIX.as_bytes(), + program_id.as_ref(), + ctx.accounts.mint_info.key.as_ref(), + EDITION.as_bytes(), + ], + )?; + } else { + return Err(MetadataError::MissingMasterEditionAccount.into()); + } + + if mint.supply > 0 || amount > 1 { + return Err(MetadataError::EditionsMustHaveExactlyOneToken.into()); + } + + // authority must be the update_authority of the metadata account + if !cmp_pubkeys(&metadata.update_authority, ctx.accounts.authority_info.key) { + return Err(MetadataError::UpdateAuthorityIncorrect.into()); + } + } + _ => { + assert_mint_authority_matches_mint(&mint.mint_authority, ctx.accounts.authority_info)?; + } + } + + // validates the token account + + if ctx.accounts.token_info.data_is_empty() { + // if we are initializing a new account, we need the token_owner + let token_owner_info = if let Some(token_owner_info) = ctx.accounts.token_owner_info { + token_owner_info + } else { + return Err(MetadataError::MissingTokenOwnerAccount.into()); + }; + + // if the token account is empty, we will initialize a new one but it must + // be an ATA account + assert_derivation( + &spl_associated_token_account::id(), + ctx.accounts.token_info, + &[ + token_owner_info.key.as_ref(), + spl_token::id().as_ref(), + ctx.accounts.mint_info.key.as_ref(), + ], + )?; + + msg!("Initializing associate token account"); + + // creating the associated token account + invoke( + &spl_associated_token_account::instruction::create_associated_token_account( + ctx.accounts.payer_info.key, + token_owner_info.key, + ctx.accounts.mint_info.key, + &spl_token::id(), + ), + &[ + ctx.accounts.payer_info.clone(), + token_owner_info.clone(), + ctx.accounts.mint_info.clone(), + ctx.accounts.token_info.clone(), + ], + )?; + } else { + assert_owned_by(ctx.accounts.token_info, &spl_token::id())?; + } + + msg!( + "Minting {} token(s) from mint {}", + amount, + ctx.accounts.mint_info.key + ); + let token: Account = assert_initialized(ctx.accounts.token_info)?; + + match metadata.token_standard { + Some(TokenStandard::NonFungible) | Some(TokenStandard::ProgrammableNonFungible) => { + // for pNFTs, we require the token record account + if matches!( + metadata.token_standard, + Some(TokenStandard::ProgrammableNonFungible) + ) { + // we always need the token_record_info + let token_record_info = ctx + .accounts + .token_record_info + .ok_or(MetadataError::MissingTokenRecord)?; + + let (pda_key, _) = find_token_record_account( + ctx.accounts.mint_info.key, + ctx.accounts.token_info.key, + ); + // validates the derivation + assert_keys_equal(&pda_key, token_record_info.key)?; + + if token_record_info.data_is_empty() { + msg!("Initializing token record account"); + + create_token_record_account( + program_id, + token_record_info, + ctx.accounts.mint_info, + ctx.accounts.token_info, + ctx.accounts.payer_info, + ctx.accounts.system_program_info, + )?; + } else { + assert_owned_by(token_record_info, &crate::ID)?; + } + } + + let mut signer_seeds = vec![ + PREFIX.as_bytes(), + program_id.as_ref(), + ctx.accounts.mint_info.key.as_ref(), + EDITION.as_bytes(), + ]; + + let (master_edition_key, bump) = + Pubkey::find_program_address(&signer_seeds, program_id); + let bump_seed = [bump]; + signer_seeds.push(&bump_seed); + + let master_edition_info = ctx + .accounts + .master_edition_info + .ok_or(MetadataError::MissingMasterEditionAccount)?; + + if !cmp_pubkeys(master_edition_info.key, &master_edition_key) { + return Err(MetadataError::InvalidMasterEdition.into()); + } + + // thaw the token account for programmable assets; the account + // is not frozen if we just initialized it + if matches!( + metadata.token_standard, + Some(TokenStandard::ProgrammableNonFungible) + ) && token.is_frozen() + { + thaw( + ctx.accounts.mint_info.clone(), + ctx.accounts.token_info.clone(), + master_edition_info.clone(), + ctx.accounts.spl_token_program_info.clone(), + )?; + } + + invoke_signed( + &spl_token::instruction::mint_to( + ctx.accounts.spl_token_program_info.key, + ctx.accounts.mint_info.key, + ctx.accounts.token_info.key, + &master_edition_key, + &[], + amount, + )?, + &[ + ctx.accounts.mint_info.clone(), + ctx.accounts.token_info.clone(), + master_edition_info.clone(), + ], + &[&signer_seeds], + )?; + + // programmable assets are always in a frozen state + if matches!( + metadata.token_standard, + Some(TokenStandard::ProgrammableNonFungible) + ) { + freeze( + ctx.accounts.mint_info.clone(), + ctx.accounts.token_info.clone(), + master_edition_info.clone(), + ctx.accounts.spl_token_program_info.clone(), + )?; + } + } + _ => { + invoke( + &spl_token::instruction::mint_to( + ctx.accounts.spl_token_program_info.key, + ctx.accounts.mint_info.key, + ctx.accounts.token_info.key, + ctx.accounts.authority_info.key, + &[], + amount, + )?, + &[ + ctx.accounts.mint_info.clone(), + ctx.accounts.token_info.clone(), + ctx.accounts.authority_info.clone(), + ], + )?; + } + } + + Ok(()) +} diff --git a/token-metadata/program/src/processor/metadata/mod.rs b/token-metadata/program/src/processor/metadata/mod.rs index 45099e0bf9..43061f43e4 100644 --- a/token-metadata/program/src/processor/metadata/mod.rs +++ b/token-metadata/program/src/processor/metadata/mod.rs @@ -1,15 +1,25 @@ +mod create; mod create_medatata_accounts_v3; +mod migrate; +mod mint; mod puff_metadata; mod remove_creator_verification; mod set_token_standard; mod sign_metadata; +mod transfer; +mod update; mod update_metadata_account_v2; mod update_primary_sale_happened_via_token; +pub use create::*; pub use create_medatata_accounts_v3::*; +pub use migrate::*; +pub use mint::*; pub use puff_metadata::*; pub use remove_creator_verification::*; pub use set_token_standard::*; pub use sign_metadata::*; +pub use transfer::*; +pub use update::*; pub use update_metadata_account_v2::*; pub use update_primary_sale_happened_via_token::*; diff --git a/token-metadata/program/src/processor/metadata/transfer.rs b/token-metadata/program/src/processor/metadata/transfer.rs new file mode 100644 index 0000000000..2ad8bfdd2a --- /dev/null +++ b/token-metadata/program/src/processor/metadata/transfer.rs @@ -0,0 +1,391 @@ +use std::fmt::Display; + +use borsh::BorshDeserialize; +use mpl_token_auth_rules::processor::cmp_pubkeys; +use mpl_utils::{assert_signer, token::TokenTransferParams}; +use solana_program::{ + account_info::AccountInfo, + entrypoint::ProgramResult, + msg, + program::invoke, + program_error::ProgramError, + program_option::COption, + program_pack::Pack, + pubkey::Pubkey, + system_program, + sysvar::{self, instructions::get_instruction_relative}, +}; +use spl_token::state::Account; + +use crate::{ + assertions::{assert_keys_equal, assert_owned_by, metadata::assert_holding_amount}, + error::MetadataError, + instruction::{Context, Transfer, TransferArgs}, + pda::find_token_record_account, + state::{ + AuthorityRequest, AuthorityType, Metadata, Operation, TokenDelegateRole, + TokenMetadataAccount, TokenRecord, TokenStandard, + }, + utils::{ + assert_derivation, auth_rules_validate, create_token_record_account, frozen_transfer, + AuthRulesValidateParams, + }, +}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum TransferScenario { + Holder, + TransferDelegate, + SaleDelegate, + MigrationDelegate, +} + +impl Display for TransferScenario { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Holder => write!(f, "Owner"), + Self::TransferDelegate => write!(f, "TransferDelegate"), + Self::SaleDelegate => write!(f, "SaleDelegate"), + Self::MigrationDelegate => write!(f, "MigrationDelegate"), + } + } +} + +impl From for TokenDelegateRole { + fn from(delegate: TransferScenario) -> Self { + match delegate { + TransferScenario::TransferDelegate => TokenDelegateRole::Transfer, + TransferScenario::SaleDelegate => TokenDelegateRole::Sale, + TransferScenario::MigrationDelegate => TokenDelegateRole::Migration, + _ => panic!("Invalid delegate role"), + } + } +} + +impl From for TransferScenario { + fn from(delegate: TokenDelegateRole) -> Self { + match delegate { + TokenDelegateRole::Transfer => TransferScenario::TransferDelegate, + TokenDelegateRole::Sale => TransferScenario::SaleDelegate, + TokenDelegateRole::Migration => TransferScenario::MigrationDelegate, + _ => panic!("Invalid delegate role"), + } + } +} + +pub fn transfer<'a>( + program_id: &Pubkey, + accounts: &'a [AccountInfo<'a>], + args: TransferArgs, +) -> ProgramResult { + let context = Transfer::to_context(accounts)?; + + match args { + TransferArgs::V1 { .. } => transfer_v1(program_id, context, args), + } +} + +fn transfer_v1(program_id: &Pubkey, ctx: Context, args: TransferArgs) -> ProgramResult { + msg!("Transfer V1"); + let TransferArgs::V1 { + authorization_data: auth_data, + amount, + } = args; + + // Check signers + + // This authority must always be a signer, regardless of if it's the + // actual token owner, a delegate or some other authority authorized + // by a rule set. + assert_signer(ctx.accounts.authority_info)?; + + // Assert program ownership. + assert_owned_by(ctx.accounts.metadata_info, program_id)?; + assert_owned_by(ctx.accounts.mint_info, &spl_token::ID)?; + assert_owned_by(ctx.accounts.token_info, &spl_token::ID)?; + if let Some(owner_token_record_info) = ctx.accounts.owner_token_record_info { + assert_owned_by(owner_token_record_info, program_id)?; + } + if let Some(master_edition) = ctx.accounts.edition_info { + assert_owned_by(master_edition, program_id)?; + } + if let Some(authorization_rules) = ctx.accounts.authorization_rules_info { + assert_owned_by(authorization_rules, &mpl_token_auth_rules::ID)?; + } + + // Check if the destination exists. + if ctx.accounts.destination_info.data_is_empty() { + // if the token account is empty, we will initialize a new one but it must + // be a ATA account + assert_derivation( + &spl_associated_token_account::id(), + ctx.accounts.destination_info, + &[ + ctx.accounts.destination_owner_info.key.as_ref(), + spl_token::id().as_ref(), + ctx.accounts.mint_info.key.as_ref(), + ], + )?; + + // creating the associated token account + invoke( + &spl_associated_token_account::instruction::create_associated_token_account( + ctx.accounts.payer_info.key, + ctx.accounts.destination_owner_info.key, + ctx.accounts.mint_info.key, + &spl_token::id(), + ), + &[ + ctx.accounts.payer_info.clone(), + ctx.accounts.destination_owner_info.clone(), + ctx.accounts.mint_info.clone(), + ctx.accounts.destination_info.clone(), + ], + )?; + } else { + assert_owned_by(ctx.accounts.destination_info, &spl_token::id())?; + } + + // Check program IDs. + + if ctx.accounts.spl_token_program_info.key != &spl_token::ID { + return Err(ProgramError::IncorrectProgramId); + } + + if ctx.accounts.spl_ata_program_info.key != &spl_associated_token_account::ID { + return Err(ProgramError::IncorrectProgramId); + } + + if ctx.accounts.system_program_info.key != &system_program::ID { + return Err(ProgramError::IncorrectProgramId); + } + + if ctx.accounts.sysvar_instructions_info.key != &sysvar::instructions::ID { + return Err(ProgramError::IncorrectProgramId); + } + + if let Some(auth_rules_program) = ctx.accounts.authorization_rules_program_info { + if auth_rules_program.key != &mpl_token_auth_rules::ID { + return Err(ProgramError::IncorrectProgramId); + } + } + + let mut is_wallet_to_wallet = false; + + // Deserialize metadata. + msg!("deserializing metadata"); + let metadata = Metadata::from_account_info(ctx.accounts.metadata_info)?; + + let token_transfer_params: TokenTransferParams = TokenTransferParams { + mint: ctx.accounts.mint_info.clone(), + source: ctx.accounts.token_info.clone(), + destination: ctx.accounts.destination_info.clone(), + amount, + authority: ctx.accounts.authority_info.clone(), + authority_signer_seeds: None, + token_program: ctx.accounts.spl_token_program_info.clone(), + }; + + let token_standard = metadata + .token_standard + .ok_or(MetadataError::InvalidTokenStandard)?; + let token = Account::unpack(&ctx.accounts.token_info.try_borrow_data()?)?; + + msg!("getting authority type"); + let authority_type = AuthorityType::get_authority_type(AuthorityRequest { + authority: ctx.accounts.authority_info.key, + update_authority: &metadata.update_authority, + mint: ctx.accounts.mint_info.key, + token: Some(ctx.accounts.token_info.key), + token_account: Some(&token), + token_record_info: ctx.accounts.owner_token_record_info, + token_delegate_roles: vec![ + TokenDelegateRole::Sale, + TokenDelegateRole::Transfer, + TokenDelegateRole::Migration, + ], + ..Default::default() + })?; + + match authority_type { + AuthorityType::Holder => { + msg!("Owner transfer"); + + // Wallet-to-wallet are currently exempt from auth rules so we need to check this and pass it into + // the auth rules validator function. + // + // This only applies to Holder transfers as we cannot prove a delegate transfer is + // from a proper system wallet. + + // If the program id of the current instruction is anything other than our program id + // we know this is a CPI call from another program. + let current_ix = + get_instruction_relative(0, ctx.accounts.sysvar_instructions_info).unwrap(); + + let is_cpi = !cmp_pubkeys(¤t_ix.program_id, &crate::ID); + + // This can be replaced with a sys call to curve25519 once that feature activates. + let wallets_are_system_program_owned = + cmp_pubkeys(ctx.accounts.token_owner_info.owner, &system_program::ID) + && cmp_pubkeys( + ctx.accounts.destination_owner_info.owner, + &system_program::ID, + ); + + // The only case where a transfer is wallet-to-wallet is if the wallets are both owned by + // the system program and it's not a CPI call. Holders have to be signers so we can reject + // malicious PDA signers owned by the system program by rejecting CPI calls here. + // + // Legitimate programs can use initialized PDAs or multiple instructions with a temp program-owned + // PDA to go around this restriction for cases where they are passing through a proper system wallet + // signer via an invoke call. + is_wallet_to_wallet = !is_cpi && wallets_are_system_program_owned; + + // Must be the actual current owner of the token where + // mint, token, owner and metadata accounts all match up. + assert_holding_amount( + &crate::ID, + ctx.accounts.token_owner_info, + ctx.accounts.metadata_info, + &metadata, + ctx.accounts.mint_info, + ctx.accounts.token_info, + amount, + )?; + } + AuthorityType::Delegate => { + // the delegate has already being validated, but we need to validate + // that it can transfer the required amount + if token.delegated_amount < amount || token.amount < amount { + return Err(MetadataError::NotEnoughTokens.into()); + } + } + _ => { + if matches!(token_standard, TokenStandard::ProgrammableNonFungible) { + return Err(MetadataError::InvalidAuthorityType.into()); + } + + // the authority must be either the token owner or a delegate for the + // transfer to succeed + let available_amount = if cmp_pubkeys(&token.owner, ctx.accounts.authority_info.key) { + token.amount + } else if COption::from(*ctx.accounts.authority_info.key) == token.delegate { + token.delegated_amount + } else { + return Err(MetadataError::InvalidAuthorityType.into()); + }; + + if available_amount < amount { + return Err(MetadataError::NotEnoughTokens.into()); + } + } + } + + match token_standard { + TokenStandard::ProgrammableNonFungible => { + msg!("pNFT"); + // All pNFTs should have a token record passed in and existing. + // The token delegate role may not be populated, however. + let owner_token_record_info = + if let Some(record_info) = ctx.accounts.owner_token_record_info { + record_info + } else { + return Err(MetadataError::MissingTokenRecord.into()); + }; + + let destination_token_record_info = + if let Some(record_info) = ctx.accounts.destination_token_record_info { + record_info + } else { + return Err(MetadataError::MissingTokenRecord.into()); + }; + + let (pda_key, _) = + find_token_record_account(ctx.accounts.mint_info.key, ctx.accounts.token_info.key); + // validates the derivation + assert_keys_equal(&pda_key, owner_token_record_info.key)?; + + let (new_pda_key, _) = find_token_record_account( + ctx.accounts.mint_info.key, + ctx.accounts.destination_info.key, + ); + // validates the derivation + assert_keys_equal(&new_pda_key, destination_token_record_info.key)?; + + let owner_token_record_data = owner_token_record_info.try_borrow_data()?; + let mut owner_token_record = + TokenRecord::deserialize(&mut owner_token_record_data.as_ref())?; + + msg!("checking if sale delegate"); + let is_sale_delegate = owner_token_record + .delegate_role + .map(|role| role == TokenDelegateRole::Sale) + .unwrap_or(false); + + msg!("determining scenario"); + let scenario = match authority_type { + AuthorityType::Holder => { + if is_sale_delegate { + return Err(MetadataError::OnlySaleDelegateCanTransfer.into()); + } + TransferScenario::Holder + } + AuthorityType::Delegate => { + if owner_token_record.delegate_role.is_none() { + return Err(MetadataError::MissingDelegateRole.into()); + } + + owner_token_record.delegate_role.unwrap().into() + } + _ => return Err(MetadataError::InvalidTransferAuthority.into()), + }; + + // Build our auth rules params. + let auth_rules_validate_params = AuthRulesValidateParams { + mint_info: ctx.accounts.mint_info, + owner_info: None, + authority_info: Some(ctx.accounts.authority_info), + source_info: Some(ctx.accounts.token_owner_info), + destination_info: Some(ctx.accounts.destination_owner_info), + programmable_config: metadata.programmable_config, + amount, + auth_data, + auth_rules_info: ctx.accounts.authorization_rules_info, + operation: Operation::Transfer { scenario }, + is_wallet_to_wallet, + rule_set_revision: owner_token_record + .rule_set_revision + .map(|revision| revision as usize), + }; + + auth_rules_validate(auth_rules_validate_params)?; + frozen_transfer(token_transfer_params, ctx.accounts.edition_info)?; + + // Drop old immutable borrow. + drop(owner_token_record_data); + owner_token_record.reset(); + owner_token_record.save(&mut owner_token_record_info.data.borrow_mut())?; + + // If the token record account for the destination owner doesn't exist, + // we create it. + if destination_token_record_info.data_is_empty() { + msg!("Initializing new token record account"); + + create_token_record_account( + program_id, + destination_token_record_info, + ctx.accounts.mint_info, + ctx.accounts.destination_info, + ctx.accounts.payer_info, + ctx.accounts.system_program_info, + )?; + } + } + _ => { + msg!("Transferring standard asset"); + mpl_utils::token::spl_token_transfer(token_transfer_params).unwrap() + } + } + + Ok(()) +} diff --git a/token-metadata/program/src/processor/metadata/update.rs b/token-metadata/program/src/processor/metadata/update.rs new file mode 100644 index 0000000000..f1c91e4f3e --- /dev/null +++ b/token-metadata/program/src/processor/metadata/update.rs @@ -0,0 +1,186 @@ +use std::fmt::{Display, Formatter}; + +use mpl_utils::assert_signer; +use solana_program::{ + account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError, + program_pack::Pack, pubkey::Pubkey, sysvar, +}; +use spl_token::state::Account; + +use crate::{ + assertions::{assert_owned_by, programmable::assert_valid_authorization}, + error::MetadataError, + instruction::{Context, MetadataDelegateRole, Update, UpdateArgs}, + pda::{EDITION, PREFIX}, + state::{ + AuthorityRequest, AuthorityType, Metadata, ProgrammableConfig, TokenMetadataAccount, + TokenStandard, + }, + utils::assert_derivation, +}; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum UpdateScenario { + MetadataAuth, + Delegate, + Proxy, +} + +impl Display for UpdateScenario { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + UpdateScenario::MetadataAuth => write!(f, "MetadataAuth"), + UpdateScenario::Delegate => write!(f, "Delegate"), + UpdateScenario::Proxy => write!(f, "Proxy"), + } + } +} + +pub fn update<'a>( + program_id: &Pubkey, + accounts: &'a [AccountInfo<'a>], + args: UpdateArgs, +) -> ProgramResult { + let context = Update::to_context(accounts)?; + + match args { + UpdateArgs::V1 { .. } => update_v1(program_id, context, args), + } +} + +fn update_v1(program_id: &Pubkey, ctx: Context, args: UpdateArgs) -> ProgramResult { + //** Account Validation **/ + // Assert signers + + // This account should always be a signer regardless of the authority type, + // because at least one signer is required to update the metadata. + assert_signer(ctx.accounts.authority_info)?; + + // Assert program ownership + + assert_owned_by(ctx.accounts.metadata_info, program_id)?; + assert_owned_by(ctx.accounts.mint_info, &spl_token::ID)?; + + if let Some(edition) = ctx.accounts.edition_info { + assert_owned_by(edition, program_id)?; + // checks that we got the correct master account + assert_derivation( + program_id, + edition, + &[ + PREFIX.as_bytes(), + program_id.as_ref(), + ctx.accounts.mint_info.key.as_ref(), + EDITION.as_bytes(), + ], + )?; + } + // authorization rules + if let Some(authorization_rules) = ctx.accounts.authorization_rules_info { + assert_owned_by(authorization_rules, &mpl_token_auth_rules::ID)?; + } + // token owner + if let Some(holder_token_account) = ctx.accounts.token_info { + assert_owned_by(holder_token_account, &spl_token::ID)?; + } + // delegate + if let Some(delegate_record_info) = ctx.accounts.delegate_record_info { + assert_owned_by(delegate_record_info, &crate::ID)?; + } + + // Check program IDs + + if ctx.accounts.system_program_info.key != &solana_program::system_program::ID { + return Err(ProgramError::IncorrectProgramId); + } + if ctx.accounts.sysvar_instructions_info.key != &sysvar::instructions::ID { + return Err(ProgramError::IncorrectProgramId); + } + if ctx.accounts.authorization_rules_info.is_some() { + if let Some(authorization_rules_program) = ctx.accounts.authorization_rules_program_info { + if authorization_rules_program.key != &mpl_token_auth_rules::ID { + return Err(ProgramError::IncorrectProgramId); + } + } else { + return Err(MetadataError::MissingAuthorizationRulesProgram.into()); + } + } + + // Validate relationships + + let mut metadata = Metadata::from_account_info(ctx.accounts.metadata_info)?; + // Mint must match metadata mint + if metadata.mint != *ctx.accounts.mint_info.key { + return Err(MetadataError::MintMismatch.into()); + } + + let token_standard = metadata + .token_standard + .ok_or(MetadataError::InvalidTokenStandard)?; + let (token_pubkey, token) = if let Some(token_info) = ctx.accounts.token_info { + ( + Some(token_info.key), + Some(Account::unpack(&token_info.try_borrow_data()?)?), + ) + } else { + (None, None) + }; + + // Determines if we have a valid authority to perform the update. This must + // be either the update authority, a delegate or the holder. This call fails + // if no valid authority is present. + let authority_type = AuthorityType::get_authority_type(AuthorityRequest { + authority: ctx.accounts.authority_info.key, + update_authority: &metadata.update_authority, + mint: ctx.accounts.mint_info.key, + token: token_pubkey, + token_account: token.as_ref(), + metadata_delegate_record_info: ctx.accounts.delegate_record_info, + metadata_delegate_role: Some(MetadataDelegateRole::Update), + ..Default::default() + })?; + + // for pNFTs, we need to validate the authorization rules + if matches!(token_standard, TokenStandard::ProgrammableNonFungible) { + if let Some(config) = &metadata.programmable_config { + // if we have a programmable rule set + if let ProgrammableConfig::V1 { rule_set: Some(_) } = config { + assert_valid_authorization(ctx.accounts.authorization_rules_info, config)?; + } + } + } + + match authority_type { + AuthorityType::Metadata => { + // Metadata authority is the paramount authority so is not subject to + // auth rules. At this point we already checked that the authority is a + // signer and that it matches the metadata's update authority. + msg!("Authority type: Metadata"); + } + AuthorityType::Delegate => { + // Support for delegate update (for pNFTs this involves validating the + // authoritzation rules) + msg!("Authority type: Delegate"); + return Err(MetadataError::FeatureNotSupported.into()); + } + AuthorityType::Holder => { + // Support for holder update (for pNFTs this involves validating the + // authoritzation rules) + msg!("Authority type: Holder"); + return Err(MetadataError::FeatureNotSupported.into()); + } + AuthorityType::None => { + return Err(MetadataError::UpdateAuthorityIncorrect.into()); + } + } + + // If we reach here without errors we have validated that the authority is allowed to + // perform an update. + metadata.update_v1( + args, + ctx.accounts.authority_info, + ctx.accounts.metadata_info, + )?; + + Ok(()) +} diff --git a/token-metadata/program/src/processor/mod.rs b/token-metadata/program/src/processor/mod.rs index c439044d54..ced8b1520a 100644 --- a/token-metadata/program/src/processor/mod.rs +++ b/token-metadata/program/src/processor/mod.rs @@ -1,27 +1,34 @@ mod bubblegum; mod burn; mod collection; +mod delegate; pub(crate) mod deprecated; mod edition; pub(crate) mod escrow; mod freeze; mod metadata; +mod state; mod uses; -use borsh::BorshDeserialize; +use borsh::{BorshDeserialize, BorshSerialize}; pub use bubblegum::*; -// Have to reexport for backwards compatibility pub use burn::*; pub use collection::*; -pub use deprecated::{ - process_create_metadata_accounts_v2, - process_deprecated_mint_new_edition_from_master_edition_via_vault_proxy, -}; +pub use delegate::*; +use deprecated::process_create_metadata_accounts_v2; pub use edition::*; pub use escrow::*; pub use freeze::*; pub use metadata::*; -use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, msg, pubkey::Pubkey}; +use mpl_token_auth_rules::payload::Payload; +use mpl_utils::cmp_pubkeys; +#[cfg(feature = "serde-feature")] +use serde::{Deserialize, Serialize}; +use solana_program::{ + account_info::AccountInfo, entrypoint::ProgramResult, msg, program_error::ProgramError, + pubkey::Pubkey, +}; +pub use state::*; pub use uses::*; use crate::{ @@ -37,14 +44,117 @@ use crate::{ }, escrow::process_transfer_out_of_escrow, }, + state::{ + Key, Metadata, TokenMetadataAccount, TokenStandard, TokenState, DISCRIMINATOR_INDEX, + TOKEN_STATE_INDEX, + }, }; +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub struct AuthorizationData { + pub payload: Payload, +} + +impl AuthorizationData { + pub fn new(payload: Payload) -> Self { + Self { payload } + } + pub fn new_empty() -> Self { + Self { + payload: Payload::new(), + } + } +} + +/// Process Token Metadata instructions. +/// +/// The processor is divided into two parts: +/// * It first tries to match the instruction into the new API; +/// * If it is not one of the new instructions, it checks that any metadata +/// account is not a pNFT before forwarding the transaction processing to +/// the "legacy" processor. pub fn process_instruction<'a>( program_id: &'a Pubkey, accounts: &'a [AccountInfo<'a>], input: &[u8], ) -> ProgramResult { let instruction = MetadataInstruction::try_from_slice(input)?; + + // checks if there is a locked token; this will block any instruction that + // requires the token record account when the token is locked – 'Update' is + // an example of an instruction that does not require the token record, so + // it can be executed even when a token is locked + if is_locked(program_id, accounts) && !matches!(instruction, MetadataInstruction::Unlock(_)) { + return Err(MetadataError::LockedToken.into()); + } + + // match on the new instruction set + match instruction { + MetadataInstruction::Burn(args) => { + msg!("Instruction: Token Metadata Burn"); + burn::burn(program_id, accounts, args) + } + MetadataInstruction::Create(args) => { + msg!("Instruction: Token Metadata Create"); + metadata::create(program_id, accounts, args) + } + MetadataInstruction::Mint(args) => { + msg!("Instruction: Token Metadata Mint"); + metadata::mint(program_id, accounts, args) + } + MetadataInstruction::Delegate(args) => { + msg!("Instruction: Token Metadata Delegate"); + delegate::delegate(program_id, accounts, args) + } + MetadataInstruction::Revoke(args) => { + msg!("Instruction: Token Metadata Revoke"); + delegate::revoke(program_id, accounts, args) + } + MetadataInstruction::Lock(args) => { + msg!("Instruction: Token Metadata Lock"); + state::lock(program_id, accounts, args) + } + MetadataInstruction::Unlock(args) => { + msg!("Instruction: Token Metadata Unlock"); + state::unlock(program_id, accounts, args) + } + MetadataInstruction::Migrate(args) => { + msg!("Instruction: Token Metadata Migrate"); + metadata::migrate(program_id, accounts, args) + } + MetadataInstruction::Transfer(args) => { + msg!("Instruction: Token Metadata Transfer"); + metadata::transfer(program_id, accounts, args) + } + MetadataInstruction::Update(args) => { + msg!("Instruction: Token Metadata Update"); + metadata::update(program_id, accounts, args) + } + MetadataInstruction::Verify(args) => { + msg!("Instruction: Token Metadata Verify"); + collection::verify(program_id, accounts, args) + } + _ => { + // pNFT accounts can only be used by the "new" API; before forwarding + // the transaction to the "legacy" processor we determine whether we are + // dealing with a pNFT or not + if !has_programmable_metadata(program_id, accounts)? { + process_legacy_instruction(program_id, accounts, instruction) + } else { + Err(MetadataError::InstructionNotSupported.into()) + } + } + } +} + +/// Matches "legacy" (pre-pNFT) instructions. +fn process_legacy_instruction<'a>( + program_id: &'a Pubkey, + accounts: &'a [AccountInfo<'a>], + instruction: MetadataInstruction, +) -> ProgramResult { match instruction { MetadataInstruction::CreateMetadataAccount(args) => { msg!("(Deprecated as of 1.1.0) Instruction: Create Metadata Accounts"); @@ -147,13 +257,9 @@ pub fn process_instruction<'a>( msg!("Instruction: Convert Master Edition V1 to V2"); process_convert_master_edition_v1_to_v2(program_id, accounts) } - MetadataInstruction::MintNewEditionFromMasterEditionViaVaultProxy(args) => { + MetadataInstruction::MintNewEditionFromMasterEditionViaVaultProxy(_args) => { msg!("Instruction: Mint New Edition from Master Edition Via Vault Proxy, deprecated as of 1.4.0."); - process_deprecated_mint_new_edition_from_master_edition_via_vault_proxy( - program_id, - accounts, - args.edition, - ) + Err(MetadataError::Removed.into()) } MetadataInstruction::PuffMetadata => { msg!("Instruction: Puff Metadata"); @@ -243,5 +349,111 @@ pub fn process_instruction<'a>( msg!("Instruction: Transfer Out Of Escrow"); process_transfer_out_of_escrow(program_id, accounts, args) } + _ => Err(ProgramError::InvalidInstructionData), + } +} + +/// Convenience function for accessing the next item in an [`AccountInfo`] +/// iterator and validating whether the account is present or not. +/// +/// This relies on the client setting the `crate::id()` as the pubkey for +/// accounts that are not set, which effectively allows us to use positional +/// optional accounts. +pub fn next_optional_account_info<'a, 'b, I: Iterator>>( + iter: &mut I, +) -> Result, ProgramError> { + let account_info = iter.next().ok_or(ProgramError::NotEnoughAccountKeys)?; + + Ok(if cmp_pubkeys(account_info.key, &crate::id()) { + None + } else { + Some(account_info) + }) +} + +/// Convenience function for accessing an [`AccountInfo`] by index +/// and validating whether the account is present or not. +/// +/// This relies on the client setting the `crate::id()` as the pubkey for +/// accounts that are not set, which effectively allows us to use positional +/// optional accounts. +pub fn try_get_account_info<'a>( + accounts: &'a [AccountInfo<'a>], + index: usize, +) -> Result<&'a AccountInfo<'a>, ProgramError> { + let account_info = try_get_optional_account_info(accounts, index)?; + // validates that we got an account info + if let Some(account_info) = account_info { + Ok(account_info) + } else { + Err(ProgramError::NotEnoughAccountKeys) + } +} + +/// Convenience function for accessing an [`AccountInfo`] by index +/// and validating whether the account is present or not. +/// +/// This relies on the client setting the `crate::id()` as the pubkey for +/// accounts that are not set, which effectively allows us to use positional +/// optional accounts. +pub fn try_get_optional_account_info<'a>( + accounts: &'a [AccountInfo<'a>], + index: usize, +) -> Result>, ProgramError> { + if index < accounts.len() { + Ok(if cmp_pubkeys(accounts[index].key, &crate::id()) { + None + } else { + Some(&accounts[index]) + }) + } else { + Err(ProgramError::NotEnoughAccountKeys) + } +} + +/// Checks if the instruction's accounts contain a pNFT metadata. +/// +/// We need to determine if we are dealing with a pNFT metadata or not +/// so we can restrict the available instructions. +fn has_programmable_metadata<'a>( + program_id: &Pubkey, + accounts: &'a [AccountInfo], +) -> Result { + for account_info in accounts { + // checks the account is owned by Token Metadata and it has data + if account_info.owner == program_id && !account_info.data_is_empty() { + let discriminator = account_info.data.borrow()[DISCRIMINATOR_INDEX]; + // checks if the account is a Metadata account + if discriminator == Key::MetadataV1 as u8 { + let metadata = Metadata::from_account_info(account_info)?; + + if matches!( + metadata.token_standard, + Some(TokenStandard::ProgrammableNonFungible) + ) { + return Ok(true); + } + } + } } + + Ok(false) +} + +/// Checks if the instruction's accounts contain a locked pNFT. +fn is_locked<'a>(program_id: &Pubkey, accounts: &'a [AccountInfo]) -> bool { + for account_info in accounts { + // checks the account is owned by Token Metadata and it has data + if account_info.owner == program_id && !account_info.data_is_empty() { + let data = account_info.data.borrow(); + // checks if the account is a Metadata account + if (data[DISCRIMINATOR_INDEX] == Key::TokenRecord as u8) + && (data[TOKEN_STATE_INDEX] == TokenState::Locked as u8) + { + return true; + } + } + } + + false } diff --git a/token-metadata/program/src/processor/state/lock.rs b/token-metadata/program/src/processor/state/lock.rs new file mode 100644 index 0000000000..b31c34a0b1 --- /dev/null +++ b/token-metadata/program/src/processor/state/lock.rs @@ -0,0 +1,36 @@ +use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey}; + +use super::toggle_asset_state; +use crate::{ + instruction::{Lock, LockArgs}, + state::TokenState, +}; + +pub fn lock<'a>( + program_id: &Pubkey, + accounts: &'a [AccountInfo<'a>], + args: LockArgs, +) -> ProgramResult { + let context = Lock::to_context(accounts)?; + + match args { + LockArgs::V1 { .. } => toggle_asset_state( + program_id, + super::ToggleAccounts { + payer_info: context.accounts.payer_info, + authority_info: context.accounts.authority_info, + token_owner_info: context.accounts.token_owner_info, + mint_info: context.accounts.mint_info, + token_info: context.accounts.token_info, + master_edition_info: context.accounts.edition_info, + metadata_info: context.accounts.metadata_info, + token_record_info: context.accounts.token_record_info, + system_program_info: context.accounts.system_program_info, + sysvar_instructions_info: context.accounts.sysvar_instructions_info, + spl_token_program_info: context.accounts.spl_token_program_info, + }, + TokenState::Unlocked, + TokenState::Locked, + ), + } +} diff --git a/token-metadata/program/src/processor/state/mod.rs b/token-metadata/program/src/processor/state/mod.rs new file mode 100644 index 0000000000..08ae5bdaae --- /dev/null +++ b/token-metadata/program/src/processor/state/mod.rs @@ -0,0 +1,267 @@ +mod lock; +mod unlock; + +use borsh::BorshSerialize; +pub use lock::*; +use mpl_utils::assert_signer; +use solana_program::{ + account_info::AccountInfo, entrypoint::ProgramResult, program::invoke, program_pack::Pack, + pubkey::Pubkey, system_program, sysvar, +}; +use spl_token::{ + instruction::{freeze_account, thaw_account}, + state::{Account, Mint}, +}; +pub use unlock::*; + +use crate::{ + assertions::{assert_keys_equal, metadata::assert_state}, + error::MetadataError, + pda::find_token_record_account, + state::{ + AuthorityRequest, AuthorityType, Metadata, TokenDelegateRole, TokenMetadataAccount, + TokenRecord, TokenStandard, TokenState, + }, + utils::{ + assert_delegated_tokens, assert_freeze_authority_matches_mint, assert_initialized, + assert_owned_by, freeze, thaw, + }, +}; + +pub(crate) struct ToggleAccounts<'a> { + payer_info: &'a AccountInfo<'a>, + authority_info: &'a AccountInfo<'a>, + token_owner_info: Option<&'a AccountInfo<'a>>, + mint_info: &'a AccountInfo<'a>, + token_info: &'a AccountInfo<'a>, + metadata_info: &'a AccountInfo<'a>, + master_edition_info: Option<&'a AccountInfo<'a>>, + token_record_info: Option<&'a AccountInfo<'a>>, + system_program_info: &'a AccountInfo<'a>, + sysvar_instructions_info: &'a AccountInfo<'a>, + spl_token_program_info: Option<&'a AccountInfo<'a>>, +} + +pub(crate) fn toggle_asset_state( + program_id: &Pubkey, + accounts: ToggleAccounts, + from: TokenState, + to: TokenState, +) -> ProgramResult { + // signers + + assert_signer(accounts.payer_info)?; + assert_signer(accounts.authority_info)?; + + // ownership + + assert_owned_by(accounts.metadata_info, program_id)?; + assert_owned_by(accounts.mint_info, &spl_token::id())?; + + // key match + + assert_keys_equal(accounts.system_program_info.key, &system_program::ID)?; + assert_keys_equal( + accounts.sysvar_instructions_info.key, + &sysvar::instructions::ID, + )?; + + // account relationships + + let metadata = Metadata::from_account_info(accounts.metadata_info)?; + // mint must match mint account key + if metadata.mint != *accounts.mint_info.key { + return Err(MetadataError::MintMismatch.into()); + } + + let token = Account::unpack(&accounts.token_info.try_borrow_data()?)?; + // mint must match mint account key + if token.mint != *accounts.mint_info.key { + return Err(MetadataError::MintMismatch.into()); + } + + // approver authority – this can be either: + // 1. token delegate: valid token_record.delegate + // 2. spl-delegate: for non-programmable assets, approver == token.delegate + + let authority_type = AuthorityType::get_authority_type(AuthorityRequest { + authority: accounts.authority_info.key, + update_authority: &metadata.update_authority, + mint: accounts.mint_info.key, + token: Some(accounts.token_info.key), + token_account: Some(&token), + token_record_info: accounts.token_record_info, + token_delegate_roles: vec![ + TokenDelegateRole::Utility, + TokenDelegateRole::Staking, + TokenDelegateRole::Migration, + ], + ..Default::default() + })?; + + let has_authority = match authority_type { + // holder is not allowed to lock/unlock + AuthorityType::Holder => false, + // (token) delegates can lock/unlock + AuthorityType::Delegate => true, + // if there is no authority, we checked if there is an spl-token + // delegate set (this will be the case for non-programmable assets) + _ => { + if !matches!( + metadata.token_standard, + Some(TokenStandard::ProgrammableNonFungible), + ) { + // check if the approver has an spl-token delegate + assert_delegated_tokens( + accounts.authority_info, + accounts.mint_info, + accounts.token_info, + )?; + + true + } else { + false + } + } + }; + + if !has_authority { + // approver does not have authority to lock/unlock + return Err(MetadataError::InvalidAuthorityType.into()); + } + + if matches!( + metadata.token_standard, + Some(TokenStandard::ProgrammableNonFungible) + ) { + let (mut token_record, token_record_info) = match accounts.token_record_info { + Some(token_record_info) => { + let (pda_key, _) = + find_token_record_account(accounts.mint_info.key, accounts.token_info.key); + + assert_keys_equal(&pda_key, token_record_info.key)?; + assert_owned_by(token_record_info, &crate::ID)?; + + ( + TokenRecord::from_account_info(token_record_info)?, + token_record_info, + ) + } + None => { + // token record is required for programmable assets + return Err(MetadataError::MissingTokenRecord.into()); + } + }; + + // make sure we are on the expected state + assert_state(&token_record, from)?; + // for pNFTs, we only need to flip the programmable state + token_record.state = to; + // save the state + token_record.serialize(&mut *token_record_info.try_borrow_mut_data()?)?; + } else { + // for non-programmable assets, we need to freeze the token account, + // which requires the freeze_authority/master_edition, token and spl-token program + // accounts to be on the transaction + + let mint: Mint = assert_initialized(accounts.mint_info)?; + + let (freeze_authority, is_master_edition) = match accounts.master_edition_info { + Some(master_edition_info) => { + assert_owned_by(master_edition_info, &crate::ID)?; + assert_freeze_authority_matches_mint(&mint.freeze_authority, master_edition_info)?; + (master_edition_info, true) + } + None => { + // in this case, the approver must be a spl-token delegate (which + // has been already validated), so we need to validate that we have + // the token owner + + let token_owner_info = match accounts.token_owner_info { + Some(token_owner_info) => token_owner_info, + None => { + return Err(MetadataError::MissingTokenOwnerAccount.into()); + } + }; + + assert_keys_equal(token_owner_info.key, &token.owner)?; + + (token_owner_info, false) + } + }; + + let spl_token_program_info = match accounts.spl_token_program_info { + Some(spl_token_program_info) => { + assert_keys_equal(spl_token_program_info.key, &spl_token::ID)?; + spl_token_program_info + } + None => { + return Err(MetadataError::MissingSplTokenProgram.into()); + } + }; + + match to { + TokenState::Locked => { + if is_master_edition { + // this will validate the master_edition derivation + freeze( + accounts.mint_info.clone(), + accounts.token_info.clone(), + freeze_authority.clone(), + spl_token_program_info.clone(), + )?; + } else { + // for fungible assets, we invoke spl-token directly + // since we have the freeze authority + invoke( + &freeze_account( + spl_token_program_info.key, + accounts.token_info.key, + accounts.mint_info.key, + freeze_authority.key, + &[], + )?, + &[ + accounts.token_info.clone(), + accounts.mint_info.clone(), + freeze_authority.clone(), + ], + )?; + } + } + TokenState::Unlocked => { + if is_master_edition { + // this will validate the master_edition derivation + thaw( + accounts.mint_info.clone(), + accounts.token_info.clone(), + freeze_authority.clone(), + spl_token_program_info.clone(), + )?; + } else { + // for fungible assets, we invoke spl-token directly + // since we have the freeze authority + invoke( + &thaw_account( + spl_token_program_info.key, + accounts.token_info.key, + accounts.mint_info.key, + freeze_authority.key, + &[], + )?, + &[ + accounts.token_info.clone(), + accounts.mint_info.clone(), + freeze_authority.clone(), + ], + )?; + } + } + TokenState::Listed => { + return Err(MetadataError::IncorrectTokenState.into()); + } + } + } + + Ok(()) +} diff --git a/token-metadata/program/src/processor/state/unlock.rs b/token-metadata/program/src/processor/state/unlock.rs new file mode 100644 index 0000000000..853401a536 --- /dev/null +++ b/token-metadata/program/src/processor/state/unlock.rs @@ -0,0 +1,36 @@ +use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey}; + +use super::toggle_asset_state; +use crate::{ + instruction::{Unlock, UnlockArgs}, + state::TokenState, +}; + +pub fn unlock<'a>( + program_id: &Pubkey, + accounts: &'a [AccountInfo<'a>], + args: UnlockArgs, +) -> ProgramResult { + let context = Unlock::to_context(accounts)?; + + match args { + UnlockArgs::V1 { .. } => toggle_asset_state( + program_id, + super::ToggleAccounts { + payer_info: context.accounts.payer_info, + authority_info: context.accounts.authority_info, + token_owner_info: context.accounts.token_owner_info, + mint_info: context.accounts.mint_info, + token_info: context.accounts.token_info, + master_edition_info: context.accounts.edition_info, + metadata_info: context.accounts.metadata_info, + token_record_info: context.accounts.token_record_info, + system_program_info: context.accounts.system_program_info, + sysvar_instructions_info: context.accounts.sysvar_instructions_info, + spl_token_program_info: context.accounts.spl_token_program_info, + }, + TokenState::Locked, + TokenState::Unlocked, + ), + } +} diff --git a/token-metadata/program/src/processor/uses/approve_use_authority.rs b/token-metadata/program/src/processor/uses/approve_use_authority.rs index 95cb6cadb9..c671c8813c 100644 --- a/token-metadata/program/src/processor/uses/approve_use_authority.rs +++ b/token-metadata/program/src/processor/uses/approve_use_authority.rs @@ -101,8 +101,9 @@ pub fn process_approve_use_authority( ], )?; } - let mutable_data = &mut *use_authority_record_info.try_borrow_mut_data()?; - let mut record = UseAuthorityRecord::from_bytes(*mutable_data)?; + let mutable_data = &mut (*use_authority_record_info.try_borrow_mut_data()?); + let mut record = UseAuthorityRecord::from_bytes(mutable_data)?; + record.key = Key::UseAuthorityRecord; record.allowed_uses = number_of_uses; record.bump = bump_seed; diff --git a/token-metadata/program/src/state/asset_data.rs b/token-metadata/program/src/state/asset_data.rs new file mode 100644 index 0000000000..2cb1584aed --- /dev/null +++ b/token-metadata/program/src/state/asset_data.rs @@ -0,0 +1,83 @@ +use borsh::{BorshDeserialize, BorshSerialize}; +use solana_program::pubkey::Pubkey; + +use super::*; + +/// Data representation of an asset. +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub struct AssetData { + /// The name of the asset. + pub name: String, + /// The symbol for the asset. + pub symbol: String, + /// URI pointing to JSON representing the asset. + pub uri: String, + /// Royalty basis points that goes to creators in secondary sales (0-10000). + pub seller_fee_basis_points: u16, + /// Array of creators. + pub creators: Option>, + // Immutable, once flipped, all sales of this metadata are considered secondary. + pub primary_sale_happened: bool, + // Whether or not the data struct is mutable (default is not). + pub is_mutable: bool, + /// Type of the token. + pub token_standard: TokenStandard, + /// Collection information. + pub collection: Option, + /// Uses information. + pub uses: Option, + /// Collection item details. + pub collection_details: Option, + /// Programmable rule set for the asset. + #[cfg_attr( + feature = "serde-feature", + serde( + deserialize_with = "deser_option_pubkey", + serialize_with = "ser_option_pubkey" + ) + )] + pub rule_set: Option, +} + +impl AssetData { + pub fn new(token_standard: TokenStandard, name: String, symbol: String, uri: String) -> Self { + Self { + name, + symbol, + uri, + seller_fee_basis_points: 0, + creators: None, + primary_sale_happened: false, + is_mutable: true, + token_standard, + collection: None, + uses: None, + collection_details: None, + rule_set: None, + } + } + + pub fn as_data_v2(&self) -> DataV2 { + DataV2 { + collection: self.collection.clone(), + creators: self.creators.clone(), + name: self.name.clone(), + seller_fee_basis_points: self.seller_fee_basis_points, + symbol: self.symbol.clone(), + uri: self.uri.clone(), + uses: self.uses.clone(), + } + } + + pub fn as_data(&self) -> Data { + Data { + name: self.name.clone(), + symbol: self.symbol.clone(), + uri: self.uri.clone(), + seller_fee_basis_points: self.seller_fee_basis_points, + creators: self.creators.clone(), + } + } +} diff --git a/token-metadata/program/src/state/collection.rs b/token-metadata/program/src/state/collection.rs index d1c1e84828..ba7a306b6b 100644 --- a/token-metadata/program/src/state/collection.rs +++ b/token-metadata/program/src/state/collection.rs @@ -7,6 +7,7 @@ pub const COLLECTION_AUTHORITY_RECORD_SIZE: usize = 35; #[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] pub struct Collection { pub verified: bool, + #[cfg_attr(feature = "serde-feature", serde(with = "As::"))] pub key: Pubkey, } diff --git a/token-metadata/program/src/state/delegate.rs b/token-metadata/program/src/state/delegate.rs new file mode 100644 index 0000000000..40d795aefa --- /dev/null +++ b/token-metadata/program/src/state/delegate.rs @@ -0,0 +1,55 @@ +use super::*; + +const SIZE: usize = 98; + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone, ShankAccount)] +/// SEEDS = [ +/// "metadata", +/// program id, +/// mint id, +/// delegate role, +/// update authority id, +/// delegate id +/// ] +pub struct MetadataDelegateRecord { + pub key: Key, // 1 + pub bump: u8, // 1 + #[cfg_attr(feature = "serde-feature", serde(with = "As::"))] + pub mint: Pubkey, // 32 + #[cfg_attr(feature = "serde-feature", serde(with = "As::"))] + pub delegate: Pubkey, // 32 + #[cfg_attr(feature = "serde-feature", serde(with = "As::"))] + pub update_authority: Pubkey, // 32 +} + +impl Default for MetadataDelegateRecord { + fn default() -> Self { + Self { + key: Key::MetadataDelegate, + bump: 255, + mint: Pubkey::default(), + delegate: Pubkey::default(), + update_authority: Pubkey::default(), + } + } +} + +impl TokenMetadataAccount for MetadataDelegateRecord { + fn key() -> Key { + Key::MetadataDelegate + } + + fn size() -> usize { + SIZE + } +} + +impl MetadataDelegateRecord { + pub fn from_bytes(data: &[u8]) -> Result { + let delegate: MetadataDelegateRecord = + try_from_slice_checked(data, Key::MetadataDelegate, MetadataDelegateRecord::size())?; + Ok(delegate) + } +} diff --git a/token-metadata/program/src/state/edition.rs b/token-metadata/program/src/state/edition.rs index f6692d0107..3f8a6609d7 100644 --- a/token-metadata/program/src/state/edition.rs +++ b/token-metadata/program/src/state/edition.rs @@ -13,6 +13,7 @@ pub struct Edition { pub key: Key, /// Points at MasterEdition struct + #[cfg_attr(feature = "serde-feature", serde(with = "As::"))] pub parent: Pubkey, /// Starting at 0 for master record, this is incremented for each edition minted. diff --git a/token-metadata/program/src/state/edition_marker.rs b/token-metadata/program/src/state/edition_marker.rs index e3c71d276a..7318005660 100644 --- a/token-metadata/program/src/state/edition_marker.rs +++ b/token-metadata/program/src/state/edition_marker.rs @@ -72,10 +72,7 @@ impl EditionMarker { let my_position_in_index_starting_from_right = EditionMarker::get_offset_from_right(offset_from_start)?; - Ok(( - index, - u8::pow(2, my_position_in_index_starting_from_right as u32), - )) + Ok((index, u8::pow(2, my_position_in_index_starting_from_right))) } pub fn edition_taken(&self, edition: u64) -> Result { diff --git a/token-metadata/program/src/state/master_edition.rs b/token-metadata/program/src/state/master_edition.rs index 32dd176d56..01568f4dae 100644 --- a/token-metadata/program/src/state/master_edition.rs +++ b/token-metadata/program/src/state/master_edition.rs @@ -5,6 +5,11 @@ use super::*; // changes the same account by rewriting it. pub const MAX_MASTER_EDITION_LEN: usize = 1 + 9 + 8 + 264; +// The last byte of the account containts the token standard value for +// pNFT assets. This is used to restrict legacy operations on the master +// edition account. +pub const TOKEN_STANDARD_INDEX: usize = MAX_MASTER_EDITION_LEN - 1; + pub trait MasterEdition { fn key(&self) -> Key; fn supply(&self) -> u64; @@ -81,7 +86,8 @@ impl MasterEdition for MasterEditionV2 { } fn save(&self, account: &AccountInfo) -> ProgramResult { - BorshSerialize::serialize(self, &mut *account.data.borrow_mut())?; + let mut storage = &mut account.data.borrow_mut()[..TOKEN_STANDARD_INDEX]; + BorshSerialize::serialize(self, &mut storage)?; Ok(()) } } @@ -140,7 +146,8 @@ impl MasterEdition for MasterEditionV1 { } fn save(&self, account: &AccountInfo) -> ProgramResult { - BorshSerialize::serialize(self, &mut *account.data.borrow_mut())?; + let mut storage = &mut account.data.borrow_mut()[..TOKEN_STANDARD_INDEX]; + BorshSerialize::serialize(self, &mut storage)?; Ok(()) } } diff --git a/token-metadata/program/src/state/metadata.rs b/token-metadata/program/src/state/metadata.rs index bc43420075..7210454d09 100644 --- a/token-metadata/program/src/state/metadata.rs +++ b/token-metadata/program/src/state/metadata.rs @@ -1,4 +1,12 @@ use super::*; +use crate::{ + assertions::{ + collection::assert_collection_update_is_valid, metadata::assert_data_valid, + uses::assert_valid_use, + }, + instruction::{CollectionDetailsToggle, CollectionToggle, RuleSetToggle, UpdateArgs}, + utils::{clean_write_metadata, puff_out_data_fields}, +}; pub const MAX_NAME_LENGTH: usize = 32; @@ -16,7 +24,9 @@ pub const MAX_METADATA_LEN: usize = 1 // key + 2 // token standard + 34 // collection + 18 // uses -+ 118; // Padding ++ 10 // collection details ++ 33 // programmable config ++ 75; // Padding pub const MAX_DATA_SIZE: usize = 4 + MAX_NAME_LENGTH @@ -33,9 +43,15 @@ pub const MAX_DATA_SIZE: usize = 4 #[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] #[derive(Clone, BorshSerialize, Debug, PartialEq, Eq, ShankAccount)] pub struct Metadata { + /// Account discriminator. pub key: Key, + /// Address of the update authority. + #[cfg_attr(feature = "serde-feature", serde(with = "As::"))] pub update_authority: Pubkey, + /// Address of the mint. + #[cfg_attr(feature = "serde-feature", serde(with = "As::"))] pub mint: Pubkey, + /// Asset data. pub data: Data, // Immutable, once flipped, all sales of this metadata are considered secondary. pub primary_sale_happened: bool, @@ -49,8 +65,159 @@ pub struct Metadata { pub collection: Option, /// Uses pub uses: Option, - /// Item Details + /// Collection Details pub collection_details: Option, + /// Programmable Config + pub programmable_config: Option, +} + +impl Metadata { + pub fn save(&self, data: &mut [u8]) -> Result<(), BorshError> { + let mut bytes = Vec::with_capacity(MAX_METADATA_LEN); + BorshSerialize::serialize(&self, &mut bytes)?; + data[..bytes.len()].copy_from_slice(&bytes); + Ok(()) + } + + pub(crate) fn update_v1<'a>( + &mut self, + args: UpdateArgs, + update_authority: &AccountInfo<'a>, + metadata: &AccountInfo<'a>, + ) -> ProgramResult { + let UpdateArgs::V1 { + data, + primary_sale_happened, + is_mutable, + collection, + uses, + new_update_authority, + rule_set, + collection_details, + .. + } = args; + + if let Some(data) = data { + if !self.is_mutable { + return Err(MetadataError::DataIsImmutable.into()); + } + + assert_data_valid( + &data, + update_authority.key, + self, + false, + update_authority.is_signer, + )?; + self.data = data; + } + + // if the Collection data is 'Set', only allow updating if it's unverified + // or if it exactly matches the existing collection info; if the Collection data + // is 'Clear', then only set to 'None' it if it's unverified. + match collection { + CollectionToggle::Set(_) => { + let collection_option = collection.to_option(); + assert_collection_update_is_valid(false, &self.collection, &collection_option)?; + self.collection = collection_option; + } + CollectionToggle::Clear => { + if let Some(current_collection) = self.collection.as_ref() { + // Can't change a verified collection in this command. + if current_collection.verified { + return Err(MetadataError::CannotUpdateVerifiedCollection.into()); + } + // If it's unverified, it's ok to set to None. + self.collection = None; + } + } + CollectionToggle::None => { /* nothing to do */ } + } + + if uses.is_some() { + let uses_option = uses.to_option(); + // If already None leave it as None. + assert_valid_use(&uses_option, &self.uses)?; + self.uses = uses_option; + } + + if let Some(authority) = new_update_authority { + self.update_authority = authority; + } + + if let Some(primary_sale) = primary_sale_happened { + // If received primary_sale is true, flip to true. + if primary_sale || !self.primary_sale_happened { + self.primary_sale_happened = primary_sale + } else { + return Err(MetadataError::PrimarySaleCanOnlyBeFlippedToTrue.into()); + } + } + + if let Some(mutable) = is_mutable { + // If received value is false, flip to false. + if !mutable || self.is_mutable { + self.is_mutable = mutable + } else { + return Err(MetadataError::IsMutableCanOnlyBeFlippedToFalse.into()); + } + } + + let token_standard = self + .token_standard + .ok_or(MetadataError::InvalidTokenStandard)?; + + // if the rule_set data is either 'Set' or 'Clear', only allow updating if the + // token standard is equal to `ProgrammableNonFungible` + if matches!(rule_set, RuleSetToggle::Clear | RuleSetToggle::Set(_)) { + if token_standard != TokenStandard::ProgrammableNonFungible { + return Err(MetadataError::InvalidTokenStandard.into()); + } + + self.programmable_config = + rule_set.to_option().map(|rule_set| ProgrammableConfig::V1 { + rule_set: Some(rule_set), + }); + } + + if let CollectionDetailsToggle::Set(collection_details) = collection_details { + // only unsized collections can have the size set, and only once. + if self.collection_details.is_some() { + return Err(MetadataError::SizedCollection.into()); + } + + self.collection_details = Some(collection_details); + } + + puff_out_data_fields(self); + clean_write_metadata(self, metadata)?; + + Ok(()) + } + + pub fn into_asset_data(self) -> AssetData { + let mut asset_data = AssetData::new( + self.token_standard.unwrap_or(TokenStandard::NonFungible), + self.data.name, + self.data.symbol, + self.data.uri, + ); + asset_data.seller_fee_basis_points = self.data.seller_fee_basis_points; + asset_data.creators = self.data.creators; + asset_data.primary_sale_happened = self.primary_sale_happened; + asset_data.is_mutable = self.is_mutable; + asset_data.collection = self.collection; + asset_data.uses = self.uses; + asset_data.collection_details = self.collection_details; + asset_data.rule_set = + if let Some(ProgrammableConfig::V1 { rule_set }) = self.programmable_config { + rule_set + } else { + None + }; + + asset_data + } } impl Default for Metadata { @@ -67,6 +234,7 @@ impl Default for Metadata { collection: None, uses: None, collection_details: None, + programmable_config: None, } } } @@ -91,6 +259,48 @@ impl borsh::de::BorshDeserialize for Metadata { } } +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +/// Represents the print supply of a non-fungible asset. +pub enum PrintSupply { + /// The asset does not have any prints. + Zero, + /// The asset has a limited amount of prints. + Limited(u64), + /// The asset has an unlimited amount of prints. + Unlimited, +} + +impl PrintSupply { + /// Converts the print supply to an option. + pub fn to_option(&self) -> Option { + match self { + PrintSupply::Zero => Some(0), + PrintSupply::Limited(supply) => Some(*supply), + PrintSupply::Unlimited => None, + } + } +} + +/// Configuration for programmable assets. +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub enum ProgrammableConfig { + V1 { + /// Programmable authorization rules. + #[cfg_attr( + feature = "serde-feature", + serde( + deserialize_with = "deser_option_pubkey", + serialize_with = "ser_option_pubkey" + ) + )] + rule_set: Option, + }, +} + #[cfg(test)] mod tests { use borsh::{BorshDeserialize, BorshSerialize}; diff --git a/token-metadata/program/src/state/migrate.rs b/token-metadata/program/src/state/migrate.rs new file mode 100644 index 0000000000..7736262239 --- /dev/null +++ b/token-metadata/program/src/state/migrate.rs @@ -0,0 +1,9 @@ +use super::*; + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(Clone, BorshSerialize, BorshDeserialize, Debug, PartialEq, Eq)] +pub enum MigrationType { + CollectionV1, + ProgrammableV1, +} diff --git a/token-metadata/program/src/state/mod.rs b/token-metadata/program/src/state/mod.rs index 6faca40ba3..c02bbce599 100644 --- a/token-metadata/program/src/state/mod.rs +++ b/token-metadata/program/src/state/mod.rs @@ -1,27 +1,36 @@ +pub(crate) mod asset_data; pub(crate) mod collection; pub(crate) mod creator; pub(crate) mod data; +pub(crate) mod delegate; pub(crate) mod edition; pub(crate) mod edition_marker; pub(crate) mod escrow; pub(crate) mod master_edition; pub(crate) mod metadata; +pub(crate) mod migrate; +pub(crate) mod programmable; pub(crate) mod reservation; +pub(crate) mod token_auth_payload; pub(crate) mod uses; use std::io::ErrorKind; +pub use asset_data::*; use borsh::{maybestd::io::Error as BorshError, BorshDeserialize, BorshSerialize}; pub use collection::*; pub use creator::*; pub use data::*; +pub use delegate::*; pub use edition::*; pub use edition_marker::*; pub use escrow::*; pub use master_edition::*; pub use metadata::*; +pub use migrate::*; use num_derive::FromPrimitive; use num_traits::FromPrimitive; +pub use programmable::*; pub use reservation::*; use shank::ShankAccount; use solana_program::{ @@ -31,8 +40,9 @@ use solana_program::{ pub use uses::*; #[cfg(feature = "serde-feature")] use { - serde::{Deserialize, Serialize}, + serde::{Deserialize, Deserializer, Serialize}, serde_with::{As, DisplayFromStr}, + std::str::FromStr, }; // Re-export constants to maintain compatibility. @@ -44,14 +54,18 @@ use crate::{ ID, }; +/// Index of the discriminator on the account data. +pub const DISCRIMINATOR_INDEX: usize = 0; + #[repr(C)] #[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] -#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone, FromPrimitive)] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone, Copy, FromPrimitive)] pub enum TokenStandard { - NonFungible, // This is a master edition - FungibleAsset, // A token with metadata that can also have attrributes - Fungible, // A token with simple metadata - NonFungibleEdition, // This is a limited edition + NonFungible, // This is a master edition + FungibleAsset, // A token with metadata that can also have attrributes + Fungible, // A token with simple metadata + NonFungibleEdition, // This is a limited edition + ProgrammableNonFungible, // NonFungible with programmable configuration } pub trait TokenMetadataAccount: BorshDeserialize { @@ -93,8 +107,9 @@ pub trait TokenMetadataAccount: BorshDeserialize { fn from_account_info(a: &AccountInfo) -> Result where { - let ua = Self::safe_deserialize(&a.data.borrow_mut()) - .map_err(|_| MetadataError::DataTypeMismatch)?; + let data = &a.data.borrow_mut(); + + let ua = Self::safe_deserialize(data).map_err(|_| MetadataError::DataTypeMismatch)?; // Check that this is a `token-metadata` owned account. assert_owned_by(a, &ID)?; @@ -118,4 +133,26 @@ pub enum Key { UseAuthorityRecord, CollectionAuthorityRecord, TokenOwnedEscrow, + TokenRecord, + MetadataDelegate, +} + +#[cfg(feature = "serde-feature")] +fn deser_option_pubkey<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + as serde::de::Deserialize>::deserialize(deserializer)? + .map(|s| Pubkey::from_str(&s)) + .transpose() + .map_err(serde::de::Error::custom) +} + +#[cfg(feature = "serde-feature")] +fn ser_option_pubkey(pubkey: &Option, serializer: S) -> Result +where + S: serde::Serializer, +{ + let pubkey_string = pubkey.as_ref().map(|p| p.to_string()); + serde::ser::Serialize::serialize(&pubkey_string, serializer) } diff --git a/token-metadata/program/src/state/programmable.rs b/token-metadata/program/src/state/programmable.rs new file mode 100644 index 0000000000..1ac398d2bb --- /dev/null +++ b/token-metadata/program/src/state/programmable.rs @@ -0,0 +1,317 @@ +use borsh::{BorshDeserialize, BorshSerialize}; +use mpl_utils::cmp_pubkeys; +use num_derive::ToPrimitive; +#[cfg(feature = "serde-feature")] +use serde::{Deserialize, Serialize}; +use shank::ShankAccount; +use solana_program::{ + account_info::AccountInfo, instruction::AccountMeta, program_error::ProgramError, + program_option::COption, pubkey::Pubkey, +}; +use spl_token::state::Account; + +use super::*; +use crate::{ + error::MetadataError, + instruction::MetadataDelegateRole, + pda::{find_metadata_delegate_record_account, find_token_record_account}, + processor::{TransferScenario, UpdateScenario}, + state::BorshError, + utils::{assert_owned_by, try_from_slice_checked}, +}; + +/// Empty pubkey constant. +const DEFAULT_PUBKEY: Pubkey = Pubkey::new_from_array([0u8; 32]); + +pub const TOKEN_RECORD_SEED: &str = "token_record"; + +pub const TOKEN_STATE_INDEX: usize = 2; + +pub const TOKEN_RECORD_SIZE: usize = 1 // Key ++ 1 // bump ++ 1 // state ++ 9 // rule set revision ++ 33 // delegate ++ 2; // delegate role + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone, ShankAccount)] +/// SEEDS = [ +/// "metadata", +/// program id, +/// mint id, +/// "token_record", +/// token owner id +/// ] +pub struct TokenRecord { + pub key: Key, + pub bump: u8, + pub state: TokenState, + pub rule_set_revision: Option, + #[cfg_attr( + feature = "serde-feature", + serde( + deserialize_with = "deser_option_pubkey", + serialize_with = "ser_option_pubkey" + ) + )] + pub delegate: Option, + pub delegate_role: Option, +} + +impl Default for TokenRecord { + fn default() -> Self { + Self { + key: Key::TokenRecord, + bump: 255, + state: TokenState::Unlocked, + rule_set_revision: None, + delegate: None, + delegate_role: None, + } + } +} + +impl TokenMetadataAccount for TokenRecord { + fn key() -> Key { + Key::TokenRecord + } + + fn size() -> usize { + TOKEN_RECORD_SIZE + } +} + +impl TokenRecord { + pub fn from_bytes(data: &[u8]) -> Result { + let record: TokenRecord = + try_from_slice_checked(data, Key::TokenRecord, TokenRecord::size())?; + Ok(record) + } + + pub fn is_locked(&self) -> bool { + matches!(self.state, TokenState::Locked) + } + + /// Resets the token state by clearing any state stored. + pub fn reset(&mut self) { + self.state = TokenState::Unlocked; + self.rule_set_revision = None; + self.delegate = None; + self.delegate_role = None; + } + + pub fn save(&self, data: &mut [u8]) -> Result<(), BorshError> { + let mut storage = &mut data[..Self::size()]; + BorshSerialize::serialize(self, &mut storage)?; + Ok(()) + } +} + +/// Programmable account state. +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub enum TokenState { + /// Token account is unlocked; operations are allowed on this account. + Unlocked, + /// Token account has been locked; no operations are allowed on this account. + Locked, + /// Token account has a `Sale` delegate set; operations are restricted. + Listed, +} + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone, Copy)] +pub enum TokenDelegateRole { + Sale, + Transfer, + Utility, + Staking, + Standard, + Migration = 255, +} + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub enum AuthorityType { + None, + Metadata, + Delegate, + Holder, +} + +pub struct AuthorityRequest<'a, 'b> { + /// Pubkey of the authority. + pub authority: &'a Pubkey, + /// Metadata's update authority pubkey of the asset. + pub update_authority: &'b Pubkey, + /// Mint address. + pub mint: &'a Pubkey, + /// Holder's token account info. + pub token: Option<&'a Pubkey>, + /// Holder's token account. + pub token_account: Option<&'b Account>, + /// `MetadataDelegateRecord` account of the authority (when the authority is a delegate). + pub metadata_delegate_record_info: Option<&'a AccountInfo<'a>>, + /// Expected `MetadataDelegateRole` for the request. + pub metadata_delegate_role: Option, + /// `TokenRecord` account. + pub token_record_info: Option<&'a AccountInfo<'a>>, + /// Expected `TokenDelegateRole` for the request. + pub token_delegate_roles: Vec, +} + +impl<'a, 'b> Default for AuthorityRequest<'a, 'b> { + fn default() -> Self { + Self { + authority: &DEFAULT_PUBKEY, + update_authority: &DEFAULT_PUBKEY, + mint: &DEFAULT_PUBKEY, + token: None, + token_account: None, + metadata_delegate_record_info: None, + metadata_delegate_role: None, + token_record_info: None, + token_delegate_roles: Vec::with_capacity(0), + } + } +} + +impl AuthorityType { + /// Determines the `AuthorityType`. + pub fn get_authority_type(request: AuthorityRequest) -> Result { + // checks if the authority is a token delegate + + if let Some(token_record_info) = request.token_record_info { + // must be owned by token medatata + assert_owned_by(token_record_info, &crate::ID)?; + + // we can only validate if it is a token delegate when we have the token account + if let Some(token_account) = request.token_account { + let token = request.token.ok_or(MetadataError::MissingTokenAccount)?; + + let (pda_key, _) = find_token_record_account(request.mint, token); + let token_record = TokenRecord::from_account_info(token_record_info)?; + + let role_matches = match token_record.delegate_role { + Some(role) => request.token_delegate_roles.contains(&role), + None => request.token_delegate_roles.is_empty(), + }; + + if cmp_pubkeys(&pda_key, token_record_info.key) + && Some(*request.authority) == token_record.delegate + && role_matches + && (COption::from(token_record.delegate) == token_account.delegate) + { + return Ok(AuthorityType::Delegate); + } + } + } + + // checks if the authority is a metadata delegate + + if let Some(metadata_delegate_record_info) = request.metadata_delegate_record_info { + // must be owned by token medatata + assert_owned_by(metadata_delegate_record_info, &crate::ID)?; + + if let Some(delegate_role) = request.metadata_delegate_role { + let (pda_key, _) = find_metadata_delegate_record_account( + request.mint, + delegate_role, + request.update_authority, + request.authority, + ); + + if cmp_pubkeys(&pda_key, metadata_delegate_record_info.key) { + let delegate_record = + MetadataDelegateRecord::from_account_info(metadata_delegate_record_info)?; + + if delegate_record.delegate == *request.authority { + return Ok(AuthorityType::Delegate); + } + } + } + } + + // checks if the authority is the token owner + + if let Some(token_account) = request.token_account { + if cmp_pubkeys(&token_account.owner, request.authority) { + return Ok(AuthorityType::Holder); + } + } + + // checks if the authority is the update authority + + if cmp_pubkeys(request.update_authority, request.authority) { + return Ok(AuthorityType::Metadata); + } + + Ok(AuthorityType::None) + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum Operation { + Transfer { scenario: TransferScenario }, + Update { scenario: UpdateScenario }, +} + +impl ToString for Operation { + fn to_string(&self) -> String { + match self { + Self::Transfer { scenario } => format!("Transfer:{}", scenario), + Self::Update { scenario } => format!("Update:{}", scenario), + } + } +} + +#[derive(Debug, Clone, ToPrimitive)] +pub enum PayloadKey { + Amount, + Authority, + AuthoritySeeds, + Delegate, + DelegateSeeds, + Destination, + DestinationSeeds, + Holder, + Source, + SourceSeeds, +} + +impl ToString for PayloadKey { + fn to_string(&self) -> String { + match self { + PayloadKey::Amount => "Amount", + PayloadKey::Authority => "Authority", + PayloadKey::AuthoritySeeds => "AuthoritySeeds", + PayloadKey::Delegate => "Delegate", + PayloadKey::DelegateSeeds => "DelegateSeeds", + PayloadKey::Destination => "Destination", + PayloadKey::DestinationSeeds => "DestinationSeeds", + PayloadKey::Holder => "Holder", + PayloadKey::Source => "Source", + PayloadKey::SourceSeeds => "SourceSeeds", + } + .to_string() + } +} + +pub trait ToAccountMeta { + fn to_account_meta(&self) -> AccountMeta; +} + +impl<'info> ToAccountMeta for AccountInfo<'info> { + fn to_account_meta(&self) -> AccountMeta { + AccountMeta { + pubkey: *self.key, + is_signer: self.is_signer, + is_writable: self.is_writable, + } + } +} diff --git a/token-metadata/program/src/state/token_auth_payload.rs b/token-metadata/program/src/state/token_auth_payload.rs new file mode 100644 index 0000000000..10f6b1350d --- /dev/null +++ b/token-metadata/program/src/state/token_auth_payload.rs @@ -0,0 +1,48 @@ +use std::collections::HashMap; + +use borsh::{BorshDeserialize, BorshSerialize}; +#[cfg(feature = "serde-feature")] +use serde::{Deserialize, Serialize}; +use solana_program::pubkey::Pubkey; + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub struct SeedsVec { + pub seeds: Vec>, +} + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub struct LeafInfo { + pub leaf: [u8; 32], + pub proof: Vec<[u8; 32]>, +} + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone)] +pub enum PayloadType { + Pubkey(Pubkey), + Seeds(SeedsVec), + MerkleProof(LeafInfo), + Number(u64), +} + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialEq, Eq, Debug, Clone, Default)] +pub struct Payload { + map: HashMap, +} + +#[repr(C)] +#[cfg_attr(feature = "serde-feature", derive(Serialize, Deserialize))] +#[derive(BorshSerialize, BorshDeserialize, PartialOrd, Hash, PartialEq, Eq, Debug, Clone, Copy)] +pub enum PayloadKey { + Target, + Holder, + Authority, + Amount, +} diff --git a/token-metadata/program/src/utils/compression.rs b/token-metadata/program/src/utils/compression.rs index 6cf2e375be..e66b357755 100644 --- a/token-metadata/program/src/utils/compression.rs +++ b/token-metadata/program/src/utils/compression.rs @@ -1,7 +1,6 @@ use mpl_utils::cmp_pubkeys; use solana_program::{account_info::AccountInfo, pubkey, pubkey::Pubkey}; -use super::*; pub const BUBBLEGUM_PROGRAM_ADDRESS: Pubkey = pubkey!("BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY"); diff --git a/token-metadata/program/src/utils/master_edition.rs b/token-metadata/program/src/utils/master_edition.rs index b0f2655166..b194e71816 100644 --- a/token-metadata/program/src/utils/master_edition.rs +++ b/token-metadata/program/src/utils/master_edition.rs @@ -8,19 +8,20 @@ use solana_program::{ account_info::AccountInfo, entrypoint::ProgramResult, program_error::ProgramError, pubkey::Pubkey, }; -use spl_token::state::Account; +use spl_token::state::{Account, Mint}; use super::*; use crate::{ assertions::{ assert_derivation, assert_initialized, assert_mint_authority_matches_mint, assert_owned_by, assert_token_program_matches_package, edition::assert_edition_valid, + metadata::assert_update_authority_is_correct, }, error::MetadataError, state::{ - get_reservation_list, DataV2, EditionMarker, Key, Metadata, TokenMetadataAccount, Uses, - EDITION, EDITION_MARKER_BIT_SIZE, MAX_EDITION_LEN, MAX_EDITION_MARKER_SIZE, - MAX_MASTER_EDITION_LEN, PREFIX, + get_reservation_list, DataV2, EditionMarker, Key, MasterEdition, Metadata, + TokenMetadataAccount, Uses, EDITION, EDITION_MARKER_BIT_SIZE, MAX_EDITION_LEN, + MAX_EDITION_MARKER_SIZE, MAX_MASTER_EDITION_LEN, PREFIX, }, }; @@ -459,3 +460,95 @@ pub fn mint_limited_edition<'a>( Ok(()) } + +/// Creates a new master edition account for the specified `edition_account_info` and +/// `mint_info`. Master editions only exist for non-fungible assets, therefore the supply +/// of the mint must thei either 0 or 1; any value higher than that will generate an +/// error. +/// +/// After a master edition is created, it becomes the mint authority of the mint account. +pub fn create_master_edition<'a>( + program_id: &Pubkey, + edition_account_info: &'a AccountInfo<'a>, + mint_info: &'a AccountInfo<'a>, + update_authority_info: &'a AccountInfo<'a>, + mint_authority_info: &'a AccountInfo<'a>, + payer_account_info: &'a AccountInfo<'a>, + metadata_account_info: &'a AccountInfo<'a>, + token_program_info: &'a AccountInfo<'a>, + system_account_info: &'a AccountInfo<'a>, + max_supply: Option, +) -> ProgramResult { + let metadata = Metadata::from_account_info(metadata_account_info)?; + let mint: Mint = assert_initialized(mint_info)?; + + let bump_seed = assert_derivation( + program_id, + edition_account_info, + &[ + PREFIX.as_bytes(), + program_id.as_ref(), + mint_info.key.as_ref(), + EDITION.as_bytes(), + ], + )?; + + assert_token_program_matches_package(token_program_info)?; + assert_mint_authority_matches_mint(&mint.mint_authority, mint_authority_info)?; + assert_owned_by(metadata_account_info, program_id)?; + assert_owned_by(mint_info, &spl_token::id())?; + + if metadata.mint != *mint_info.key { + return Err(MetadataError::MintMismatch.into()); + } + + if mint.decimals != 0 { + return Err(MetadataError::EditionMintDecimalsShouldBeZero.into()); + } + + assert_update_authority_is_correct(&metadata, update_authority_info)?; + + if mint.supply > 1 { + return Err(MetadataError::EditionsMustHaveExactlyOneToken.into()); + } + + let edition_authority_seeds = &[ + PREFIX.as_bytes(), + program_id.as_ref(), + mint_info.key.as_ref(), + EDITION.as_bytes(), + &[bump_seed], + ]; + + create_or_allocate_account_raw( + *program_id, + edition_account_info, + system_account_info, + payer_account_info, + MAX_MASTER_EDITION_LEN, + edition_authority_seeds, + )?; + + let mut edition = MasterEditionV2::from_account_info(edition_account_info)?; + + edition.key = Key::MasterEditionV2; + edition.supply = 0; + edition.max_supply = max_supply; + edition.save(edition_account_info)?; + + if metadata_account_info.is_writable { + let mut metadata_mut = Metadata::from_account_info(metadata_account_info)?; + metadata_mut.token_standard = Some(TokenStandard::NonFungible); + metadata_mut.save(&mut metadata_account_info.try_borrow_mut_data()?)?; + } + + // while you can't mint only mint 1 token from your master record, you can + // mint as many limited editions as you like within your max supply + transfer_mint_authority( + edition_account_info.key, + edition_account_info, + mint_info, + mint_authority_info, + token_program_info, + ) +} diff --git a/token-metadata/program/src/utils/metadata.rs b/token-metadata/program/src/utils/metadata.rs index d25f86564f..7d36de6f70 100644 --- a/token-metadata/program/src/utils/metadata.rs +++ b/token-metadata/program/src/utils/metadata.rs @@ -12,8 +12,8 @@ use crate::{ uses::assert_valid_use, }, state::{ - Collection, CollectionDetails, Data, DataV2, Key, Metadata, TokenStandard, Uses, EDITION, - MAX_METADATA_LEN, PREFIX, + Collection, CollectionDetails, Data, DataV2, Key, Metadata, ProgrammableConfig, + TokenStandard, Uses, EDITION, MAX_METADATA_LEN, PREFIX, }, }; @@ -64,8 +64,10 @@ pub fn process_create_metadata_accounts_logic( let mut update_authority_key = *update_authority_info.key; let existing_mint_authority = get_mint_authority(mint_info)?; - // IMPORTANT NOTE - // This allows the Metaplex Foundation to Create but not update metadata for SPL tokens that have not populated their metadata. + + // IMPORTANT NOTE: + // This allows the Metaplex Foundation to Create but not update metadata for SPL tokens that + // have not populated their metadata. assert_mint_authority_matches_mint(&existing_mint_authority, mint_authority_info).or_else( |e| { // Allow seeding by the authority seed populator @@ -115,7 +117,6 @@ pub fn process_create_metadata_accounts_logic( // This allows the Bubblegum program to create metadata with verified creators since they were // verified already by the Bubblegum program. - // let allow_direct_creator_writes = allow_direct_creator_writes || is_decompression(mint_info, mint_authority_info); @@ -175,7 +176,8 @@ pub fn process_create_metadata_accounts_logic( ]; let (_, edition_bump_seed) = Pubkey::find_program_address(edition_seeds, program_id); metadata.edition_nonce = Some(edition_bump_seed); - metadata.serialize(&mut *metadata_account_info.data.borrow_mut())?; + // saves the changes to the account data + metadata.save(&mut metadata_account_info.data.borrow_mut())?; Ok(()) } @@ -207,9 +209,13 @@ pub fn meta_deser_unchecked(buf: &mut &[u8]) -> Result { let collection_details_res: Result, BorshError> = BorshDeserialize::deserialize(buf); - /* We can have accidentally valid, but corrupted data, particularly on the Collection struct, - so to increase probability of catching errors If any of these deserializations fail, set all values to None. - */ + // pNFT - Programmable Config + let programmable_config_res: Result, BorshError> = + BorshDeserialize::deserialize(buf); + + // We can have accidentally valid, but corrupted data, particularly on the Collection struct, + // so to increase probability of catching errors. If any of these deserializations fail, set + // all values to None. let (token_standard, collection, uses) = match (token_standard_res, collection_res, uses_res) { (Ok(token_standard_res), Ok(collection_res), Ok(uses_res)) => { (token_standard_res, collection_res, uses_res) @@ -217,12 +223,15 @@ pub fn meta_deser_unchecked(buf: &mut &[u8]) -> Result { _ => (None, None, None), }; - // Handle v1.3 separately + // V1.3 let collection_details = match collection_details_res { Ok(details) => details, Err(_) => None, }; + // Programmable Config + let programmable_config = programmable_config_res.unwrap_or(None); + let metadata = Metadata { key, update_authority, @@ -235,6 +244,7 @@ pub fn meta_deser_unchecked(buf: &mut &[u8]) -> Result { collection, uses, collection_details, + programmable_config, }; Ok(metadata) @@ -336,6 +346,7 @@ pub mod tests { collection: None, uses: None, collection_details: None, + programmable_config: None, }; puff_out_data_fields(&mut metadata); diff --git a/token-metadata/program/src/utils/mod.rs b/token-metadata/program/src/utils/mod.rs index 8650142a40..735749b648 100644 --- a/token-metadata/program/src/utils/mod.rs +++ b/token-metadata/program/src/utils/mod.rs @@ -2,19 +2,8 @@ pub(crate) mod collection; pub(crate) mod compression; pub(crate) mod master_edition; pub(crate) mod metadata; +pub(crate) mod programmable_asset; -pub use crate::assertions::{ - assert_delegated_tokens, assert_derivation, assert_freeze_authority_matches_mint, - assert_initialized, assert_mint_authority_matches_mint, assert_owned_by, assert_rent_exempt, - assert_token_program_matches_package, - edition::{ - assert_edition_is_not_mint_authority, assert_edition_valid, assert_supply_invariance, - }, - metadata::{ - assert_currently_holding, assert_data_valid, assert_update_authority_is_correct, - assert_verified_member_of_collection, - }, -}; pub use collection::*; pub use compression::*; pub use master_edition::*; @@ -27,12 +16,23 @@ pub use mpl_utils::{ get_owner_from_token_account, spl_token_burn, spl_token_close, spl_token_mint_to, }, }; +pub use programmable_asset::*; use solana_program::{ account_info::AccountInfo, borsh::try_from_slice_unchecked, entrypoint::ProgramResult, msg, - program::invoke_signed, program_error::ProgramError, pubkey::Pubkey, + program::invoke_signed, program_error::ProgramError, pubkey::Pubkey, system_program, }; use spl_token::instruction::{set_authority, AuthorityType}; +pub use crate::assertions::{ + assert_delegated_tokens, assert_derivation, assert_freeze_authority_matches_mint, + assert_initialized, assert_mint_authority_matches_mint, assert_owned_by, assert_rent_exempt, + assert_token_program_matches_package, + edition::{assert_edition_is_not_mint_authority, assert_edition_valid}, + metadata::{ + assert_currently_holding, assert_data_valid, assert_update_authority_is_correct, + assert_verified_member_of_collection, + }, +}; use crate::{ error::MetadataError, state::{ @@ -185,6 +185,25 @@ pub fn zero_account(s: &str, size: usize) -> String { s.to_owned() + std::str::from_utf8(&array_of_zeroes).unwrap() } +pub fn close_program_account<'a>( + account_info: &AccountInfo<'a>, + funds_dest_account_info: &AccountInfo<'a>, +) -> ProgramResult { + // Transfer lamports from the account to the destination account. + let dest_starting_lamports = funds_dest_account_info.lamports(); + **funds_dest_account_info.lamports.borrow_mut() = dest_starting_lamports + .checked_add(account_info.lamports()) + .unwrap(); + **account_info.lamports.borrow_mut() = 0; + + // Realloc the account data size to 0 bytes and teassign ownership of + // the account to the system program + account_info.realloc(0, false)?; + account_info.assign(&system_program::ID); + + Ok(()) +} + #[cfg(test)] mod tests { pub use solana_program::pubkey::Pubkey; @@ -239,6 +258,7 @@ mod tests { uses: None, token_standard: None, collection_details: None, + programmable_config: None, }; puff_out_data_fields(&mut metadata); diff --git a/token-metadata/program/src/utils/programmable_asset.rs b/token-metadata/program/src/utils/programmable_asset.rs new file mode 100644 index 0000000000..2ae060ac79 --- /dev/null +++ b/token-metadata/program/src/utils/programmable_asset.rs @@ -0,0 +1,321 @@ +use borsh::BorshSerialize; +use mpl_token_auth_rules::{ + instruction::{builders::ValidateBuilder, InstructionBuilder, ValidateArgs}, + payload::PayloadType, +}; +use mpl_utils::{create_or_allocate_account_raw, token::TokenTransferParams}; +use solana_program::{ + account_info::AccountInfo, entrypoint::ProgramResult, msg, program::invoke_signed, + program_error::ProgramError, pubkey::Pubkey, +}; +use spl_token::instruction::{freeze_account, thaw_account}; + +use crate::{ + assertions::{assert_derivation, programmable::assert_valid_authorization}, + error::MetadataError, + pda::{EDITION, PREFIX}, + processor::{AuthorizationData, TransferScenario}, + state::{ + Operation, PayloadKey, ProgrammableConfig, ToAccountMeta, TokenMetadataAccount, + TokenRecord, TOKEN_RECORD_SEED, + }, +}; + +pub fn create_token_record_account<'a>( + program_id: &Pubkey, + token_record_info: &'a AccountInfo<'a>, + mint_info: &'a AccountInfo<'a>, + token_info: &'a AccountInfo<'a>, + payer_info: &'a AccountInfo<'a>, + system_program_info: &'a AccountInfo<'a>, +) -> ProgramResult { + if !token_record_info.data_is_empty() { + return Err(MetadataError::DelegateAlreadyExists.into()); + } + + let mut signer_seeds = Vec::from([ + PREFIX.as_bytes(), + crate::ID.as_ref(), + mint_info.key.as_ref(), + TOKEN_RECORD_SEED.as_bytes(), + token_info.key.as_ref(), + ]); + + let bump = &[assert_derivation( + program_id, + token_record_info, + &signer_seeds, + )?]; + signer_seeds.push(bump); + + // allocate the delegate account + + create_or_allocate_account_raw( + *program_id, + token_record_info, + system_program_info, + payer_info, + TokenRecord::size(), + &signer_seeds, + )?; + + let token_record = TokenRecord { + bump: bump[0], + ..Default::default() + }; + token_record.serialize(&mut *token_record_info.try_borrow_mut_data()?)?; + + Ok(()) +} + +pub fn freeze<'a>( + mint: AccountInfo<'a>, + token: AccountInfo<'a>, + edition: AccountInfo<'a>, + spl_token_program: AccountInfo<'a>, +) -> ProgramResult { + let edition_info_path = Vec::from([ + PREFIX.as_bytes(), + crate::ID.as_ref(), + mint.key.as_ref(), + EDITION.as_bytes(), + ]); + let edition_info_path_bump_seed = &[assert_derivation( + &crate::id(), + &edition, + &edition_info_path, + )?]; + let mut edition_info_seeds = edition_info_path.clone(); + edition_info_seeds.push(edition_info_path_bump_seed); + + invoke_signed( + &freeze_account(spl_token_program.key, token.key, mint.key, edition.key, &[]).unwrap(), + &[token, mint, edition], + &[&edition_info_seeds], + )?; + Ok(()) +} + +pub fn thaw<'a>( + mint_info: AccountInfo<'a>, + token_info: AccountInfo<'a>, + edition_info: AccountInfo<'a>, + spl_token_program: AccountInfo<'a>, +) -> ProgramResult { + let edition_info_path = Vec::from([ + PREFIX.as_bytes(), + crate::ID.as_ref(), + mint_info.key.as_ref(), + EDITION.as_bytes(), + ]); + let edition_info_path_bump_seed = &[assert_derivation( + &crate::id(), + &edition_info, + &edition_info_path, + )?]; + let mut edition_info_seeds = edition_info_path.clone(); + edition_info_seeds.push(edition_info_path_bump_seed); + + invoke_signed( + &thaw_account( + spl_token_program.key, + token_info.key, + mint_info.key, + edition_info.key, + &[], + ) + .unwrap(), + &[token_info, mint_info, edition_info], + &[&edition_info_seeds], + )?; + Ok(()) +} + +pub fn validate<'a>( + ruleset: &'a AccountInfo<'a>, + operation: Operation, + mint_info: &'a AccountInfo<'a>, + additional_rule_accounts: Vec<&'a AccountInfo<'a>>, + auth_data: &AuthorizationData, + rule_set_revision: Option, +) -> Result<(), ProgramError> { + let account_metas = additional_rule_accounts + .iter() + .map(|account| account.to_account_meta()) + .collect(); + + let validate_ix = ValidateBuilder::new() + .rule_set_pda(*ruleset.key) + .mint(*mint_info.key) + .additional_rule_accounts(account_metas) + .build(ValidateArgs::V1 { + operation: operation.to_string(), + payload: auth_data.payload.clone(), + update_rule_state: false, + rule_set_revision, + }) + .map_err(|_error| MetadataError::InvalidAuthorizationRules)? + .instruction(); + + let mut account_infos = vec![ruleset.clone(), mint_info.clone()]; + account_infos.extend(additional_rule_accounts.into_iter().cloned()); + invoke_signed(&validate_ix, account_infos.as_slice(), &[]) +} + +#[derive(Debug, Clone)] +pub struct AuthRulesValidateParams<'a> { + pub mint_info: &'a AccountInfo<'a>, + pub source_info: Option<&'a AccountInfo<'a>>, + pub destination_info: Option<&'a AccountInfo<'a>>, + pub authority_info: Option<&'a AccountInfo<'a>>, + pub owner_info: Option<&'a AccountInfo<'a>>, + pub programmable_config: Option, + pub amount: u64, + pub auth_data: Option, + pub auth_rules_info: Option<&'a AccountInfo<'a>>, + pub operation: Operation, + pub is_wallet_to_wallet: bool, + pub rule_set_revision: Option, +} + +pub fn auth_rules_validate(params: AuthRulesValidateParams) -> ProgramResult { + let AuthRulesValidateParams { + mint_info, + owner_info, + source_info, + destination_info, + authority_info, + programmable_config, + amount, + auth_data, + auth_rules_info, + operation, + is_wallet_to_wallet, + rule_set_revision, + } = params; + + if is_wallet_to_wallet { + msg!("Wallet to wallet transfer. Skipping auth rules validation"); + return Ok(()); + } + + if let Operation::Transfer { scenario } = &operation { + // Migration delegate is allowed to skip auth rules to guarantee that + // it can transfer the asset. + if matches!(scenario, TransferScenario::MigrationDelegate) { + return Ok(()); + } + } + + if let Some(ref config) = programmable_config { + if let ProgrammableConfig::V1 { rule_set: Some(_) } = config { + msg!("Programmable config exists"); + + assert_valid_authorization(auth_rules_info, config)?; + + msg!("valid auth data. Adding rules..."); + // We can safely unwrap here because they were all checked for existence + // in the assertion above. + let auth_pda = auth_rules_info.unwrap(); + + let mut auth_data = if let Some(auth_data) = auth_data { + auth_data + } else { + AuthorizationData::new_empty() + }; + + let mut additional_rule_accounts = vec![]; + if let Some(target_info) = source_info { + additional_rule_accounts.push(target_info); + } + if let Some(target_info) = destination_info { + additional_rule_accounts.push(target_info); + } + if let Some(authority_info) = authority_info { + additional_rule_accounts.push(authority_info); + } + if let Some(owner_info) = owner_info { + additional_rule_accounts.push(owner_info); + } + + // Insert auth rules for the operation type. + match operation { + Operation::Transfer { scenario: _ } => { + // Get account infos + let authority_info = authority_info.ok_or(MetadataError::InvalidOperation)?; + let source_info = source_info.ok_or(MetadataError::InvalidOperation)?; + let destination_info = + destination_info.ok_or(MetadataError::InvalidOperation)?; + + // Transfer Amount + auth_data + .payload + .insert(PayloadKey::Amount.to_string(), PayloadType::Number(amount)); + + // Transfer Authority + auth_data.payload.insert( + PayloadKey::Authority.to_string(), + PayloadType::Pubkey(*authority_info.key), + ); + + // Transfer Source + auth_data.payload.insert( + PayloadKey::Source.to_string(), + PayloadType::Pubkey(*source_info.key), + ); + + // Transfer Destination + auth_data.payload.insert( + PayloadKey::Destination.to_string(), + PayloadType::Pubkey(*destination_info.key), + ); + } + _ => { + return Err(MetadataError::InvalidOperation.into()); + } + } + + validate( + auth_pda, + operation, + mint_info, + additional_rule_accounts, + &auth_data, + rule_set_revision, + )?; + } + } + Ok(()) +} + +pub fn frozen_transfer<'a, 'b>( + params: TokenTransferParams<'a, 'b>, + edition_opt_info: Option<&'a AccountInfo<'a>>, +) -> ProgramResult { + if edition_opt_info.is_none() { + return Err(MetadataError::MissingEditionAccount.into()); + } + let master_edition_info = edition_opt_info.unwrap(); + + thaw( + params.mint.clone(), + params.source.clone(), + master_edition_info.clone(), + params.token_program.clone(), + )?; + + let mint_info = params.mint.clone(); + let dest_info = params.destination.clone(); + let token_program_info = params.token_program.clone(); + + mpl_utils::token::spl_token_transfer(params).unwrap(); + + freeze( + mint_info, + dest_info.clone(), + master_edition_info.clone(), + token_program_info.clone(), + )?; + + Ok(()) +} diff --git a/token-metadata/program/tests/burn_edition_nft.rs b/token-metadata/program/tests/burn_edition_nft.rs index 7b67fa8733..098777df1f 100644 --- a/token-metadata/program/tests/burn_edition_nft.rs +++ b/token-metadata/program/tests/burn_edition_nft.rs @@ -690,6 +690,8 @@ mod burn_edition_nft { .await .unwrap(); + context.warp_to_slot(10).unwrap(); + let print_editions = master_edition .mint_editions(&mut context, &original_nft, 10) .await diff --git a/token-metadata/program/tests/create.rs b/token-metadata/program/tests/create.rs new file mode 100644 index 0000000000..743630effe --- /dev/null +++ b/token-metadata/program/tests/create.rs @@ -0,0 +1,450 @@ +#![cfg(feature = "test-bpf")] +pub mod utils; + +use mpl_token_metadata::{ + id, + state::{Key, MAX_NAME_LENGTH, MAX_SYMBOL_LENGTH, MAX_URI_LENGTH}, + utils::puffed_out_string, +}; +use num_traits::FromPrimitive; +use solana_program::pubkey::Pubkey; +use solana_program_test::*; +use solana_sdk::{ + instruction::InstructionError, + signature::{Keypair, Signer}, + transaction::{Transaction, TransactionError}, +}; +use utils::*; + +mod create { + + use std::str::FromStr; + + use mpl_token_metadata::{ + error::MetadataError, + instruction::{builders::CreateBuilder, CreateArgs, InstructionBuilder}, + state::{ + AssetData, Metadata, PrintSupply, ProgrammableConfig, TokenStandard, EDITION, PREFIX, + }, + }; + use solana_program::borsh::try_from_slice_unchecked; + + use super::*; + + #[tokio::test] + async fn create_programmable_nonfungible() { + let mut context = program_test().start_with_context().await; + + // asset details + + let name = puffed_out_string("Programmable NFT", MAX_NAME_LENGTH); + let symbol = puffed_out_string("PRG", MAX_SYMBOL_LENGTH); + let uri = puffed_out_string("uri", MAX_URI_LENGTH); + + let mut asset = AssetData::new( + TokenStandard::ProgrammableNonFungible, + name.clone(), + symbol.clone(), + uri.clone(), + ); + asset.seller_fee_basis_points = 500; + asset.rule_set = + Some(Pubkey::from_str("Cex6GAMtCwD9E17VsEK4rQTbmcVtSdHxWcxhwdwXkuAN").unwrap()); + + // build the mint transaction + + let payer_pubkey = context.payer.pubkey(); + let mint = Keypair::new(); + let mint_pubkey = mint.pubkey(); + + let program_id = id(); + // metadata PDA address + let metadata_seeds = &[PREFIX.as_bytes(), program_id.as_ref(), mint_pubkey.as_ref()]; + let (metadata, _) = Pubkey::find_program_address(metadata_seeds, &id()); + // master edition PDA address + let master_edition_seeds = &[ + PREFIX.as_bytes(), + program_id.as_ref(), + mint_pubkey.as_ref(), + EDITION.as_bytes(), + ]; + let (master_edition, _) = Pubkey::find_program_address(master_edition_seeds, &id()); + + let create_ix = CreateBuilder::new() + .metadata(metadata) + .master_edition(master_edition) + .mint(mint.pubkey()) + .authority(payer_pubkey) + .payer(payer_pubkey) + .update_authority(payer_pubkey) + .initialize_mint(true) + .update_authority_as_signer(true) + .build(CreateArgs::V1 { + asset_data: asset, + decimals: Some(0), + print_supply: Some(PrintSupply::Zero), + }) + .unwrap() + .instruction(); + + let tx = Transaction::new_signed_with_payer( + &[create_ix], + Some(&context.payer.pubkey()), + &[&context.payer, &mint], + context.last_blockhash, + ); + + context.banks_client.process_transaction(tx).await.unwrap(); + + // checks the created metadata values + + let metadata_account = get_account(&mut context, &metadata).await; + let metadata: Metadata = try_from_slice_unchecked(&metadata_account.data).unwrap(); + + assert_eq!(metadata.data.name, name); + assert_eq!(metadata.data.symbol, symbol); + assert_eq!(metadata.data.uri, uri); + assert_eq!(metadata.data.seller_fee_basis_points, 500); + assert_eq!(metadata.data.creators, None); + + assert!(!metadata.primary_sale_happened); + assert!(metadata.is_mutable); + assert_eq!(metadata.mint, mint_pubkey); + assert_eq!(metadata.update_authority, context.payer.pubkey()); + assert_eq!(metadata.key, Key::MetadataV1); + + assert_eq!( + metadata.token_standard, + Some(TokenStandard::ProgrammableNonFungible) + ); + assert_eq!(metadata.uses, None); + assert_eq!(metadata.collection, None); + if let Some(ProgrammableConfig::V1 { + rule_set: Some(rule_set), + }) = metadata.programmable_config + { + assert_eq!( + rule_set, + Pubkey::from_str("Cex6GAMtCwD9E17VsEK4rQTbmcVtSdHxWcxhwdwXkuAN").unwrap() + ); + } else { + panic!("Missing programmable config"); + } + } + + #[tokio::test] + async fn create_programmable_nonfungible_with_existing_mint() { + let mut context = program_test().start_with_context().await; + + // asset details + + let name = puffed_out_string("Programmable NFT", MAX_NAME_LENGTH); + let symbol = puffed_out_string("PRG", MAX_SYMBOL_LENGTH); + let uri = puffed_out_string("uri", MAX_URI_LENGTH); + + let mut asset = AssetData::new( + TokenStandard::ProgrammableNonFungible, + name.clone(), + symbol.clone(), + uri.clone(), + ); + asset.seller_fee_basis_points = 500; + /* + asset.programmable_config = Some(ProgrammableConfig { + rule_set: Pubkey::from_str("Cex6GAMtCwD9E17VsEK4rQTbmcVtSdHxWcxhwdwXkuAN")?, + }); + */ + + // creates the mint + + let payer_pubkey = context.payer.pubkey(); + let mint = Keypair::new(); + let mint_pubkey = mint.pubkey(); + + create_mint(&mut context, &mint, &payer_pubkey, Some(&payer_pubkey), 0) + .await + .unwrap(); + + // build the create metadata transaction + + let program_id = id(); + // metadata PDA address + let metadata_seeds = &[PREFIX.as_bytes(), program_id.as_ref(), mint_pubkey.as_ref()]; + let (metadata, _) = Pubkey::find_program_address(metadata_seeds, &id()); + // master edition PDA address + let master_edition_seeds = &[ + PREFIX.as_bytes(), + program_id.as_ref(), + mint_pubkey.as_ref(), + EDITION.as_bytes(), + ]; + let (master_edition, _) = Pubkey::find_program_address(master_edition_seeds, &id()); + + let create_ix = CreateBuilder::new() + .metadata(metadata) + .master_edition(master_edition) + .mint(mint.pubkey()) + .authority(payer_pubkey) + .payer(payer_pubkey) + .update_authority(payer_pubkey) + .initialize_mint(false) + .update_authority_as_signer(true) + .build(CreateArgs::V1 { + asset_data: asset, + decimals: Some(0), + print_supply: Some(PrintSupply::Zero), + }) + .unwrap() + .instruction(); + + let tx = Transaction::new_signed_with_payer( + &[create_ix], + Some(&context.payer.pubkey()), + &[&context.payer], + context.last_blockhash, + ); + + context.banks_client.process_transaction(tx).await.unwrap(); + + // checks the created metadata values + + let metadata_account = get_account(&mut context, &metadata).await; + let metadata: Metadata = try_from_slice_unchecked(&metadata_account.data).unwrap(); + + assert_eq!(metadata.data.name, name); + assert_eq!(metadata.data.symbol, symbol); + assert_eq!(metadata.data.uri, uri); + assert_eq!(metadata.data.seller_fee_basis_points, 500); + assert_eq!(metadata.data.creators, None); + + assert!(!metadata.primary_sale_happened); + assert!(metadata.is_mutable); + assert_eq!(metadata.mint, mint_pubkey); + assert_eq!(metadata.update_authority, context.payer.pubkey()); + assert_eq!(metadata.key, Key::MetadataV1); + + assert_eq!( + metadata.token_standard, + Some(TokenStandard::ProgrammableNonFungible) + ); + assert_eq!(metadata.uses, None); + assert_eq!(metadata.collection, None); + assert_eq!( + metadata.programmable_config, + Some(ProgrammableConfig::V1 { rule_set: None }) + ); + } + + #[tokio::test] + async fn create_programmable_nonfungible_with_invalid_mint() { + let mut context = program_test().start_with_context().await; + + // asset details + + let name = puffed_out_string("Programmable NFT", MAX_NAME_LENGTH); + let symbol = puffed_out_string("PRG", MAX_SYMBOL_LENGTH); + let uri = puffed_out_string("uri", MAX_URI_LENGTH); + + let mut asset = AssetData::new( + TokenStandard::ProgrammableNonFungible, + name.clone(), + symbol.clone(), + uri.clone(), + ); + asset.seller_fee_basis_points = 500; + /* + asset.programmable_config = Some(ProgrammableConfig { + rule_set: Pubkey::from_str("Cex6GAMtCwD9E17VsEK4rQTbmcVtSdHxWcxhwdwXkuAN")?, + }); + */ + + // creates the mint + + let payer_pubkey = context.payer.pubkey(); + let mint = Keypair::new(); + let mint_pubkey = mint.pubkey(); + + // invalid number of digits for non-fungible assets + create_mint(&mut context, &mint, &payer_pubkey, Some(&payer_pubkey), 2) + .await + .unwrap(); + + // build the create metadata transaction + + let program_id = id(); + // metadata PDA address + let metadata_seeds = &[PREFIX.as_bytes(), program_id.as_ref(), mint_pubkey.as_ref()]; + let (metadata, _) = Pubkey::find_program_address(metadata_seeds, &id()); + // master edition PDA address + let master_edition_seeds = &[ + PREFIX.as_bytes(), + program_id.as_ref(), + mint_pubkey.as_ref(), + EDITION.as_bytes(), + ]; + let (master_edition, _) = Pubkey::find_program_address(master_edition_seeds, &id()); + + let create_ix = CreateBuilder::new() + .metadata(metadata) + .master_edition(master_edition) + .mint(mint.pubkey()) + .authority(payer_pubkey) + .payer(payer_pubkey) + .update_authority(payer_pubkey) + .initialize_mint(false) + .update_authority_as_signer(true) + .build(CreateArgs::V1 { + asset_data: asset, + decimals: Some(0), + print_supply: Some(PrintSupply::Zero), + }) + .unwrap() + .instruction(); + + let tx = Transaction::new_signed_with_payer( + &[create_ix], + Some(&context.payer.pubkey()), + &[&context.payer], + context.last_blockhash, + ); + + let error = context + .banks_client + .process_transaction(tx) + .await + .unwrap_err(); + assert_custom_error!(error, MetadataError::InvalidMintForTokenStandard); + } + + #[tokio::test] + async fn create_nonfungible() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create(&mut context, TokenStandard::NonFungible, None) + .await + .unwrap(); + + // checks the created metadata values + + let metadata_account = get_account(&mut context, &asset.metadata).await; + let metadata: Metadata = try_from_slice_unchecked(&metadata_account.data).unwrap(); + + assert_eq!( + metadata.data.name, + puffed_out_string(DEFAULT_NAME, MAX_NAME_LENGTH) + ); + assert_eq!( + metadata.data.symbol, + puffed_out_string(DEFAULT_SYMBOL, MAX_SYMBOL_LENGTH) + ); + assert_eq!( + metadata.data.uri, + puffed_out_string(DEFAULT_URI, MAX_URI_LENGTH) + ); + assert!(metadata.data.creators.is_some()); + + assert!(!metadata.primary_sale_happened); + assert!(metadata.is_mutable); + assert_eq!(metadata.mint, asset.mint.pubkey()); + assert_eq!(metadata.update_authority, context.payer.pubkey()); + assert_eq!(metadata.key, Key::MetadataV1); + + assert_eq!(metadata.token_standard, Some(TokenStandard::NonFungible)); + assert_eq!(metadata.uses, None); + assert_eq!(metadata.collection, None); + assert_eq!(metadata.programmable_config, None); + assert!(asset.master_edition.is_some()); + } + + #[tokio::test] + async fn create_fungible() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create(&mut context, TokenStandard::Fungible, None) + .await + .unwrap(); + + // checks the created metadata values + + let metadata_account = get_account(&mut context, &asset.metadata).await; + let metadata: Metadata = try_from_slice_unchecked(&metadata_account.data).unwrap(); + + assert_eq!( + metadata.data.name, + puffed_out_string(DEFAULT_NAME, MAX_NAME_LENGTH) + ); + assert_eq!( + metadata.data.symbol, + puffed_out_string(DEFAULT_SYMBOL, MAX_SYMBOL_LENGTH) + ); + assert_eq!( + metadata.data.uri, + puffed_out_string(DEFAULT_URI, MAX_URI_LENGTH) + ); + assert!(metadata.data.creators.is_some()); + + assert!(!metadata.primary_sale_happened); + assert!(metadata.is_mutable); + assert_eq!(metadata.mint, asset.mint.pubkey()); + assert_eq!(metadata.update_authority, context.payer.pubkey()); + assert_eq!(metadata.key, Key::MetadataV1); + + assert_eq!(metadata.token_standard, Some(TokenStandard::Fungible)); + assert_eq!(metadata.uses, None); + assert_eq!(metadata.collection, None); + assert_eq!(metadata.programmable_config, None); + assert!(asset.master_edition.is_none()); + } + + #[tokio::test] + async fn create_fungible_asset() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create(&mut context, TokenStandard::FungibleAsset, None) + .await + .unwrap(); + + // checks the created metadata values + + let metadata_account = get_account(&mut context, &asset.metadata).await; + let metadata: Metadata = try_from_slice_unchecked(&metadata_account.data).unwrap(); + + assert_eq!( + metadata.data.name, + puffed_out_string(DEFAULT_NAME, MAX_NAME_LENGTH) + ); + assert_eq!( + metadata.data.symbol, + puffed_out_string(DEFAULT_SYMBOL, MAX_SYMBOL_LENGTH) + ); + assert_eq!( + metadata.data.uri, + puffed_out_string(DEFAULT_URI, MAX_URI_LENGTH) + ); + assert!(metadata.data.creators.is_some()); + + assert!(!metadata.primary_sale_happened); + assert!(metadata.is_mutable); + assert_eq!(metadata.mint, asset.mint.pubkey()); + assert_eq!(metadata.update_authority, context.payer.pubkey()); + assert_eq!(metadata.key, Key::MetadataV1); + + assert_eq!(metadata.token_standard, Some(TokenStandard::FungibleAsset)); + assert_eq!(metadata.uses, None); + assert_eq!(metadata.collection, None); + assert_eq!(metadata.programmable_config, None); + assert!(asset.master_edition.is_none()); + } +} diff --git a/token-metadata/program/tests/create_metadata_account.rs b/token-metadata/program/tests/create_metadata_account.rs index c4f6b82e78..40b6314ac8 100644 --- a/token-metadata/program/tests/create_metadata_account.rs +++ b/token-metadata/program/tests/create_metadata_account.rs @@ -576,8 +576,9 @@ mod create_meta_accounts { } #[tokio::test] + #[allow(deprecated)] async fn fail_bubblegum_owner() { - let mut context = &mut program_test().start_with_context().await; + let context = &mut program_test().start_with_context().await; let test_metadata = Metadata::new(); let name = "Test".to_string(); let symbol = "TST".to_string(); diff --git a/token-metadata/program/tests/delegate.rs b/token-metadata/program/tests/delegate.rs new file mode 100644 index 0000000000..f0a7b0b998 --- /dev/null +++ b/token-metadata/program/tests/delegate.rs @@ -0,0 +1,396 @@ +#![cfg(feature = "test-bpf")] +pub mod utils; + +use solana_program_test::*; +use solana_sdk::{ + instruction::InstructionError, + signature::{Keypair, Signer}, + transaction::TransactionError, +}; +use utils::*; + +mod delegate { + + use mpl_token_metadata::{ + error::MetadataError, + instruction::{DelegateArgs, MetadataDelegateRole}, + pda::{find_metadata_delegate_record_account, find_token_record_account}, + state::{ + Key, Metadata, MetadataDelegateRecord, TokenDelegateRole, TokenRecord, TokenStandard, + }, + }; + use num_traits::FromPrimitive; + use solana_program::{ + borsh::try_from_slice_unchecked, program_option::COption, program_pack::Pack, + }; + use spl_token::state::Account; + + use super::*; + + #[tokio::test] + async fn set_transfer_delegate_programmable_nonfungible() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create_and_mint( + &mut context, + TokenStandard::ProgrammableNonFungible, + None, + None, + 1, + ) + .await + .unwrap(); + + assert!(asset.token.is_some()); + + // delegates the asset for transfer + + let user = Keypair::new(); + let user_pubkey = user.pubkey(); + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + asset + .delegate( + &mut context, + payer, + user_pubkey, + DelegateArgs::TransferV1 { + amount: 1, + authorization_data: None, + }, + ) + .await + .unwrap(); + + // asserts + + let (pda_key, _) = find_token_record_account(&asset.mint.pubkey(), &asset.token.unwrap()); + + let pda = get_account(&mut context, &pda_key).await; + let token_record: TokenRecord = try_from_slice_unchecked(&pda.data).unwrap(); + + assert_eq!(token_record.key, Key::TokenRecord); + assert_eq!(token_record.delegate, Some(user_pubkey)); + assert_eq!( + token_record.delegate_role, + Some(TokenDelegateRole::Transfer) + ); + + if let Some(token) = asset.token { + let account = get_account(&mut context, &token).await; + let token_account = Account::unpack(&account.data).unwrap(); + + assert!(token_account.is_frozen()); + assert_eq!(token_account.delegate, COption::Some(user_pubkey)); + assert_eq!(token_account.delegated_amount, 1); + } else { + panic!("Missing token account"); + } + } + + #[tokio::test] + async fn set_collection_delegate_programmable_nonfungible() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create_and_mint( + &mut context, + TokenStandard::ProgrammableNonFungible, + None, + None, + 1, + ) + .await + .unwrap(); + + assert!(asset.token.is_some()); + + let metadata_account = get_account(&mut context, &asset.metadata).await; + let metadata: Metadata = try_from_slice_unchecked(&metadata_account.data).unwrap(); + assert_eq!(metadata.update_authority, context.payer.pubkey()); + + // creates a collection delegate + + let user = Keypair::new(); + let user_pubkey = user.pubkey(); + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + let payer_pubkey = payer.pubkey(); + + asset + .delegate( + &mut context, + payer, + user_pubkey, + DelegateArgs::CollectionV1 { + authorization_data: None, + }, + ) + .await + .unwrap(); + + // asserts + + let (pda_key, _) = find_metadata_delegate_record_account( + &asset.mint.pubkey(), + MetadataDelegateRole::Collection, + &payer_pubkey, + &user_pubkey, + ); + + let pda = get_account(&mut context, &pda_key).await; + let delegate_record = MetadataDelegateRecord::from_bytes(&pda.data).unwrap(); + assert_eq!(delegate_record.key, Key::MetadataDelegate); + } + + #[tokio::test] + async fn set_sale_delegate_programmable_nonfungible() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create_and_mint( + &mut context, + TokenStandard::ProgrammableNonFungible, + None, + None, + 1, + ) + .await + .unwrap(); + + assert!(asset.token.is_some()); + + // delegates the asset for transfer + + let user = Keypair::new(); + let user_pubkey = user.pubkey(); + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + asset + .delegate( + &mut context, + payer, + user_pubkey, + DelegateArgs::SaleV1 { + amount: 1, + authorization_data: None, + }, + ) + .await + .unwrap(); + + // asserts + + let (pda_key, _) = find_token_record_account(&asset.mint.pubkey(), &asset.token.unwrap()); + + let pda = get_account(&mut context, &pda_key).await; + let token_record: TokenRecord = try_from_slice_unchecked(&pda.data).unwrap(); + + assert_eq!(token_record.key, Key::TokenRecord); + assert_eq!(token_record.delegate, Some(user_pubkey)); + assert_eq!(token_record.delegate_role, Some(TokenDelegateRole::Sale)); + + if let Some(token) = asset.token { + let account = get_account(&mut context, &token).await; + let token_account = Account::unpack(&account.data).unwrap(); + + assert!(token_account.is_frozen()); + assert_eq!(token_account.delegate, COption::Some(user_pubkey)); + assert_eq!(token_account.delegated_amount, 1); + } else { + panic!("Missing token account"); + } + } + + #[tokio::test] + async fn cannot_set_sale_delegate_nonfungible() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create_and_mint(&mut context, TokenStandard::NonFungible, None, None, 1) + .await + .unwrap(); + + assert!(asset.token.is_some()); + + // delegates the asset for transfer + + let user = Keypair::new(); + let user_pubkey = user.pubkey(); + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + let error = asset + .delegate( + &mut context, + payer, + user_pubkey, + DelegateArgs::SaleV1 { + amount: 1, + authorization_data: None, + }, + ) + .await + .unwrap_err(); + + // asserts + + assert_custom_error!(error, MetadataError::InvalidDelegateRole); + } + + #[tokio::test] + async fn set_standard_delegate_nonfungible() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create_and_mint(&mut context, TokenStandard::NonFungible, None, None, 1) + .await + .unwrap(); + + assert!(asset.token.is_some()); + + // delegates the asset for transfer + + let user = Keypair::new(); + let user_pubkey = user.pubkey(); + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + asset + .delegate( + &mut context, + payer, + user_pubkey, + DelegateArgs::StandardV1 { amount: 1 }, + ) + .await + .unwrap(); + } + + #[tokio::test] + async fn cannot_set_utility_delegate_nonfungible() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create_and_mint(&mut context, TokenStandard::NonFungible, None, None, 1) + .await + .unwrap(); + + assert!(asset.token.is_some()); + + // delegates the asset for transfer + + let user = Keypair::new(); + let user_pubkey = user.pubkey(); + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + let error = asset + .delegate( + &mut context, + payer, + user_pubkey, + DelegateArgs::UtilityV1 { + amount: 1, + authorization_data: None, + }, + ) + .await + .unwrap_err(); + + assert_custom_error!(error, MetadataError::InvalidDelegateRole); + } + + #[tokio::test] + async fn store_rule_set_revision_on_delegate() { + let mut program_test = ProgramTest::new("mpl_token_metadata", mpl_token_metadata::ID, None); + program_test.add_program("mpl_token_auth_rules", mpl_token_auth_rules::ID, None); + let mut context = program_test.start_with_context().await; + + // creates the auth rule set + + let payer = context.payer.dirty_clone(); + let (rule_set, auth_data) = create_default_metaplex_rule_set(&mut context, payer).await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create_and_mint( + &mut context, + TokenStandard::ProgrammableNonFungible, + Some(rule_set), + Some(auth_data), + 1, + ) + .await + .unwrap(); + + assert!(asset.token.is_some()); + + // asserts + + let (pda_key, _) = find_token_record_account(&asset.mint.pubkey(), &asset.token.unwrap()); + let pda = get_account(&mut context, &pda_key).await; + let token_record: TokenRecord = try_from_slice_unchecked(&pda.data).unwrap(); + + assert_eq!(token_record.rule_set_revision, None); + + // delegates the asset for transfer + + let user = Keypair::new(); + let user_pubkey = user.pubkey(); + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + asset + .delegate( + &mut context, + payer, + user_pubkey, + DelegateArgs::SaleV1 { + amount: 1, + authorization_data: None, + }, + ) + .await + .unwrap(); + + // asserts + + let (pda_key, _) = find_token_record_account(&asset.mint.pubkey(), &asset.token.unwrap()); + + let pda = get_account(&mut context, &pda_key).await; + let token_record: TokenRecord = try_from_slice_unchecked(&pda.data).unwrap(); + + assert_eq!(token_record.key, Key::TokenRecord); + assert_eq!(token_record.delegate, Some(user_pubkey)); + assert_eq!(token_record.delegate_role, Some(TokenDelegateRole::Sale)); + assert_eq!(token_record.rule_set_revision, Some(0)); + + if let Some(token) = asset.token { + let account = get_account(&mut context, &token).await; + let token_account = Account::unpack(&account.data).unwrap(); + + assert!(token_account.is_frozen()); + assert_eq!(token_account.delegate, COption::Some(user_pubkey)); + assert_eq!(token_account.delegated_amount, 1); + } else { + panic!("Missing token account"); + } + } +} diff --git a/token-metadata/program/tests/escrow.rs b/token-metadata/program/tests/escrow.rs index 8cca6cf4d3..b9a3c356aa 100644 --- a/token-metadata/program/tests/escrow.rs +++ b/token-metadata/program/tests/escrow.rs @@ -106,6 +106,7 @@ mod escrow { &context.payer.pubkey(), &escrow_address.0, &attribute_test_metadata.mint.pubkey(), + &spl_token::ID, ); let ix1 = spl_token::instruction::transfer( &spl_token::id(), @@ -299,6 +300,7 @@ mod escrow { &context.payer.pubkey(), &escrow_address.0, &attribute_test_metadata.mint.pubkey(), + &spl_token::ID, ); let ix1 = spl_token::instruction::transfer( &spl_token::id(), diff --git a/token-metadata/program/tests/lock.rs b/token-metadata/program/tests/lock.rs new file mode 100644 index 0000000000..19d3dfd565 --- /dev/null +++ b/token-metadata/program/tests/lock.rs @@ -0,0 +1,298 @@ +#![cfg(feature = "test-bpf")] +pub mod utils; + +use num_traits::FromPrimitive; +use solana_program_test::*; +use utils::*; + +mod lock { + + use mpl_token_metadata::{ + error::MetadataError, + instruction::DelegateArgs, + pda::find_token_record_account, + state::{TokenRecord, TokenStandard, TokenState}, + }; + use solana_program::{borsh::try_from_slice_unchecked, program_pack::Pack}; + use solana_sdk::{ + instruction::InstructionError, + signature::{Keypair, Signer}, + transaction::TransactionError, + }; + use spl_token::state::Account; + + use super::*; + + #[tokio::test] + async fn fail_owner_lock_programmable_nonfungible() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create_and_mint( + &mut context, + TokenStandard::ProgrammableNonFungible, + None, + None, + 1, + ) + .await + .unwrap(); + + // asserts + + let (pda_key, _) = find_token_record_account(&asset.mint.pubkey(), &asset.token.unwrap()); + + let pda = get_account(&mut context, &pda_key).await; + let token_record: TokenRecord = try_from_slice_unchecked(&pda.data).unwrap(); + + assert_eq!(token_record.state, TokenState::Unlocked); + + // locks + + let approver = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + let error = asset + .lock(&mut context, approver, Some(pda_key), payer) + .await + .unwrap_err(); + + // asserts + + assert_custom_error!(error, MetadataError::InvalidAuthorityType); + } + + #[tokio::test] + async fn fail_owner_lock_nonfungible() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create_and_mint(&mut context, TokenStandard::NonFungible, None, None, 1) + .await + .unwrap(); + + // asserts + + let token_account = get_account(&mut context, &asset.token.unwrap()).await; + let token = Account::unpack(&token_account.data).unwrap(); + // should not be frozen + assert!(!token.is_frozen()); + + // lock the token + + let approver = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + let error = asset + .lock(&mut context, approver, None, payer) + .await + .unwrap_err(); + + assert_custom_error!(error, MetadataError::InvalidAuthorityType); + } + + #[tokio::test] + async fn delegate_lock_programmable_nonfungible() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create_and_mint( + &mut context, + TokenStandard::ProgrammableNonFungible, + None, + None, + 1, + ) + .await + .unwrap(); + + // asserts + + let (pda_key, _) = find_token_record_account(&asset.mint.pubkey(), &asset.token.unwrap()); + + let pda = get_account(&mut context, &pda_key).await; + let token_record: TokenRecord = try_from_slice_unchecked(&pda.data).unwrap(); + + assert_eq!(token_record.state, TokenState::Unlocked); + + // set a utility delegate + + let delegate = Keypair::new(); + let delegate_pubkey = delegate.pubkey(); + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + asset + .delegate( + &mut context, + payer, + delegate_pubkey, + DelegateArgs::UtilityV1 { + amount: 1, + authorization_data: None, + }, + ) + .await + .unwrap(); + + // locks + + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + asset + .lock(&mut context, delegate, Some(pda_key), payer) + .await + .unwrap(); + + // asserts + + let token_account = get_account(&mut context, &asset.token.unwrap()).await; + let token = Account::unpack(&token_account.data).unwrap(); + // should not be frozen + assert!(token.is_frozen()); + } + + #[tokio::test] + async fn delegate_lock_nonfungible() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create_and_mint(&mut context, TokenStandard::NonFungible, None, None, 1) + .await + .unwrap(); + + // asserts + + let token_account = get_account(&mut context, &asset.token.unwrap()).await; + let token = Account::unpack(&token_account.data).unwrap(); + // should not be frozen + assert!(!token.is_frozen()); + + // set a utility delegate + + let delegate = Keypair::new(); + let delegate_pubkey = delegate.pubkey(); + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + asset + .delegate( + &mut context, + payer, + delegate_pubkey, + DelegateArgs::StandardV1 { amount: 1 }, + ) + .await + .unwrap(); + + // lock the token + + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + asset + .lock(&mut context, delegate, None, payer) + .await + .unwrap(); + + // asserts + + let token_account = get_account(&mut context, &asset.token.unwrap()).await; + let token = Account::unpack(&token_account.data).unwrap(); + // should not be frozen + assert!(token.is_frozen()); + } + + #[tokio::test] + async fn locked_programmable_nonfungible_delegate_fails() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create_and_mint( + &mut context, + TokenStandard::ProgrammableNonFungible, + None, + None, + 1, + ) + .await + .unwrap(); + + // asserts + + let (pda_key, _) = find_token_record_account(&asset.mint.pubkey(), &asset.token.unwrap()); + + let pda = get_account(&mut context, &pda_key).await; + let token_record: TokenRecord = try_from_slice_unchecked(&pda.data).unwrap(); + + assert_eq!(token_record.state, TokenState::Unlocked); + + // set a utility delegate + + let delegate = Keypair::new(); + let delegate_pubkey = delegate.pubkey(); + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + asset + .delegate( + &mut context, + payer, + delegate_pubkey, + DelegateArgs::UtilityV1 { + amount: 1, + authorization_data: None, + }, + ) + .await + .unwrap(); + + // locks + + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + asset + .lock(&mut context, delegate, Some(pda_key), payer) + .await + .unwrap(); + + // asserts + + let pda = get_account(&mut context, &pda_key).await; + let token_record: TokenRecord = try_from_slice_unchecked(&pda.data).unwrap(); + + assert_eq!(token_record.state, TokenState::Locked); + + // delegates the asset for transfer (this should fail since the token is locked) + + let another_delegate = Keypair::new(); + let delegate_pubkey = another_delegate.pubkey(); + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + let error = asset + .delegate( + &mut context, + payer, + delegate_pubkey, + DelegateArgs::TransferV1 { + amount: 1, + authorization_data: None, + }, + ) + .await + .unwrap_err(); + + assert_custom_error!(error, MetadataError::LockedToken); + } +} diff --git a/token-metadata/program/tests/migrate.rs b/token-metadata/program/tests/migrate.rs new file mode 100644 index 0000000000..09da14ba78 --- /dev/null +++ b/token-metadata/program/tests/migrate.rs @@ -0,0 +1,151 @@ +// #![cfg(feature = "test-bpf")] +// pub mod utils; +// use utils::*; + +// use num_traits::FromPrimitive; +// use solana_program_test::*; +// use solana_sdk::{instruction::InstructionError, transaction::TransactionError}; + +// mod migrate { +// use super::*; +// use mpl_token_metadata::{ +// error::MetadataError, +// instruction::MigrateArgs, +// state::{MigrationType, ProgrammableConfig, TokenStandard}, +// }; +// use solana_program::pubkey::Pubkey; +// use solana_sdk::{signature::Keypair, signer::Signer}; + +// #[tokio::test] +// async fn success_migrate() { +// let mut program_test = ProgramTest::new("mpl_token_metadata", mpl_token_metadata::ID, None); +// program_test.add_program("mpl_token_auth_rules", mpl_token_auth_rules::ID, None); +// let mut context = program_test.start_with_context().await; + +// let authority = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + +// // Create a rule set for the pNFTs. +// let (rule_set, _auth_data) = +// create_default_metaplex_rule_set(&mut context, authority).await; + +// // Create an unsized collection for the pNFT to belong to, since +// // migration requires the item being a verified member of a collection. +// let (collection_nft, collection_me) = +// Metadata::create_default_nft(&mut context).await.unwrap(); + +// // Create the NFT item to migrate. +// let (nft, me) = Metadata::create_default_nft(&mut context).await.unwrap(); + +// let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + +// nft.set_and_verify_collection( +// &mut context, +// collection_nft.pubkey, +// &payer, +// payer.pubkey(), +// collection_nft.mint.pubkey(), +// collection_me.pubkey, +// None, +// ) +// .await +// .unwrap(); + +// let md = nft.get_data(&mut context).await; + +// // set up our digital asset struct +// let mut asset = nft.into_digital_asset(&mut context).await; +// asset.master_edition = Some(me.pubkey); + +// let args = MigrateArgs::V1 { +// migration_type: MigrationType::ProgrammableV1, +// rule_set: Some(rule_set), +// }; + +// assert_eq!(md.token_standard, Some(TokenStandard::NonFungible)); +// assert_eq!(md.programmable_config, None); + +// let authority = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + +// asset +// .migrate(&mut context, authority, collection_nft.pubkey, args) +// .await +// .unwrap(); + +// let new_md = asset.get_metadata(&mut context).await; + +// assert_eq!( +// new_md.token_standard, +// Some(TokenStandard::ProgrammableNonFungible) +// ); + +// if let Some(ProgrammableConfig::V1 { +// rule_set: Some(rule_set), +// }) = new_md.programmable_config +// { +// assert_eq!(rule_set, rule_set); +// } else { +// panic!("Missing programmable config"); +// } +// } + +// #[tokio::test] +// async fn migrate_invalid_collection_metadata() { +// let context = &mut program_test().start_with_context().await; + +// let authority = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); +// let amount = 1; + +// let mut asset = DigitalAsset::new(); +// asset +// .create_and_mint(context, TokenStandard::NonFungible, None, None, amount) +// .await +// .unwrap(); + +// let args = MigrateArgs::V1 { +// migration_type: MigrationType::ProgrammableV1, +// rule_set: None, +// }; + +// let md = asset.get_metadata(context).await; +// assert_eq!(md.token_standard, Some(TokenStandard::NonFungible)); + +// let err = asset +// .migrate(context, authority, Pubkey::new_unique(), args) +// .await +// .unwrap_err(); + +// assert_custom_error!(err, MetadataError::DataTypeMismatch); +// } + +// #[tokio::test] +// async fn migrate_no_collection() { +// let context = &mut program_test().start_with_context().await; + +// let authority = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); +// let amount = 1; + +// // Unsized collection +// let (collection_nft, _collection_me) = Metadata::create_default_nft(context).await.unwrap(); + +// let mut asset = DigitalAsset::new(); +// asset +// .create_and_mint(context, TokenStandard::NonFungible, None, None, amount) +// .await +// .unwrap(); + +// let args = MigrateArgs::V1 { +// migration_type: MigrationType::ProgrammableV1, +// rule_set: None, +// }; + +// let md = asset.get_metadata(context).await; +// assert_eq!(md.token_standard, Some(TokenStandard::NonFungible)); + +// let err = asset +// .migrate(context, authority, collection_nft.pubkey, args) +// .await +// .unwrap_err(); + +// assert_custom_error!(err, MetadataError::NotAMemberOfCollection); +// } +// } diff --git a/token-metadata/program/tests/mint.rs b/token-metadata/program/tests/mint.rs new file mode 100644 index 0000000000..315fa3278e --- /dev/null +++ b/token-metadata/program/tests/mint.rs @@ -0,0 +1,164 @@ +#![cfg(feature = "test-bpf")] +pub mod utils; + +use solana_program_test::*; +use solana_sdk::{instruction::InstructionError, signature::Signer, transaction::TransactionError}; +use utils::*; + +mod mint { + + use mpl_token_metadata::{error::MetadataError, state::TokenStandard}; + use num_traits::FromPrimitive; + use solana_program::program_pack::Pack; + use spl_token::state::Account; + + use super::*; + + #[tokio::test] + async fn mint_programmable_nonfungible() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create(&mut context, TokenStandard::ProgrammableNonFungible, None) + .await + .unwrap(); + + // mints one token + + let payer_pubkey = context.payer.pubkey(); + + asset.mint(&mut context, None, None, 1).await.unwrap(); + + // asserts + + let account = get_account(&mut context, &asset.token.unwrap()).await; + let token_account = Account::unpack(&account.data).unwrap(); + + assert!(token_account.is_frozen()); + assert_eq!(token_account.amount, 1); + assert_eq!(token_account.mint, asset.mint.pubkey()); + assert_eq!(token_account.owner, payer_pubkey); + } + + #[tokio::test] + async fn mint_nonfungible() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create(&mut context, TokenStandard::NonFungible, None) + .await + .unwrap(); + + // mints one token + + asset.mint(&mut context, None, None, 1).await.unwrap(); + + assert!(asset.token.is_some()); + + if let Some(token) = asset.token { + let account = get_account(&mut context, &token).await; + let token_account = Account::unpack(&account.data).unwrap(); + + assert!(!token_account.is_frozen()); + assert_eq!(token_account.amount, 1); + assert_eq!(token_account.mint, asset.mint.pubkey()); + assert_eq!(token_account.owner, context.payer.pubkey()); + } + } + + #[tokio::test] + async fn mint_fungible() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create(&mut context, TokenStandard::Fungible, None) + .await + .unwrap(); + + // mints one token + + asset.mint(&mut context, None, None, 100).await.unwrap(); + + assert!(asset.token.is_some()); + + if let Some(token) = asset.token { + let account = get_account(&mut context, &token).await; + let token_account = Account::unpack(&account.data).unwrap(); + + assert!(!token_account.is_frozen()); + assert_eq!(token_account.amount, 100); + assert_eq!(token_account.mint, asset.mint.pubkey()); + assert_eq!(token_account.owner, context.payer.pubkey()); + } + } + + #[tokio::test] + async fn mint_fungible_asset() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create(&mut context, TokenStandard::FungibleAsset, None) + .await + .unwrap(); + + // mints one token + + asset.mint(&mut context, None, None, 50).await.unwrap(); + + assert!(asset.token.is_some()); + + if let Some(token) = asset.token { + let account = get_account(&mut context, &token).await; + let token_account = Account::unpack(&account.data).unwrap(); + + assert!(!token_account.is_frozen()); + assert_eq!(token_account.amount, 50); + assert_eq!(token_account.mint, asset.mint.pubkey()); + assert_eq!(token_account.owner, context.payer.pubkey()); + } + } + + #[tokio::test] + async fn try_mint_multiple_programmable_nonfungible() { + let mut context = program_test().start_with_context().await; + + let mut asset = DigitalAsset::default(); + let error = asset + .create_and_mint( + &mut context, + TokenStandard::ProgrammableNonFungible, + None, + None, + 2, + ) + .await + .unwrap_err(); + + assert_custom_error_ix!(1, error, MetadataError::EditionsMustHaveExactlyOneToken); + } + + #[tokio::test] + async fn try_mint_multiple_nonfungible() { + let mut context = program_test().start_with_context().await; + + let mut asset = DigitalAsset::default(); + let error = asset + .create_and_mint(&mut context, TokenStandard::NonFungible, None, None, 2) + .await + .unwrap_err(); + + assert_custom_error_ix!(1, error, MetadataError::EditionsMustHaveExactlyOneToken); + } +} diff --git a/token-metadata/program/tests/mint_new_edition_from_master_edition_via_token.rs b/token-metadata/program/tests/mint_new_edition_from_master_edition_via_token.rs index a9bbffcaca..ed89a53d54 100644 --- a/token-metadata/program/tests/mint_new_edition_from_master_edition_via_token.rs +++ b/token-metadata/program/tests/mint_new_edition_from_master_edition_via_token.rs @@ -5,9 +5,8 @@ use borsh::BorshSerialize; use mpl_token_metadata::{ error::MetadataError, id, instruction, - state::{Creator, Key, MAX_MASTER_EDITION_LEN}, + state::{Collection, Creator, Key, MAX_MASTER_EDITION_LEN}, }; -use mpl_token_metadata::{instruction::sign_metadata, state::Collection}; use num_traits::FromPrimitive; use solana_program_test::*; use solana_sdk::{ @@ -28,7 +27,7 @@ mod mint_new_edition_from_master_edition_via_token { #[tokio::test] async fn success() { let mut context = program_test().start_with_context().await; - let payer_key = context.payer.pubkey().clone(); + let payer_key = context.payer.pubkey(); let test_metadata = Metadata::new(); let test_master_edition = MasterEditionV2::new(&test_metadata); let test_edition_marker = EditionMarker::new(&test_metadata, &test_master_edition, 1); @@ -68,10 +67,9 @@ mod mint_new_edition_from_master_edition_via_token { async fn success_v2() { let mut context = program_test().start_with_context().await; let test_metadata = Metadata::new(); - let payer_key = context.payer.pubkey().clone(); let creator = Keypair::new(); - let creator_pub = creator.pubkey().clone(); + let creator_pub = creator.pubkey(); airdrop(&mut context, &creator_pub.clone(), 3 * LAMPORTS_PER_SOL) .await .unwrap(); @@ -93,7 +91,7 @@ mod mint_new_edition_from_master_edition_via_token { "TST".to_string(), "uri".to_string(), Some(vec![Creator { - address: creator_pub.clone(), + address: creator_pub, verified: false, share: 100, }]), @@ -114,7 +112,7 @@ mod mint_new_edition_from_master_edition_via_token { .unwrap(); let tx = Transaction::new_signed_with_payer( - &[instruction::sign_metadata( + [instruction::sign_metadata( mpl_token_metadata::id(), test_metadata.pubkey, creator_pub, @@ -124,7 +122,7 @@ mod mint_new_edition_from_master_edition_via_token { &[&creator], context.last_blockhash, ); - let result = context.banks_client.process_transaction(tx).await; + let _result = context.banks_client.process_transaction(tx).await; let kpbytes = &context.payer; let kp = Keypair::from_bytes(&kpbytes.to_bytes()).unwrap(); diff --git a/token-metadata/program/tests/process_legacy_instruction.rs b/token-metadata/program/tests/process_legacy_instruction.rs new file mode 100644 index 0000000000..4199062311 --- /dev/null +++ b/token-metadata/program/tests/process_legacy_instruction.rs @@ -0,0 +1,195 @@ +#![cfg(feature = "test-bpf")] +pub mod utils; + +use num_traits::FromPrimitive; +use solana_program::pubkey::Pubkey; +use solana_program_test::*; +use solana_sdk::{ + instruction::InstructionError, + signature::{Keypair, Signer}, + transaction::{Transaction, TransactionError}, +}; +use utils::*; + +mod process_legacy_instruction { + + use mpl_token_metadata::{ + error::MetadataError, + instruction::{sign_metadata, DelegateArgs}, + state::{Metadata, TokenStandard}, + }; + use solana_program::{borsh::try_from_slice_unchecked, program_pack::Pack}; + use spl_token::state::Account; + + use super::*; + + #[tokio::test] + async fn programmable_nft_in_legacy_processor() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create(&mut context, TokenStandard::ProgrammableNonFungible, None) + .await + .unwrap(); + + // mints one token + + let payer_pubkey = context.payer.pubkey(); + let (token, _) = Pubkey::find_program_address( + &[ + &payer_pubkey.to_bytes(), + &spl_token::id().to_bytes(), + &asset.mint.pubkey().to_bytes(), + ], + &spl_associated_token_account::id(), + ); + asset.token = Some(token); + + asset.mint(&mut context, None, None, 1).await.unwrap(); + + let metadata_account = get_account(&mut context, &asset.metadata).await; + let metadata: Metadata = try_from_slice_unchecked(&metadata_account.data).unwrap(); + + assert_eq!( + metadata.token_standard, + Some(TokenStandard::ProgrammableNonFungible) + ); + + // tries to use a "legacy" instruction with a pNFT + + // we won't need to use this keypair + let creator = Keypair::new(); + + let sign_ix = sign_metadata(mpl_token_metadata::id(), asset.metadata, creator.pubkey()); + let sign_tx = Transaction::new_signed_with_payer( + &[sign_ix], + Some(&context.payer.pubkey()), + &[&creator, &context.payer], + context.last_blockhash, + ); + + let error = context + .banks_client + .process_transaction(sign_tx) + .await + .unwrap_err(); + + assert_custom_error!(error, MetadataError::InstructionNotSupported); + } + + #[tokio::test] + async fn thaw_programmable_nft() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create_and_mint( + &mut context, + TokenStandard::ProgrammableNonFungible, + None, + None, + 1, + ) + .await + .unwrap(); + + let metadata_account = get_account(&mut context, &asset.metadata).await; + let metadata: Metadata = try_from_slice_unchecked(&metadata_account.data).unwrap(); + + assert_eq!( + metadata.token_standard, + Some(TokenStandard::ProgrammableNonFungible) + ); + + let account = get_account(&mut context, &asset.token.unwrap()).await; + let token_account = Account::unpack(&account.data).unwrap(); + + assert!(token_account.is_frozen()); + assert_eq!(token_account.amount, 1); + assert_eq!(token_account.mint, asset.mint.pubkey()); + assert_eq!(token_account.owner, context.payer.pubkey()); + + // creates a transfer delegate so we have a SPL Token delegate in place + + let delegate = Keypair::new(); + let delegate_pubkey = delegate.pubkey(); + + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + asset + .delegate( + &mut context, + payer, + delegate_pubkey, + DelegateArgs::TransferV1 { + amount: 1, + authorization_data: None, + }, + ) + .await + .unwrap(); + + // tries to use a "legacy" thaw instruction with a pNFT + + let thaw_ix = mpl_token_metadata::instruction::thaw_delegated_account( + mpl_token_metadata::id(), + delegate_pubkey, + asset.token.unwrap(), + asset.master_edition.unwrap(), + asset.mint.pubkey(), + ); + let thaw_tx = Transaction::new_signed_with_payer( + &[thaw_ix], + Some(&context.payer.pubkey()), + &[&context.payer, &delegate], + context.last_blockhash, + ); + + // fails because it is a pNFT master edition + let error = context + .banks_client + .process_transaction(thaw_tx) + .await + .unwrap_err(); + + assert_custom_error!(error, MetadataError::InvalidTokenStandard); + + // makes sure the token still frozen + + let account = get_account(&mut context, &asset.token.unwrap()).await; + let token_account = Account::unpack(&account.data).unwrap(); + assert!(token_account.is_frozen()); + + // tries to freeze (this would normally fail at the SPL Token level, but we + // should get our custom error first) + + let freeze_ix = mpl_token_metadata::instruction::freeze_delegated_account( + mpl_token_metadata::id(), + delegate_pubkey, + asset.token.unwrap(), + asset.master_edition.unwrap(), + asset.mint.pubkey(), + ); + + let freeze_tx = Transaction::new_signed_with_payer( + &[freeze_ix], + Some(&context.payer.pubkey()), + &[&context.payer, &delegate], + context.last_blockhash, + ); + + // fails because it is a pNFT master edition + let error = context + .banks_client + .process_transaction(freeze_tx) + .await + .unwrap_err(); + + assert_custom_error!(error, MetadataError::InvalidTokenStandard); + } +} diff --git a/token-metadata/program/tests/revoke.rs b/token-metadata/program/tests/revoke.rs new file mode 100644 index 0000000000..34044f38d4 --- /dev/null +++ b/token-metadata/program/tests/revoke.rs @@ -0,0 +1,426 @@ +#![cfg(feature = "test-bpf")] +pub mod utils; + +use solana_program_test::*; +use solana_sdk::{ + instruction::InstructionError, + signature::{Keypair, Signer}, + transaction::TransactionError, +}; +use utils::*; + +mod revoke { + + use mpl_token_metadata::{ + error::MetadataError, + instruction::{DelegateArgs, MetadataDelegateRole, RevokeArgs}, + pda::{find_metadata_delegate_record_account, find_token_record_account}, + state::{ + Key, Metadata, MetadataDelegateRecord, TokenDelegateRole, TokenRecord, TokenStandard, + }, + }; + use num_traits::FromPrimitive; + use solana_program::{ + borsh::try_from_slice_unchecked, program_option::COption, program_pack::Pack, + }; + use spl_token::state::Account; + + use super::*; + + #[tokio::test] + async fn revoke_transfer_delegate_programmable_nonfungible() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create_and_mint( + &mut context, + TokenStandard::ProgrammableNonFungible, + None, + None, + 1, + ) + .await + .unwrap(); + + assert!(asset.token.is_some()); + + // delegates the asset for sale + + let user = Keypair::new(); + let user_pubkey = user.pubkey(); + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + asset + .delegate( + &mut context, + payer, + user_pubkey, + DelegateArgs::TransferV1 { + amount: 1, + authorization_data: None, + }, + ) + .await + .unwrap(); + + let (pda_key, _) = find_token_record_account(&asset.mint.pubkey(), &asset.token.unwrap()); + + let pda = get_account(&mut context, &pda_key).await; + let token_record: TokenRecord = try_from_slice_unchecked(&pda.data).unwrap(); + + assert_eq!(token_record.delegate, Some(user_pubkey)); + assert_eq!( + token_record.delegate_role, + Some(TokenDelegateRole::Transfer) + ); + + // revokes the delegate + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + let approver = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + asset + .revoke( + &mut context, + payer, + approver, + user_pubkey, + RevokeArgs::TransferV1, + ) + .await + .unwrap(); + + let pda = get_account(&mut context, &pda_key).await; + let token_record: TokenRecord = try_from_slice_unchecked(&pda.data).unwrap(); + + assert_eq!(token_record.delegate, None); + + if let Some(token) = asset.token { + let account = get_account(&mut context, &token).await; + let token_account = Account::unpack(&account.data).unwrap(); + + assert!(token_account.is_frozen()); + assert_eq!(token_account.delegate, COption::None); + } else { + panic!("Missing token account"); + } + } + + #[tokio::test] + async fn revoke_collection_delegate_programmable_nonfungible() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create_and_mint( + &mut context, + TokenStandard::ProgrammableNonFungible, + None, + None, + 1, + ) + .await + .unwrap(); + + assert!(asset.token.is_some()); + + let metadata_account = get_account(&mut context, &asset.metadata).await; + let metadata: Metadata = try_from_slice_unchecked(&metadata_account.data).unwrap(); + assert_eq!(metadata.update_authority, context.payer.pubkey()); + + // creates a collection delegate + + let user = Keypair::new(); + let user_pubkey = user.pubkey(); + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + let payer_pubkey = payer.pubkey(); + + asset + .delegate( + &mut context, + payer, + user_pubkey, + DelegateArgs::CollectionV1 { + authorization_data: None, + }, + ) + .await + .unwrap(); + + // checks that the delegate exists + let (pda_key, _) = find_metadata_delegate_record_account( + &asset.mint.pubkey(), + MetadataDelegateRole::Collection, + &payer_pubkey, + &user_pubkey, + ); + + let pda = get_account(&mut context, &pda_key).await; + let delegate_record = MetadataDelegateRecord::from_bytes(&pda.data).unwrap(); + assert_eq!(delegate_record.key, Key::MetadataDelegate); + + // revokes the delegate + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + let approver = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + asset + .revoke( + &mut context, + payer, + approver, + user_pubkey, + RevokeArgs::CollectionV1, + ) + .await + .unwrap(); + + // checks that the delagate exists (it should not exist) + + assert!(context + .banks_client + .get_account(pda_key) + .await + .unwrap() + .is_none()); + } + + #[tokio::test] + async fn revoke_sale_delegate_programmable_nonfungible() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create_and_mint( + &mut context, + TokenStandard::ProgrammableNonFungible, + None, + None, + 1, + ) + .await + .unwrap(); + + assert!(asset.token.is_some()); + + // delegates the asset for sale + let user = Keypair::new(); + let user_pubkey = user.pubkey(); + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + asset + .delegate( + &mut context, + payer, + user_pubkey, + DelegateArgs::SaleV1 { + amount: 1, + authorization_data: None, + }, + ) + .await + .unwrap(); + + // checks that the delagate exists + + let (pda_key, _) = find_token_record_account(&asset.mint.pubkey(), &asset.token.unwrap()); + + let pda = get_account(&mut context, &pda_key).await; + let token_record: TokenRecord = try_from_slice_unchecked(&pda.data).unwrap(); + + assert_eq!(token_record.delegate, Some(user_pubkey)); + + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + let approver = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + // revokes the delegate + asset + .revoke( + &mut context, + payer, + approver, + user_pubkey, + RevokeArgs::SaleV1, + ) + .await + .unwrap(); + + // asserts + + let pda = get_account(&mut context, &pda_key).await; + let token_record: TokenRecord = try_from_slice_unchecked(&pda.data).unwrap(); + + assert_eq!(token_record.delegate, None); + + if let Some(token) = asset.token { + let account = get_account(&mut context, &token).await; + let token_account = Account::unpack(&account.data).unwrap(); + + assert!(token_account.is_frozen()); + assert_eq!(token_account.delegate, COption::None); + } else { + panic!("Missing token account"); + } + } + + #[tokio::test] + async fn revoke_sale_delegate_as_transfer_delegate() { + let mut context = program_test().start_with_context().await; + + // asset + let mut asset = DigitalAsset::default(); + asset + .create_and_mint( + &mut context, + TokenStandard::ProgrammableNonFungible, + None, + None, + 1, + ) + .await + .unwrap(); + + assert!(asset.token.is_some()); + + // delegates the asset for sale + let user = Keypair::new(); + let user_pubkey = user.pubkey(); + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + asset + .delegate( + &mut context, + payer, + user_pubkey, + DelegateArgs::SaleV1 { + amount: 1, + authorization_data: None, + }, + ) + .await + .unwrap(); + + // checks that the delagate exists + + let (pda_key, _) = find_token_record_account(&asset.mint.pubkey(), &asset.token.unwrap()); + + let pda = get_account(&mut context, &pda_key).await; + let token_record: TokenRecord = try_from_slice_unchecked(&pda.data).unwrap(); + + assert_eq!(token_record.delegate, Some(user_pubkey)); + + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + let approver = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + // revokes the delegate + let error = asset + .revoke( + &mut context, + payer, + approver, + user_pubkey, + RevokeArgs::TransferV1, + ) + .await + .unwrap_err(); + + // assert + + assert_custom_error!(error, MetadataError::InvalidDelegate); + } + + #[tokio::test] + async fn clear_rule_set_revision_on_delegate() { + let mut program_test = ProgramTest::new("mpl_token_metadata", mpl_token_metadata::ID, None); + program_test.add_program("mpl_token_auth_rules", mpl_token_auth_rules::ID, None); + let mut context = program_test.start_with_context().await; + + // creates the auth rule set + + let payer = context.payer.dirty_clone(); + let (rule_set, auth_data) = create_default_metaplex_rule_set(&mut context, payer).await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create_and_mint( + &mut context, + TokenStandard::ProgrammableNonFungible, + Some(rule_set), + Some(auth_data), + 1, + ) + .await + .unwrap(); + + assert!(asset.token.is_some()); + + // asserts + + let (pda_key, _) = find_token_record_account(&asset.mint.pubkey(), &asset.token.unwrap()); + let pda = get_account(&mut context, &pda_key).await; + let token_record: TokenRecord = try_from_slice_unchecked(&pda.data).unwrap(); + + assert_eq!(token_record.rule_set_revision, None); + + // delegates the asset for transfer + + let user = Keypair::new(); + let user_pubkey = user.pubkey(); + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + asset + .delegate( + &mut context, + payer, + user_pubkey, + DelegateArgs::SaleV1 { + amount: 1, + authorization_data: None, + }, + ) + .await + .unwrap(); + + // asserts + + let (pda_key, _) = find_token_record_account(&asset.mint.pubkey(), &asset.token.unwrap()); + + let pda = get_account(&mut context, &pda_key).await; + let token_record: TokenRecord = try_from_slice_unchecked(&pda.data).unwrap(); + + assert_eq!(token_record.key, Key::TokenRecord); + assert_eq!(token_record.delegate, Some(user_pubkey)); + assert_eq!(token_record.delegate_role, Some(TokenDelegateRole::Sale)); + assert_eq!(token_record.rule_set_revision, Some(0)); + + // revokes the delegate + + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + let authority = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + asset + .revoke( + &mut context, + payer, + authority, + user_pubkey, + RevokeArgs::SaleV1, + ) + .await + .unwrap(); + + // asserts + + let pda = get_account(&mut context, &pda_key).await; + let token_record: TokenRecord = try_from_slice_unchecked(&pda.data).unwrap(); + // the revision must have been cleared + assert_eq!(token_record.rule_set_revision, None); + } +} diff --git a/token-metadata/program/tests/transfer.rs b/token-metadata/program/tests/transfer.rs new file mode 100644 index 0000000000..3ec681099f --- /dev/null +++ b/token-metadata/program/tests/transfer.rs @@ -0,0 +1,983 @@ +#![cfg(feature = "test-bpf")] + +pub mod utils; + +use mpl_token_auth_rules::payload::{PayloadType, SeedsVec}; +use mpl_token_metadata::{ + instruction::TransferArgs, + state::{PayloadKey, TokenStandard}, +}; +use num_traits::FromPrimitive; +use rooster::instruction::DelegateArgs as RoosterDelegateArgs; +use solana_program::{native_token::LAMPORTS_PER_SOL, program_pack::Pack, pubkey::Pubkey}; +use solana_program_test::*; +use solana_sdk::{ + instruction::InstructionError, + signature::{Keypair, Signer}, + transaction::TransactionError, +}; +use spl_associated_token_account::get_associated_token_address; +use utils::*; + +mod standard_transfer { + + use mpl_token_metadata::{ + error::MetadataError, + instruction::{DelegateArgs, TransferArgs}, + state::TokenStandard, + }; + use solana_program::{ + native_token::LAMPORTS_PER_SOL, program_option::COption, program_pack::Pack, pubkey::Pubkey, + }; + use spl_associated_token_account::get_associated_token_address; + + use super::*; + + #[tokio::test] + async fn transfer_nonfungible() { + let mut context = program_test().start_with_context().await; + + let mut da = DigitalAsset::new(); + da.create_and_mint(&mut context, TokenStandard::NonFungible, None, None, 1) + .await + .unwrap(); + + let destination_owner = Keypair::new().pubkey(); + let destination_token = get_associated_token_address(&destination_owner, &da.mint.pubkey()); + airdrop(&mut context, &destination_owner, LAMPORTS_PER_SOL) + .await + .unwrap(); + + let authority = &Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + let args = TransferArgs::V1 { + authorization_data: None, + amount: 1, + }; + + let params = TransferFromParams { + context: &mut context, + authority, + source_owner: &authority.pubkey(), + destination_owner, + destination_token: None, + authorization_rules: None, + payer: authority, + args, + }; + + da.transfer_from(params).await.unwrap(); + + let token_account = spl_token::state::Account::unpack( + &context + .banks_client + .get_account(destination_token) + .await + .unwrap() + .unwrap() + .data, + ) + .unwrap(); + + assert_eq!(token_account.amount, 1); + } + + #[tokio::test] + async fn transfer_fungible() { + let mut context = program_test().start_with_context().await; + + let mint_amount = 10; + let amount = 5; + + let mut da = DigitalAsset::new(); + da.create_and_mint( + &mut context, + TokenStandard::Fungible, + None, + None, + mint_amount, + ) + .await + .unwrap(); + + let destination_owner = Keypair::new().pubkey(); + let destination_token = get_associated_token_address(&destination_owner, &da.mint.pubkey()); + airdrop(&mut context, &destination_owner, LAMPORTS_PER_SOL) + .await + .unwrap(); + + let authority = &Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + let args = TransferArgs::V1 { + authorization_data: None, + amount, + }; + + let params = TransferFromParams { + context: &mut context, + authority, + source_owner: &authority.pubkey(), + destination_owner, + destination_token: None, + authorization_rules: None, + payer: authority, + args, + }; + + da.transfer_from(params).await.unwrap(); + + let token_account = spl_token::state::Account::unpack( + &context + .banks_client + .get_account(destination_token) + .await + .unwrap() + .unwrap() + .data, + ) + .unwrap(); + + assert_eq!(token_account.amount, amount); + } + + #[tokio::test] + async fn transfer_fungible_asset() { + let mut context = program_test().start_with_context().await; + + let mint_amount = 100; + let transfer_amount = 99; + + let mut da = DigitalAsset::new(); + da.create_and_mint( + &mut context, + TokenStandard::FungibleAsset, + None, + None, + mint_amount, + ) + .await + .unwrap(); + + let destination_owner = Pubkey::new_unique(); + let destination_token = get_associated_token_address(&destination_owner, &da.mint.pubkey()); + airdrop(&mut context, &destination_owner, LAMPORTS_PER_SOL) + .await + .unwrap(); + + let authority = &Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + let args = TransferArgs::V1 { + authorization_data: None, + amount: transfer_amount, + }; + + let params = TransferFromParams { + context: &mut context, + authority, + source_owner: &authority.pubkey(), + destination_owner, + destination_token: None, + authorization_rules: None, + payer: authority, + args, + }; + + da.transfer_from(params).await.unwrap(); + + let token_account = spl_token::state::Account::unpack( + &context + .banks_client + .get_account(destination_token) + .await + .unwrap() + .unwrap() + .data, + ) + .unwrap(); + + assert_eq!(token_account.amount, transfer_amount); + } + + #[tokio::test] + async fn transfer_with_delegate() { + let mut context = program_test().start_with_context().await; + + let transfer_amount = 1; + + let mut da = DigitalAsset::new(); + da.create_and_mint(&mut context, TokenStandard::NonFungible, None, None, 1) + .await + .unwrap(); + + let delegate = Keypair::new(); + airdrop(&mut context, &delegate.pubkey(), LAMPORTS_PER_SOL) + .await + .unwrap(); + + let authority = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + let authority_pubkey = authority.pubkey(); + let source_owner = &Keypair::from_bytes(&context.payer.to_bytes()) + .unwrap() + .pubkey(); + + let delegate_args = DelegateArgs::StandardV1 { + amount: transfer_amount, + }; + + da.delegate(&mut context, authority, delegate.pubkey(), delegate_args) + .await + .unwrap(); + + let delegate_role = da + .get_token_delegate_role(&mut context, &da.token.unwrap()) + .await; + // Because this is a pass-through SPL token delegate there will be no role + // set but the record will still exist. + assert_eq!(delegate_role, None); + + // SPL delegate will exist. + let authority_ata = get_associated_token_address(&authority_pubkey, &da.mint.pubkey()); + let authority_token_account = get_account(&mut context, &authority_ata).await; + let authority_token: spl_token::state::Account = + spl_token::state::Account::unpack(&authority_token_account.data).unwrap(); + + assert_eq!(authority_token.delegate, COption::Some(delegate.pubkey())); + + let destination_owner = Pubkey::new_unique(); + let destination_token = get_associated_token_address(&destination_owner, &da.mint.pubkey()); + airdrop(&mut context, &destination_owner, LAMPORTS_PER_SOL) + .await + .unwrap(); + + let args = TransferArgs::V1 { + authorization_data: None, + amount: transfer_amount, + }; + + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + let params = TransferFromParams { + context: &mut context, + authority: &delegate, + source_owner, + destination_owner, + destination_token: None, + authorization_rules: None, + payer: &payer, + args: args.clone(), + }; + + da.transfer_from(params).await.unwrap(); + + let token_account = spl_token::state::Account::unpack( + &context + .banks_client + .get_account(destination_token) + .await + .unwrap() + .unwrap() + .data, + ) + .unwrap(); + + assert_eq!(token_account.amount, transfer_amount); + } + + #[tokio::test] + async fn fake_delegate_fails() { + let mut context = program_test().start_with_context().await; + + let transfer_amount = 1; + + let mut da = DigitalAsset::new(); + da.create_and_mint(&mut context, TokenStandard::NonFungible, None, None, 1) + .await + .unwrap(); + + let delegate = Keypair::new(); + airdrop(&mut context, &delegate.pubkey(), LAMPORTS_PER_SOL) + .await + .unwrap(); + + let authority = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + let authority_pubkey = authority.pubkey(); + let source_owner = &Keypair::from_bytes(&context.payer.to_bytes()) + .unwrap() + .pubkey(); + + let delegate_args = DelegateArgs::StandardV1 { + amount: transfer_amount, + }; + + da.delegate(&mut context, authority, delegate.pubkey(), delegate_args) + .await + .unwrap(); + + let delegate_role = da + .get_token_delegate_role(&mut context, &da.token.unwrap()) + .await; + // Because this is a pass-through SPL token delegate there will be no role + // set but the record will still exist. + assert_eq!(delegate_role, None); + + // SPL delegate will exist. + let authority_ata = get_associated_token_address(&authority_pubkey, &da.mint.pubkey()); + let authority_token_account = get_account(&mut context, &authority_ata).await; + let authority_token: spl_token::state::Account = + spl_token::state::Account::unpack(&authority_token_account.data).unwrap(); + + assert_eq!(authority_token.delegate, COption::Some(delegate.pubkey())); + + let destination_owner = Pubkey::new_unique(); + let destination_token = get_associated_token_address(&destination_owner, &da.mint.pubkey()); + airdrop(&mut context, &destination_owner, LAMPORTS_PER_SOL) + .await + .unwrap(); + + let args = TransferArgs::V1 { + authorization_data: None, + amount: transfer_amount, + }; + + let fake_delegate = Keypair::new(); + airdrop(&mut context, &fake_delegate.pubkey(), LAMPORTS_PER_SOL) + .await + .unwrap(); + + // Associated token account already exists so we pass it in, + // otherwise we will get an "IllegalOwner" errror. + + let params = TransferFromParams { + context: &mut context, + authority: &fake_delegate, + source_owner, + destination_owner, + destination_token: Some(destination_token), + authorization_rules: None, + payer: &fake_delegate, + args, + }; + + let err = da.transfer_from(params).await.unwrap_err(); + + // Owner does not match. + assert_custom_error_ix!(1, err, MetadataError::InvalidAuthorityType); + } +} + +mod auth_rules_transfer { + use mpl_token_auth_rules::payload::Payload; + use mpl_token_metadata::{ + instruction::DelegateArgs, + state::{ProgrammableConfig, TokenDelegateRole}, + }; + + use super::*; + + #[tokio::test] + async fn wallet_to_wallet() { + // Wallet to wallet should skip royalties rules, for now. + + let mut program_test = ProgramTest::new("mpl_token_metadata", mpl_token_metadata::ID, None); + program_test.add_program("mpl_token_auth_rules", mpl_token_auth_rules::ID, None); + let mut context = program_test.start_with_context().await; + + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + // Create rule-set for the transfer requiring the destination to be program owned + // by Token Metadata program. (Token Owned Escrow scenario.) + let (rule_set, auth_data) = create_default_metaplex_rule_set(&mut context, payer).await; + + // Create NFT for transfer tests. + let mut nft = DigitalAsset::new(); + nft.create_and_mint( + &mut context, + TokenStandard::ProgrammableNonFungible, + Some(rule_set), + Some(auth_data.clone()), + 1, + ) + .await + .unwrap(); + + let metadata = nft.get_metadata(&mut context).await; + assert_eq!( + metadata.token_standard, + Some(TokenStandard::ProgrammableNonFungible) + ); + + if let Some(ProgrammableConfig::V1 { + rule_set: Some(rule_set), + }) = metadata.programmable_config + { + assert_eq!(rule_set, rule_set); + } else { + panic!("Missing programmable config"); + } + + let transfer_amount = 1; + + // Our first destination will be an account owned by + // the mpl-token-metadata. This should fail because it's not + // in the program allowlist and also not a wallet-to-wallet + // transfer. + let destination_owner = nft.metadata; + + let authority = &Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + let args = TransferArgs::V1 { + authorization_data: None, + amount: transfer_amount, + }; + + let params = TransferFromParams { + context: &mut context, + authority, + source_owner: &authority.pubkey(), + destination_owner, + destination_token: None, + authorization_rules: Some(rule_set), + payer: authority, + args: args.clone(), + }; + + let err = nft.transfer_from(params).await.unwrap_err(); + + assert_custom_error_ix!( + 2, + err, + mpl_token_auth_rules::error::RuleSetError::ProgramOwnedListCheckFailed + ); + + // Our second destination will be a wallet-to-wallet transfer so should + // circumvent the program owned check and should succeed. + let destination_owner = Pubkey::new_unique(); + + let authority = &Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + let params = TransferFromParams { + context: &mut context, + authority, + source_owner: &authority.pubkey(), + destination_owner, + destination_token: None, + authorization_rules: Some(rule_set), + payer: authority, + args, + }; + + nft.transfer_from(params).await.unwrap(); + + let destination_token = + get_associated_token_address(&destination_owner, &nft.mint.pubkey()); + + let token_account = spl_token::state::Account::unpack( + &context + .banks_client + .get_account(destination_token) + .await + .unwrap() + .unwrap() + .data, + ) + .unwrap(); + + assert_eq!(token_account.amount, transfer_amount); + } + + #[tokio::test] + async fn owner_transfer() { + // Tests an owner transferring from a system wallet to a PDA and vice versa. + let mut program_test = ProgramTest::new("mpl_token_metadata", mpl_token_metadata::ID, None); + program_test.add_program("mpl_token_auth_rules", mpl_token_auth_rules::ID, None); + program_test.add_program("rooster", rooster::ID, None); + let mut context = program_test.start_with_context().await; + + let payer = context.payer.dirty_clone(); + + // Create rule-set for the transfer; this has the Rooster program in the allowlist. + let (rule_set, mut auth_data) = create_default_metaplex_rule_set(&mut context, payer).await; + + // Create NFT for transfer tests. + let mut nft = DigitalAsset::new(); + nft.create_and_mint( + &mut context, + TokenStandard::ProgrammableNonFungible, + Some(rule_set), + Some(auth_data.clone()), + 1, + ) + .await + .unwrap(); + + let transfer_amount = 1; + + let authority = context.payer.dirty_clone(); + let rooster_manager = RoosterManager::init(&mut context, authority).await.unwrap(); + + let authority = context.payer.dirty_clone(); + + // Update auth data payload with the seeds of the PDA we're + // transferring to. + let seeds = SeedsVec { + seeds: vec![ + String::from("rooster").as_bytes().to_vec(), + authority.pubkey().as_ref().to_vec(), + ], + }; + + auth_data.payload.insert( + PayloadKey::DestinationSeeds.to_string(), + PayloadType::Seeds(seeds), + ); + + let args = TransferArgs::V1 { + authorization_data: Some(auth_data.clone()), + amount: transfer_amount, + }; + + let params = TransferFromParams { + context: &mut context, + authority: &authority, + source_owner: &authority.pubkey(), + destination_owner: rooster_manager.pda(), + destination_token: None, + authorization_rules: Some(rule_set), + payer: &authority, + args: args.clone(), + }; + + nft.transfer_from(params).await.unwrap(); + + let destination_token = + get_associated_token_address(&rooster_manager.pda(), &nft.mint.pubkey()); + let dest_token_account = spl_token::state::Account::unpack( + get_account(&mut context, &destination_token) + .await + .data + .as_slice(), + ) + .unwrap(); + + // Destination now has the token. + assert_eq!(dest_token_account.amount, 1); + + // Update auth data payload with the seeds of the PDA we're + // transferring from. + let mut payload = Payload::new(); + let seeds = SeedsVec { + seeds: vec![ + String::from("rooster").as_bytes().to_vec(), + authority.pubkey().as_ref().to_vec(), + ], + }; + payload.insert( + PayloadKey::SourceSeeds.to_string(), + PayloadType::Seeds(seeds), + ); + + // Now we withdraw from Rooster to test the pda-to-system-wallet transfer. + rooster_manager + .withdraw( + &mut context, + &authority, + authority.pubkey(), + nft.mint.pubkey(), + nft.metadata, + nft.master_edition.unwrap(), + rule_set, + payload, + ) + .await + .unwrap(); + + let authority_ata = get_associated_token_address(&authority.pubkey(), &nft.mint.pubkey()); + let authority_ata_account = spl_token::state::Account::unpack( + get_account(&mut context, &authority_ata) + .await + .data + .as_slice(), + ) + .unwrap(); + + assert_eq!(authority_ata_account.amount, 1); + } + + #[tokio::test] + async fn transfer_delegate() { + // Tests a delegate transferring from a system wallet to a PDA and vice versa. + let mut program_test = ProgramTest::new("mpl_token_metadata", mpl_token_metadata::ID, None); + program_test.add_program("mpl_token_auth_rules", mpl_token_auth_rules::ID, None); + program_test.add_program("rooster", rooster::ID, None); + let mut context = program_test.start_with_context().await; + + let payer = context.payer.dirty_clone(); + + // Create rule-set for the transfer; this has the Rooster program in the allowlist. + let (rule_set, mut auth_data) = create_default_metaplex_rule_set(&mut context, payer).await; + + // Create NFT for transfer tests. + let mut nft = DigitalAsset::new(); + nft.create_and_mint( + &mut context, + TokenStandard::ProgrammableNonFungible, + Some(rule_set), + Some(auth_data.clone()), + 1, + ) + .await + .unwrap(); + + let transfer_amount = 1; + + // Create a transfer delegate + let payer = context.payer.dirty_clone(); + let delegate = Keypair::new(); + airdrop(&mut context, &delegate.pubkey(), LAMPORTS_PER_SOL) + .await + .unwrap(); + + let delegate_args = DelegateArgs::TransferV1 { + amount: transfer_amount, + authorization_data: None, + }; + + nft.delegate(&mut context, payer, delegate.pubkey(), delegate_args) + .await + .unwrap(); + + let delegate_role = nft + .get_token_delegate_role(&mut context, &nft.token.unwrap()) + .await; + + assert_eq!(delegate_role, Some(TokenDelegateRole::Transfer)); + + // Set up the PDA account. + let authority = context.payer.dirty_clone(); + let rooster_manager = RoosterManager::init(&mut context, authority).await.unwrap(); + + let authority = context.payer.dirty_clone(); + + // Update auth data payload with the seeds of the PDA we're + // transferring to. + let seeds = SeedsVec { + seeds: vec![ + String::from("rooster").as_bytes().to_vec(), + authority.pubkey().as_ref().to_vec(), + ], + }; + + auth_data.payload.insert( + PayloadKey::DestinationSeeds.to_string(), + PayloadType::Seeds(seeds), + ); + + let args = TransferArgs::V1 { + authorization_data: Some(auth_data.clone()), + amount: transfer_amount, + }; + + let params = TransferFromParams { + context: &mut context, + authority: &delegate, + source_owner: &authority.pubkey(), + destination_owner: rooster_manager.pda(), + destination_token: None, + authorization_rules: Some(rule_set), + payer: &authority, + args: args.clone(), + }; + + nft.transfer_from(params).await.unwrap(); + + let rooster_ata = get_associated_token_address(&rooster_manager.pda(), &nft.mint.pubkey()); + let rooster_ata_account = spl_token::state::Account::unpack( + get_account(&mut context, &rooster_ata) + .await + .data + .as_slice(), + ) + .unwrap(); + + // Destination now has the token. + assert_eq!(rooster_ata_account.amount, 1); + + let rooster_delegate_args = RoosterDelegateArgs { + amount: 1, + bump: rooster_manager.bump(), + authority: authority.pubkey(), + }; + + // Create new delegate using Rooster + rooster_manager + .delegate( + &mut context, + &delegate, + nft.mint.pubkey(), + nft.metadata, + nft.master_edition.unwrap(), + Some(rule_set), + rooster_delegate_args, + ) + .await + .unwrap(); + + // Update auth data payload with the seeds of the PDA we're + // transferring from. + let seeds = SeedsVec { + seeds: vec![ + String::from("rooster").as_bytes().to_vec(), + authority.pubkey().as_ref().to_vec(), + ], + }; + auth_data.payload.insert( + PayloadKey::SourceSeeds.to_string(), + PayloadType::Seeds(seeds), + ); + + let args = TransferArgs::V1 { + authorization_data: Some(auth_data.clone()), + amount: transfer_amount, + }; + + let params = TransferToParams { + context: &mut context, + authority: &delegate, + source_owner: &rooster_manager.pda(), + source_token: &rooster_ata, + destination_owner: authority.pubkey(), + destination_token: Some(nft.token.unwrap()), + authorization_rules: Some(rule_set), + payer: &delegate, + args: args.clone(), + }; + + nft.transfer_to(params).await.unwrap(); + + let authority_ata = get_associated_token_address(&authority.pubkey(), &nft.mint.pubkey()); + let authority_ata_account = spl_token::state::Account::unpack( + get_account(&mut context, &authority_ata) + .await + .data + .as_slice(), + ) + .unwrap(); + + // Destination now has the token. + assert_eq!(authority_ata_account.amount, 1); + } + + #[tokio::test] + async fn sale_delegate() { + // Tests a delegate transferring from a system wallet to a PDA and vice versa. + let mut program_test = ProgramTest::new("mpl_token_metadata", mpl_token_metadata::ID, None); + program_test.add_program("mpl_token_auth_rules", mpl_token_auth_rules::ID, None); + program_test.add_program("rooster", rooster::ID, None); + let mut context = program_test.start_with_context().await; + + let payer = context.payer.dirty_clone(); + + // Create rule-set for the transfer; this has the Rooster program in the allowlist. + let (rule_set, mut auth_data) = create_default_metaplex_rule_set(&mut context, payer).await; + + // Create NFT for transfer tests. + let mut nft = DigitalAsset::new(); + nft.create_and_mint( + &mut context, + TokenStandard::ProgrammableNonFungible, + Some(rule_set), + Some(auth_data.clone()), + 1, + ) + .await + .unwrap(); + + let transfer_amount = 1; + + // Create a sale delegate + let payer = context.payer.dirty_clone(); + let delegate = Keypair::new(); + airdrop(&mut context, &delegate.pubkey(), LAMPORTS_PER_SOL) + .await + .unwrap(); + + let delegate_args = DelegateArgs::SaleV1 { + amount: transfer_amount, + authorization_data: Some(auth_data.clone()), + }; + nft.delegate(&mut context, payer, delegate.pubkey(), delegate_args) + .await + .unwrap(); + + let delegate_role = nft + .get_token_delegate_role(&mut context, &nft.token.unwrap()) + .await; + + assert_eq!(delegate_role, Some(TokenDelegateRole::Sale)); + + // Set up the PDA account. + let authority = context.payer.dirty_clone(); + let rooster_manager = RoosterManager::init(&mut context, authority).await.unwrap(); + + let authority = context.payer.dirty_clone(); + + // Update auth data payload with the seeds of the PDA we're + // transferring to. + let seeds = SeedsVec { + seeds: vec![ + String::from("rooster").as_bytes().to_vec(), + authority.pubkey().as_ref().to_vec(), + ], + }; + + auth_data.payload.insert( + PayloadKey::DestinationSeeds.to_string(), + PayloadType::Seeds(seeds), + ); + + let args = TransferArgs::V1 { + authorization_data: Some(auth_data.clone()), + amount: transfer_amount, + }; + + let params = TransferFromParams { + context: &mut context, + authority: &delegate, + source_owner: &authority.pubkey(), + destination_owner: rooster_manager.pda(), + destination_token: None, + authorization_rules: Some(rule_set), + payer: &authority, + args: args.clone(), + }; + + nft.transfer_from(params).await.unwrap(); + + let rooster_ata = get_associated_token_address(&rooster_manager.pda(), &nft.mint.pubkey()); + let rooster_ata_account = spl_token::state::Account::unpack( + get_account(&mut context, &rooster_ata) + .await + .data + .as_slice(), + ) + .unwrap(); + + // Destination now has the token. + assert_eq!(rooster_ata_account.amount, 1); + + let rooster_delegate_args = RoosterDelegateArgs { + amount: 1, + bump: rooster_manager.bump(), + authority: authority.pubkey(), + }; + + // Create new delegate using Rooster + rooster_manager + .delegate( + &mut context, + &delegate, + nft.mint.pubkey(), + nft.metadata, + nft.master_edition.unwrap(), + Some(rule_set), + rooster_delegate_args, + ) + .await + .unwrap(); + + // Update auth data payload with the seeds of the PDA we're + // transferring from. + let seeds = SeedsVec { + seeds: vec![ + String::from("rooster").as_bytes().to_vec(), + authority.pubkey().as_ref().to_vec(), + ], + }; + auth_data.payload.insert( + PayloadKey::SourceSeeds.to_string(), + PayloadType::Seeds(seeds), + ); + + let args = TransferArgs::V1 { + authorization_data: Some(auth_data.clone()), + amount: transfer_amount, + }; + + let params = TransferToParams { + context: &mut context, + authority: &delegate, + source_owner: &rooster_manager.pda(), + source_token: &rooster_ata, + destination_owner: authority.pubkey(), + destination_token: Some(nft.token.unwrap()), + authorization_rules: Some(rule_set), + payer: &delegate, + args: args.clone(), + }; + + nft.transfer_to(params).await.unwrap(); + + let authority_ata = get_associated_token_address(&authority.pubkey(), &nft.mint.pubkey()); + let authority_ata_account = spl_token::state::Account::unpack( + get_account(&mut context, &authority_ata) + .await + .data + .as_slice(), + ) + .unwrap(); + + // Destination now has the token. + assert_eq!(authority_ata_account.amount, 1); + } + + #[tokio::test] + async fn no_auth_rules_skips_validation() { + // Tests a pNFT with a rule_set of None skipping validation and still being + // transferred correctly. + let mut program_test = ProgramTest::new("mpl_token_metadata", mpl_token_metadata::ID, None); + program_test.add_program("mpl_token_auth_rules", mpl_token_auth_rules::ID, None); + let mut context = program_test.start_with_context().await; + + // Create NFT for transfer tests. + let mut nft = DigitalAsset::new(); + nft.create_and_mint( + &mut context, + TokenStandard::ProgrammableNonFungible, + None, + None, + 1, + ) + .await + .unwrap(); + + let transfer_amount = 1; + + // Our destination will be an account owned by the mpl-token-metadata + // program. This will fail normally because it's not + // in the program allowlist and also not a wallet-to-wallet + // transfer. However, with no rule set present it should succeed because + // there are no rules to validate. + let destination_owner = nft.metadata; + + let authority = &Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + let args = TransferArgs::V1 { + authorization_data: None, + amount: transfer_amount, + }; + + let params = TransferFromParams { + context: &mut context, + authority, + source_owner: &authority.pubkey(), + destination_owner, + destination_token: None, + authorization_rules: None, + payer: authority, + args: args.clone(), + }; + + // Transfer should succeed because no rule set is present on the NFT. + nft.transfer_from(params).await.unwrap(); + } +} diff --git a/token-metadata/program/tests/unlock.rs b/token-metadata/program/tests/unlock.rs new file mode 100644 index 0000000000..d2f9354465 --- /dev/null +++ b/token-metadata/program/tests/unlock.rs @@ -0,0 +1,166 @@ +#![cfg(feature = "test-bpf")] +pub mod utils; + +use solana_program_test::*; +use utils::*; + +mod utility { + + use mpl_token_metadata::{ + instruction::DelegateArgs, + pda::find_token_record_account, + state::{TokenRecord, TokenStandard, TokenState}, + }; + use solana_program::{borsh::try_from_slice_unchecked, program_pack::Pack}; + use solana_sdk::signature::{Keypair, Signer}; + use spl_token::state::Account; + + use super::*; + + #[tokio::test] + async fn delegate_unlock_programmable_nonfungible() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create_and_mint( + &mut context, + TokenStandard::ProgrammableNonFungible, + None, + None, + 1, + ) + .await + .unwrap(); + + // asserts + + let (pda_key, _) = find_token_record_account(&asset.mint.pubkey(), &asset.token.unwrap()); + + let pda = get_account(&mut context, &pda_key).await; + let token_record: TokenRecord = try_from_slice_unchecked(&pda.data).unwrap(); + + assert_eq!(token_record.state, TokenState::Unlocked); + + // set a utility delegate + + let delegate = Keypair::new(); + let delegate_pubkey = delegate.pubkey(); + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + asset + .delegate( + &mut context, + payer, + delegate_pubkey, + DelegateArgs::UtilityV1 { + amount: 1, + authorization_data: None, + }, + ) + .await + .unwrap(); + + // lock + + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + let token_delegate = Keypair::from_bytes(&delegate.to_bytes()).unwrap(); + + asset + .lock(&mut context, token_delegate, Some(pda_key), payer) + .await + .unwrap(); + + // asserts + + let pda = get_account(&mut context, &pda_key).await; + let token_record: TokenRecord = try_from_slice_unchecked(&pda.data).unwrap(); + + assert_eq!(token_record.state, TokenState::Locked); + + // unlock + + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + let token_delegate = Keypair::from_bytes(&delegate.to_bytes()).unwrap(); + + asset + .unlock(&mut context, token_delegate, Some(pda_key), payer) + .await + .unwrap(); + + // asserts + + let pda = get_account(&mut context, &pda_key).await; + let token_record: TokenRecord = try_from_slice_unchecked(&pda.data).unwrap(); + + assert_eq!(token_record.state, TokenState::Unlocked); + } + + #[tokio::test] + async fn delegate_unlock_nonfungible() { + let mut context = program_test().start_with_context().await; + + // asset + + let mut asset = DigitalAsset::default(); + asset + .create_and_mint(&mut context, TokenStandard::NonFungible, None, None, 1) + .await + .unwrap(); + + // asserts + + let token_account = get_account(&mut context, &asset.token.unwrap()).await; + let token = Account::unpack(&token_account.data).unwrap(); + // should NOT be frozen + assert!(!token.is_frozen()); + + // set a utility delegate + + let delegate = Keypair::new(); + let delegate_pubkey = delegate.pubkey(); + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + asset + .delegate( + &mut context, + payer, + delegate_pubkey, + DelegateArgs::StandardV1 { amount: 1 }, + ) + .await + .unwrap(); + + // lock + + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + let token_delegate = Keypair::from_bytes(&delegate.to_bytes()).unwrap(); + + asset + .lock(&mut context, token_delegate, None, payer) + .await + .unwrap(); + + let token_account = get_account(&mut context, &asset.token.unwrap()).await; + let token = Account::unpack(&token_account.data).unwrap(); + // should be frozen + assert!(token.is_frozen()); + + // unlock + + let payer = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + let token_delegate = Keypair::from_bytes(&delegate.to_bytes()).unwrap(); + + asset + .unlock(&mut context, token_delegate, None, payer) + .await + .unwrap(); + + let token_account = get_account(&mut context, &asset.token.unwrap()).await; + let token = Account::unpack(&token_account.data).unwrap(); + // should NOT be frozen + assert!(!token.is_frozen()); + } +} diff --git a/token-metadata/program/tests/update.rs b/token-metadata/program/tests/update.rs new file mode 100644 index 0000000000..2ba7dad8f2 --- /dev/null +++ b/token-metadata/program/tests/update.rs @@ -0,0 +1,164 @@ +#![cfg(feature = "test-bpf")] +pub mod utils; + +use mpl_token_metadata::{ + instruction::{builders::UpdateBuilder, InstructionBuilder}, + state::{MAX_NAME_LENGTH, MAX_SYMBOL_LENGTH, MAX_URI_LENGTH}, + utils::puffed_out_string, +}; +use solana_program_test::*; +use solana_sdk::{signature::Signer, transaction::Transaction}; +use utils::{DigitalAsset, *}; + +mod update { + + use mpl_token_metadata::{ + instruction::{RuleSetToggle, UpdateArgs}, + state::{Data, ProgrammableConfig, TokenStandard}, + }; + use solana_sdk::signature::Keypair; + + use super::*; + #[tokio::test] + async fn success_update() { + let context = &mut program_test().start_with_context().await; + + let update_authority = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + let mut da = DigitalAsset::new(); + da.create(context, TokenStandard::NonFungible, None) + .await + .unwrap(); + + let metadata = da.get_metadata(context).await; + assert_eq!( + metadata.data.name, + puffed_out_string(DEFAULT_NAME, MAX_NAME_LENGTH) + ); + assert_eq!( + metadata.data.symbol, + puffed_out_string(DEFAULT_SYMBOL, MAX_SYMBOL_LENGTH) + ); + assert_eq!( + metadata.data.uri, + puffed_out_string(DEFAULT_URI, MAX_URI_LENGTH) + ); + assert_eq!(metadata.update_authority, update_authority.pubkey()); + + let new_name = puffed_out_string("New Name", MAX_NAME_LENGTH); + let new_symbol = puffed_out_string("NEW", MAX_SYMBOL_LENGTH); + let new_uri = puffed_out_string("https://new.digital.asset.org", MAX_URI_LENGTH); + + // Change a few values and update the metadata. + let data = Data { + name: new_name.clone(), + symbol: new_symbol.clone(), + uri: new_uri.clone(), + creators: None, + seller_fee_basis_points: 0, + }; + + let mut update_args = UpdateArgs::default(); + let UpdateArgs::V1 { + data: current_data, .. + } = &mut update_args; + *current_data = Some(data); + + let mut builder = UpdateBuilder::new(); + builder + .authority(update_authority.pubkey()) + .metadata(da.metadata) + .mint(da.mint.pubkey()) + .payer(update_authority.pubkey()); + + if let Some(edition) = da.master_edition { + builder.edition(edition); + } + + let update_ix = builder.build(update_args).unwrap().instruction(); + + let tx = Transaction::new_signed_with_payer( + &[update_ix], + Some(&update_authority.pubkey()), + &[&update_authority], + context.last_blockhash, + ); + + context.banks_client.process_transaction(tx).await.unwrap(); + + // checks the created metadata values + let metadata = da.get_metadata(context).await; + + assert_eq!(metadata.data.name, new_name); + assert_eq!(metadata.data.symbol, new_symbol); + assert_eq!(metadata.data.uri, new_uri); + } + + #[tokio::test] + async fn update_pfnt_config() { + let mut program_test = ProgramTest::new("mpl_token_metadata", mpl_token_metadata::ID, None); + program_test.add_program("mpl_token_auth_rules", mpl_token_auth_rules::ID, None); + let context = &mut program_test.start_with_context().await; + + let authority = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + // Create rule-set for the transfer + let (authorization_rules, _auth_data) = + create_default_metaplex_rule_set(context, authority).await; + + let update_authority = Keypair::from_bytes(&context.payer.to_bytes()).unwrap(); + + let mut da = DigitalAsset::new(); + da.create( + context, + TokenStandard::ProgrammableNonFungible, + Some(authorization_rules), + ) + .await + .unwrap(); + + let metadata = da.get_metadata(context).await; + + if let Some(ProgrammableConfig::V1 { + rule_set: Some(rule_set), + }) = metadata.programmable_config + { + assert_eq!(rule_set, authorization_rules); + } else { + panic!("Missing rule set programmable config"); + } + + let mut update_args = UpdateArgs::default(); + let UpdateArgs::V1 { rule_set, .. } = &mut update_args; + // remove the rule set + *rule_set = RuleSetToggle::Clear; + + let mut builder = UpdateBuilder::new(); + builder + .authority(update_authority.pubkey()) + .metadata(da.metadata) + .mint(da.mint.pubkey()) + .authorization_rules(authorization_rules) + .payer(update_authority.pubkey()); + + if let Some(edition) = da.master_edition { + builder.edition(edition); + } + + let update_ix = builder.build(update_args).unwrap().instruction(); + + let tx = Transaction::new_signed_with_payer( + &[update_ix], + Some(&update_authority.pubkey()), + &[&update_authority], + context.last_blockhash, + ); + + context.banks_client.process_transaction(tx).await.unwrap(); + + // checks the created metadata values + let metadata = da.get_metadata(context).await; + + assert_eq!(metadata.programmable_config, None); + } +} diff --git a/token-metadata/program/tests/utils/assert.rs b/token-metadata/program/tests/utils/assert.rs index dbda669bd2..6ae6de40a7 100644 --- a/token-metadata/program/tests/utils/assert.rs +++ b/token-metadata/program/tests/utils/assert.rs @@ -36,3 +36,30 @@ macro_rules! assert_custom_error { }; }; } + +#[macro_export] +macro_rules! assert_custom_error_ix { + ($ix:expr, $error:expr, $matcher:pat) => { + match $error { + BanksClientError::TransactionError(TransactionError::InstructionError( + $ix, + InstructionError::Custom(x), + )) => match FromPrimitive::from_i32(x as i32) { + Some($matcher) => assert!(true), + Some(other) => { + assert!( + false, + "Expected another custom instruction error than '{:#?}'", + other + ) + } + None => assert!(false, "Expected custom instruction error"), + }, + err => assert!( + false, + "Expected custom instruction error but got '{:#?}'", + err + ), + }; + }; +} diff --git a/token-metadata/program/tests/utils/digital_asset.rs b/token-metadata/program/tests/utils/digital_asset.rs new file mode 100644 index 0000000000..98062057c1 --- /dev/null +++ b/token-metadata/program/tests/utils/digital_asset.rs @@ -0,0 +1,720 @@ +use mpl_token_metadata::{ + id, + instruction::{ + builders::{ + CreateBuilder, DelegateBuilder, LockBuilder, MigrateBuilder, MintBuilder, + RevokeBuilder, TransferBuilder, UnlockBuilder, + }, + CreateArgs, DelegateArgs, InstructionBuilder, LockArgs, MetadataDelegateRole, MigrateArgs, + MintArgs, RevokeArgs, TransferArgs, UnlockArgs, + }, + pda::{find_metadata_delegate_record_account, find_token_record_account}, + processor::AuthorizationData, + state::{ + AssetData, Creator, Metadata, PrintSupply, ProgrammableConfig, TokenDelegateRole, + TokenMetadataAccount, TokenRecord, TokenStandard, EDITION, PREFIX, + }, +}; +use solana_program::{borsh::try_from_slice_unchecked, pubkey::Pubkey}; +use solana_program_test::{BanksClientError, ProgramTestContext}; +use solana_sdk::{ + compute_budget::ComputeBudgetInstruction, + signature::{Keypair, Signer}, + transaction::Transaction, +}; +use spl_associated_token_account::{ + get_associated_token_address, instruction::create_associated_token_account, +}; + +use super::get_account; + +pub const DEFAULT_NAME: &str = "Digital Asset"; +pub const DEFAULT_SYMBOL: &str = "DA"; +pub const DEFAULT_URI: &str = "https://digital.asset.org"; + +pub struct DigitalAsset { + pub metadata: Pubkey, + pub mint: Keypair, + pub token: Option, + pub master_edition: Option, + pub token_record: Option, +} + +impl Default for DigitalAsset { + fn default() -> Self { + Self::new() + } +} + +impl DigitalAsset { + pub fn new() -> Self { + let mint = Keypair::new(); + let mint_pubkey = mint.pubkey(); + let program_id = id(); + + let metadata_seeds = &[PREFIX.as_bytes(), program_id.as_ref(), mint_pubkey.as_ref()]; + let (metadata, _) = Pubkey::find_program_address(metadata_seeds, &program_id); + + Self { + metadata, + mint, + token: None, + master_edition: None, + token_record: None, + } + } + + pub async fn create( + &mut self, + context: &mut ProgramTestContext, + token_standard: TokenStandard, + authorization_rules: Option, + ) -> Result<(), BanksClientError> { + let mut asset = AssetData::new( + token_standard, + String::from(DEFAULT_NAME), + String::from(DEFAULT_SYMBOL), + String::from(DEFAULT_URI), + ); + asset.seller_fee_basis_points = 500; + + let creators = vec![Creator { + address: context.payer.pubkey(), + share: 100, + verified: true, + }]; + asset.creators = Some(creators); + asset.rule_set = authorization_rules; + + let payer_pubkey = context.payer.pubkey(); + let mint_pubkey = self.mint.pubkey(); + + let program_id = id(); + let mut builder = CreateBuilder::new(); + builder + .metadata(self.metadata) + .mint(self.mint.pubkey()) + .authority(payer_pubkey) + .payer(payer_pubkey) + .update_authority(payer_pubkey) + .initialize_mint(true) + .update_authority_as_signer(true); + + let master_edition = match token_standard { + TokenStandard::NonFungible | TokenStandard::ProgrammableNonFungible => { + // master edition PDA address + let master_edition_seeds = &[ + PREFIX.as_bytes(), + program_id.as_ref(), + mint_pubkey.as_ref(), + EDITION.as_bytes(), + ]; + let (master_edition, _) = Pubkey::find_program_address(master_edition_seeds, &id()); + // sets the master edition to the builder + builder.master_edition(master_edition); + Some(master_edition) + } + _ => None, + }; + // builds the instruction + let create_ix = builder + .build(CreateArgs::V1 { + asset_data: asset, + decimals: Some(0), + print_supply: Some(PrintSupply::Zero), + }) + .unwrap() + .instruction(); + + let compute_ix = ComputeBudgetInstruction::set_compute_unit_limit(800_000); + + let tx = Transaction::new_signed_with_payer( + &[compute_ix, create_ix], + Some(&context.payer.pubkey()), + &[&context.payer, &self.mint], + context.last_blockhash, + ); + + self.master_edition = master_edition; + + context.banks_client.process_transaction(tx).await + } + + pub async fn mint( + &mut self, + context: &mut ProgramTestContext, + authorization_rules: Option, + authorization_data: Option, + amount: u64, + ) -> Result<(), BanksClientError> { + let payer_pubkey = context.payer.pubkey(); + let (token, _) = Pubkey::find_program_address( + &[ + &payer_pubkey.to_bytes(), + &spl_token::id().to_bytes(), + &self.mint.pubkey().to_bytes(), + ], + &spl_associated_token_account::id(), + ); + + let (token_record, _) = find_token_record_account(&self.mint.pubkey(), &token); + + let token_record_opt = if self.is_pnft(context).await { + Some(token_record) + } else { + None + }; + + let mut builder = MintBuilder::new(); + builder + .token(token) + .token_record(token_record) + .token_owner(payer_pubkey) + .metadata(self.metadata) + .mint(self.mint.pubkey()) + .payer(payer_pubkey) + .authority(payer_pubkey); + + if let Some(edition) = self.master_edition { + builder.master_edition(edition); + } + + if let Some(authorization_rules) = authorization_rules { + builder.authorization_rules(authorization_rules); + } + + let mint_ix = builder + .build(MintArgs::V1 { + amount, + authorization_data, + }) + .unwrap() + .instruction(); + + let compute_ix = ComputeBudgetInstruction::set_compute_unit_limit(800_000); + + let tx = Transaction::new_signed_with_payer( + &[compute_ix, mint_ix], + Some(&context.payer.pubkey()), + &[&context.payer], + context.last_blockhash, + ); + + match context.banks_client.process_transaction(tx).await { + Ok(_) => { + self.token = Some(token); + self.token_record = token_record_opt; + Ok(()) + } + Err(error) => Err(error), + } + } + + pub async fn create_and_mint( + &mut self, + context: &mut ProgramTestContext, + token_standard: TokenStandard, + authorization_rules: Option, + authorization_data: Option, + amount: u64, + ) -> Result<(), BanksClientError> { + // creates the metadata + self.create(context, token_standard, authorization_rules) + .await + .unwrap(); + // mints tokens + self.mint(context, authorization_rules, authorization_data, amount) + .await + } + + pub async fn delegate( + &mut self, + context: &mut ProgramTestContext, + payer: Keypair, + delegate: Pubkey, + args: DelegateArgs, + ) -> Result<(), BanksClientError> { + let mut builder = DelegateBuilder::new(); + builder + .delegate(delegate) + .mint(self.mint.pubkey()) + .metadata(self.metadata) + .payer(payer.pubkey()) + .authority(payer.pubkey()) + .spl_token_program(spl_token::ID); + + match args { + DelegateArgs::CollectionV1 { .. } => { + let (delegate_record, _) = find_metadata_delegate_record_account( + &self.mint.pubkey(), + MetadataDelegateRole::Collection, + &payer.pubkey(), + &delegate, + ); + builder.delegate_record(delegate_record); + } + DelegateArgs::SaleV1 { .. } + | DelegateArgs::TransferV1 { .. } + | DelegateArgs::UtilityV1 { .. } + | DelegateArgs::StakingV1 { .. } => { + let (token_record, _) = + find_token_record_account(&self.mint.pubkey(), &self.token.unwrap()); + builder.token_record(token_record); + } + DelegateArgs::UpdateV1 { .. } => { + let (delegate_record, _) = find_metadata_delegate_record_account( + &self.mint.pubkey(), + MetadataDelegateRole::Update, + &payer.pubkey(), + &delegate, + ); + builder.delegate_record(delegate_record); + } + DelegateArgs::StandardV1 { .. } => { /* nothing to add */ } + } + + if let Some(edition) = self.master_edition { + builder.master_edition(edition); + } + + if let Some(token) = self.token { + builder.token(token); + } + + // determines if we need to set the rule set + let metadata_account = get_account(context, &self.metadata).await; + let metadata: Metadata = try_from_slice_unchecked(&metadata_account.data).unwrap(); + + if let Some(ProgrammableConfig::V1 { + rule_set: Some(rule_set), + }) = metadata.programmable_config + { + builder.authorization_rules(rule_set); + builder.authorization_rules_program(mpl_token_auth_rules::ID); + } + + let delegate_ix = builder.build(args.clone()).unwrap().instruction(); + + let tx = Transaction::new_signed_with_payer( + &[delegate_ix], + Some(&payer.pubkey()), + &[&payer], + context.last_blockhash, + ); + + context.banks_client.process_transaction(tx).await + } + + pub async fn migrate( + &mut self, + context: &mut ProgramTestContext, + authority: Keypair, + collection_metadata: Pubkey, + args: MigrateArgs, + ) -> Result<(), BanksClientError> { + let mut builder = MigrateBuilder::new(); + builder + .mint(self.mint.pubkey()) + .metadata(self.metadata) + .edition(self.master_edition.unwrap()) + .token(self.token.unwrap()) + .payer(authority.pubkey()) + .collection_metadata(collection_metadata) + .authority(authority.pubkey()); + + let migrate_ix = builder.build(args.clone()).unwrap().instruction(); + + let tx = Transaction::new_signed_with_payer( + &[migrate_ix], + Some(&authority.pubkey()), + &[&authority], + context.last_blockhash, + ); + + context.banks_client.process_transaction(tx).await + } + + pub async fn revoke( + &mut self, + context: &mut ProgramTestContext, + payer: Keypair, + approver: Keypair, + delegate: Pubkey, + args: RevokeArgs, + ) -> Result<(), BanksClientError> { + let mut builder = RevokeBuilder::new(); + builder + .delegate(delegate) + .mint(self.mint.pubkey()) + .metadata(self.metadata) + .payer(approver.pubkey()) + .authority(approver.pubkey()) + .spl_token_program(spl_token::ID); + + match args { + RevokeArgs::CollectionV1 => { + let (delegate_record, _) = find_metadata_delegate_record_account( + &self.mint.pubkey(), + MetadataDelegateRole::Collection, + &payer.pubkey(), + &delegate, + ); + builder.delegate_record(delegate_record); + } + RevokeArgs::SaleV1 + | RevokeArgs::TransferV1 + | RevokeArgs::UtilityV1 + | RevokeArgs::StakingV1 => { + let (token_record, _) = + find_token_record_account(&self.mint.pubkey(), &self.token.unwrap()); + builder.token_record(token_record); + } + RevokeArgs::UpdateV1 => { + let (delegate_record, _) = find_metadata_delegate_record_account( + &self.mint.pubkey(), + MetadataDelegateRole::Update, + &payer.pubkey(), + &delegate, + ); + builder.delegate_record(delegate_record); + } + RevokeArgs::StandardV1 { .. } => { /* nothing to add */ } + } + + if let Some(edition) = self.master_edition { + builder.master_edition(edition); + } + + if let Some(token) = self.token { + builder.token(token); + } + + let revoke_ix = builder.build(args.clone()).unwrap().instruction(); + + let tx = Transaction::new_signed_with_payer( + &[revoke_ix], + Some(&payer.pubkey()), + &[&approver, &payer], + context.last_blockhash, + ); + + context.banks_client.process_transaction(tx).await + } + + pub async fn transfer_from( + &self, + params: TransferFromParams<'_>, + ) -> Result<(), BanksClientError> { + let TransferFromParams { + context, + authority, + source_owner, + destination_owner, + destination_token, + authorization_rules, + payer, + args, + } = params; + + let compute_ix = ComputeBudgetInstruction::set_compute_unit_limit(800_000); + let mut instructions = vec![compute_ix]; + + let destination_token = if let Some(destination_token) = destination_token { + destination_token + } else { + instructions.push(create_associated_token_account( + &authority.pubkey(), + &destination_owner, + &self.mint.pubkey(), + &spl_token::id(), + )); + + get_associated_token_address(&destination_owner, &self.mint.pubkey()) + }; + + let mut builder = TransferBuilder::new(); + builder + .authority(authority.pubkey()) + .token_owner(*source_owner) + .token(self.token.unwrap()) + .destination_owner(destination_owner) + .destination(destination_token) + .metadata(self.metadata) + .payer(payer.pubkey()) + .mint(self.mint.pubkey()); + + if let Some(record) = self.token_record { + builder.owner_token_record(record); + } + + // This can be optional for non pNFTs but always include it for now. + let (destination_token_record, _bump) = + find_token_record_account(&self.mint.pubkey(), &destination_token); + builder.destination_token_record(destination_token_record); + + if let Some(master_edition) = self.master_edition { + builder.edition(master_edition); + } + + if let Some(authorization_rules) = authorization_rules { + builder.authorization_rules(authorization_rules); + builder.authorization_rules_program(mpl_token_auth_rules::ID); + } + + let transfer_ix = builder.build(args).unwrap().instruction(); + + instructions.push(transfer_ix); + + let tx = Transaction::new_signed_with_payer( + &instructions, + Some(&authority.pubkey()), + &[authority, payer], + context.last_blockhash, + ); + + context.banks_client.process_transaction(tx).await + } + + pub async fn lock( + &mut self, + context: &mut ProgramTestContext, + delegate: Keypair, + token_record: Option, + payer: Keypair, + ) -> Result<(), BanksClientError> { + let mut builder = LockBuilder::new(); + builder + .authority(delegate.pubkey()) + .mint(self.mint.pubkey()) + .metadata(self.metadata) + .payer(payer.pubkey()) + .spl_token_program(spl_token::ID); + + if let Some(token_record) = token_record { + builder.token_record(token_record); + } + + if let Some(edition) = self.master_edition { + builder.edition(edition); + } + + if let Some(token) = self.token { + builder.token(token); + } + + let utility_ix = builder + .build(LockArgs::V1 { + authorization_data: None, + }) + .unwrap() + .instruction(); + + let tx = Transaction::new_signed_with_payer( + &[utility_ix], + Some(&payer.pubkey()), + &[&delegate, &payer], + context.last_blockhash, + ); + + context.banks_client.process_transaction(tx).await + } + + pub async fn unlock( + &mut self, + context: &mut ProgramTestContext, + delegate: Keypair, + token_record: Option, + payer: Keypair, + ) -> Result<(), BanksClientError> { + let mut builder = UnlockBuilder::new(); + builder + .authority(delegate.pubkey()) + .mint(self.mint.pubkey()) + .metadata(self.metadata) + .payer(payer.pubkey()) + .spl_token_program(spl_token::ID); + + if let Some(token_record) = token_record { + builder.token_record(token_record); + } + + if let Some(edition) = self.master_edition { + builder.edition(edition); + } + + if let Some(token) = self.token { + builder.token(token); + } + + let utility_ix = builder + .build(UnlockArgs::V1 { + authorization_data: None, + }) + .unwrap() + .instruction(); + + let tx = Transaction::new_signed_with_payer( + &[utility_ix], + Some(&payer.pubkey()), + &[&delegate, &payer], + context.last_blockhash, + ); + + context.banks_client.process_transaction(tx).await + } + + pub async fn transfer_to(&self, params: TransferToParams<'_>) -> Result<(), BanksClientError> { + let TransferToParams { + context, + authority, + source_owner, + source_token, + destination_owner, + destination_token, + authorization_rules, + payer, + args, + } = params; + + // Increase compute budget to handle larger test transactions. + let compute_ix = ComputeBudgetInstruction::set_compute_unit_limit(400_000); + let mut instructions = vec![compute_ix]; + + let destination_token = if let Some(destination_token) = destination_token { + destination_token + } else { + instructions.push(create_associated_token_account( + &authority.pubkey(), + &destination_owner, + &self.mint.pubkey(), + &spl_token::id(), + )); + + get_associated_token_address(&destination_owner, &self.mint.pubkey()) + }; + + let mut builder = TransferBuilder::new(); + builder + .authority(authority.pubkey()) + .token_owner(*source_owner) + .token(*source_token) + .destination_owner(destination_owner) + .destination(destination_token) + .metadata(self.metadata) + .payer(payer.pubkey()) + .mint(self.mint.pubkey()); + + // This can be optional for non pNFTs but always include it for now. + let (owner_token_record, _bump) = + find_token_record_account(&self.mint.pubkey(), source_token); + builder.owner_token_record(owner_token_record); + + // This can be optional for non pNFTs but always include it for now. + let (destination_token_record, _bump) = + find_token_record_account(&self.mint.pubkey(), &destination_token); + builder.destination_token_record(destination_token_record); + + if let Some(master_edition) = self.master_edition { + builder.edition(master_edition); + } + + if let Some(authorization_rules) = authorization_rules { + builder.authorization_rules(authorization_rules); + builder.authorization_rules_program(mpl_token_auth_rules::ID); + } + + let transfer_ix = builder.build(args).unwrap().instruction(); + + instructions.push(transfer_ix); + + let tx = Transaction::new_signed_with_payer( + &instructions, + Some(&authority.pubkey()), + &[authority, payer], + context.last_blockhash, + ); + + context.banks_client.process_transaction(tx).await + } + + pub async fn get_metadata(&self, context: &mut ProgramTestContext) -> Metadata { + let metadata_account = context + .banks_client + .get_account(self.metadata) + .await + .unwrap() + .unwrap(); + + Metadata::safe_deserialize(&metadata_account.data).unwrap() + } + + pub async fn get_asset_data(&self, context: &mut ProgramTestContext) -> AssetData { + let metadata = self.get_metadata(context).await; + + metadata.into_asset_data() + } + + pub async fn compare_asset_data( + &self, + context: &mut ProgramTestContext, + asset_data: &AssetData, + ) { + let on_chain_asset_data = self.get_asset_data(context).await; + + assert_eq!(on_chain_asset_data, *asset_data); + } + + pub async fn get_token_delegate_role( + &self, + context: &mut ProgramTestContext, + token: &Pubkey, + ) -> Option { + let (delegate_record_pubkey, _) = find_token_record_account(&self.mint.pubkey(), token); + let delegate_record_account = context + .banks_client + .get_account(delegate_record_pubkey) + .await + .unwrap(); + + if let Some(account) = delegate_record_account { + let delegate_record = TokenRecord::safe_deserialize(&account.data).unwrap(); + delegate_record.delegate_role + } else { + None + } + } + + pub async fn is_pnft(&self, context: &mut ProgramTestContext) -> bool { + let md = self.get_metadata(context).await; + if let Some(standard) = md.token_standard { + if standard == TokenStandard::ProgrammableNonFungible { + return true; + } + } + + false + } +} + +pub struct TransferFromParams<'a> { + pub context: &'a mut ProgramTestContext, + pub authority: &'a Keypair, + pub source_owner: &'a Pubkey, + pub destination_owner: Pubkey, + pub destination_token: Option, + pub payer: &'a Keypair, + pub authorization_rules: Option, + pub args: TransferArgs, +} + +pub struct TransferToParams<'a> { + pub context: &'a mut ProgramTestContext, + pub authority: &'a Keypair, + pub source_owner: &'a Pubkey, + pub source_token: &'a Pubkey, + pub destination_owner: Pubkey, + pub destination_token: Option, + pub payer: &'a Keypair, + pub authorization_rules: Option, + pub args: TransferArgs, +} diff --git a/token-metadata/program/tests/utils/edition_marker.rs b/token-metadata/program/tests/utils/edition_marker.rs index 44abfffbe8..8f353ec19c 100644 --- a/token-metadata/program/tests/utils/edition_marker.rs +++ b/token-metadata/program/tests/utils/edition_marker.rs @@ -211,6 +211,7 @@ impl EditionMarker { &context.payer.pubkey(), new_owner, &self.mint.pubkey(), + &spl_token::ID, ); let transfer_ix = spl_token::instruction::transfer( diff --git a/token-metadata/program/tests/utils/external_price.rs b/token-metadata/program/tests/utils/external_price.rs deleted file mode 100644 index 4ccd31665c..0000000000 --- a/token-metadata/program/tests/utils/external_price.rs +++ /dev/null @@ -1,88 +0,0 @@ -use mpl_token_vault::instruction; -use solana_program::{borsh::try_from_slice_unchecked, system_instruction}; -use solana_sdk::{ - pubkey::Pubkey, signature::Signer, signer::keypair::Keypair, transaction::Transaction, -}; - -use crate::*; - -#[derive(Debug)] -pub struct ExternalPrice { - pub keypair: Keypair, - pub price_mint: Keypair, -} - -impl ExternalPrice { - pub fn new() -> Self { - ExternalPrice { - keypair: Keypair::new(), - price_mint: Keypair::new(), - } - } - - pub async fn get_data( - &self, - context: &mut ProgramTestContext, - ) -> mpl_token_vault::state::ExternalPriceAccount { - let account = get_account(context, &self.keypair.pubkey()).await; - try_from_slice_unchecked(&account.data).unwrap() - } - - pub async fn update( - &self, - context: &mut ProgramTestContext, - price_per_share: u64, - price_mint: &Pubkey, - allowed_to_combine: bool, - ) -> Result<(), BanksClientError> { - let tx = Transaction::new_signed_with_payer( - &[ - instruction::create_update_external_price_account_instruction( - mpl_token_vault::id(), - self.keypair.pubkey(), - price_per_share, - *price_mint, - allowed_to_combine, - ), - ], - Some(&context.payer.pubkey()), - &[&context.payer, &self.keypair], - context.last_blockhash, - ); - - context.banks_client.process_transaction(tx).await - } - - pub async fn create(&self, context: &mut ProgramTestContext) -> Result<(), BanksClientError> { - create_mint( - context, - &self.price_mint, - &context.payer.pubkey(), - Some(&context.payer.pubkey()), - 0, - ) - .await?; - - let rent = context.banks_client.get_rent().await.unwrap(); - let tx = Transaction::new_signed_with_payer( - &[system_instruction::create_account( - &context.payer.pubkey(), - &self.keypair.pubkey(), - rent.minimum_balance(mpl_token_vault::state::MAX_EXTERNAL_ACCOUNT_SIZE), - mpl_token_vault::state::MAX_EXTERNAL_ACCOUNT_SIZE as u64, - &mpl_token_vault::id(), - )], - Some(&context.payer.pubkey()), - &[&context.payer, &self.keypair], - context.last_blockhash, - ); - - context.banks_client.process_transaction(tx).await - } -} - -impl Default for ExternalPrice { - fn default() -> Self { - Self::new() - } -} diff --git a/token-metadata/program/tests/utils/metadata.rs b/token-metadata/program/tests/utils/metadata.rs index 315894fbe5..dbd083e15b 100644 --- a/token-metadata/program/tests/utils/metadata.rs +++ b/token-metadata/program/tests/utils/metadata.rs @@ -1,6 +1,9 @@ use mpl_token_metadata::{ id, instruction, - state::{Collection, CollectionDetails, Creator, Data, DataV2, Uses, PREFIX}, + state::{ + Collection, CollectionDetails, Creator, Data, DataV2, Metadata as TmMetadata, + TokenMetadataAccount, TokenStandard, Uses, PREFIX, + }, }; use solana_program::borsh::try_from_slice_unchecked; use solana_sdk::{ @@ -32,6 +35,22 @@ impl Metadata { } } + pub async fn into_digital_asset(self, context: &mut ProgramTestContext) -> DigitalAsset { + let token_record = if self.is_pnft(context).await { + Some(self.token.pubkey()) + } else { + None + }; + + DigitalAsset { + metadata: self.pubkey, + mint: self.mint, + token: Some(self.token.pubkey()), + master_edition: None, + token_record, + } + } + pub async fn get_data( &self, context: &mut ProgramTestContext, @@ -40,6 +59,28 @@ impl Metadata { try_from_slice_unchecked(&account.data).unwrap() } + pub async fn is_pnft(&self, context: &mut ProgramTestContext) -> bool { + let md = self.get_metadata(context).await; + if let Some(standard) = md.token_standard { + if standard == TokenStandard::ProgrammableNonFungible { + return true; + } + } + + false + } + + pub async fn get_metadata(&self, context: &mut ProgramTestContext) -> TmMetadata { + let metadata_account = context + .banks_client + .get_account(self.pubkey) + .await + .unwrap() + .unwrap(); + + TmMetadata::safe_deserialize(&metadata_account.data).unwrap() + } + #[allow(deprecated)] pub async fn create( &self, diff --git a/token-metadata/program/tests/utils/mod.rs b/token-metadata/program/tests/utils/mod.rs index 33bca100c4..05d6907e68 100644 --- a/token-metadata/program/tests/utils/mod.rs +++ b/token-metadata/program/tests/utils/mod.rs @@ -1,24 +1,26 @@ mod assert; +mod digital_asset; mod edition_marker; -mod external_price; mod master_edition_v2; mod metadata; -mod vault; +mod programmable; +mod rooster_manager; pub use assert::*; +pub use digital_asset::*; pub use edition_marker::EditionMarker; -pub use external_price::ExternalPrice; pub use master_edition_v2::MasterEditionV2; pub use metadata::{assert_collection_size, Metadata}; pub use mpl_token_metadata::instruction; use mpl_token_metadata::state::CollectionDetails; +pub use programmable::create_default_metaplex_rule_set; +pub use rooster_manager::*; use solana_program_test::*; use solana_sdk::{ account::Account, program_pack::Pack, pubkey::Pubkey, signature::Signer, signer::keypair::Keypair, system_instruction, transaction::Transaction, }; use spl_token::state::Mint; -pub use vault::Vault; pub const DEFAULT_COLLECTION_DETAILS: Option = Some(CollectionDetails::V1 { size: 0 }); @@ -220,3 +222,13 @@ pub async fn create_mint( context.banks_client.process_transaction(tx).await } + +pub trait DirtyClone { + fn dirty_clone(&self) -> Self; +} + +impl DirtyClone for Keypair { + fn dirty_clone(&self) -> Self { + Keypair::from_bytes(&self.to_bytes()).unwrap() + } +} diff --git a/token-metadata/program/tests/utils/programmable.rs b/token-metadata/program/tests/utils/programmable.rs new file mode 100644 index 0000000000..1f3dc3dba9 --- /dev/null +++ b/token-metadata/program/tests/utils/programmable.rs @@ -0,0 +1,175 @@ +use mpl_token_auth_rules::{ + instruction::{builders::CreateOrUpdateBuilder, CreateOrUpdateArgs, InstructionBuilder}, + payload::Payload, + state::{CompareOp, Rule, RuleSetV1}, +}; +use mpl_token_metadata::{ + processor::{AuthorizationData, TransferScenario}, + state::{Operation, PayloadKey}, +}; +use rmp_serde::Serializer; +use serde::Serialize; +use solana_program::system_program; +use solana_sdk::{ + pubkey::Pubkey, + signature::{Keypair, Signer}, + transaction::Transaction, +}; + +use crate::*; + +static PROGRAM_ALLOW_LIST: [Pubkey; 2] = [mpl_token_auth_rules::ID, rooster::ID]; + +macro_rules! get_primitive_rules { + ( + $source_owned_by_sys_program:ident, + $dest_program_allow_list:ident, + $dest_pda_match:ident, + $source_program_allow_list:ident, + $source_pda_match:ident, + $dest_owned_by_sys_program:ident, + $nft_amount:ident, + ) => { + let $source_owned_by_sys_program = Rule::ProgramOwned { + program: system_program::ID, + field: PayloadKey::Source.to_string(), + }; + + let $dest_program_allow_list = Rule::ProgramOwnedList { + programs: PROGRAM_ALLOW_LIST.to_vec(), + field: PayloadKey::Destination.to_string(), + }; + + let $dest_pda_match = Rule::PDAMatch { + program: None, + pda_field: PayloadKey::Destination.to_string(), + seeds_field: PayloadKey::DestinationSeeds.to_string(), + }; + + let $source_program_allow_list = Rule::ProgramOwnedList { + programs: PROGRAM_ALLOW_LIST.to_vec(), + field: PayloadKey::Source.to_string(), + }; + + let $source_pda_match = Rule::PDAMatch { + program: None, + pda_field: PayloadKey::Source.to_string(), + seeds_field: PayloadKey::SourceSeeds.to_string(), + }; + + let $dest_owned_by_sys_program = Rule::ProgramOwned { + program: system_program::ID, + field: PayloadKey::Destination.to_string(), + }; + let $nft_amount = Rule::Amount { + field: PayloadKey::Amount.to_string(), + amount: 1, + operator: CompareOp::Eq, + }; + }; +} + +pub async fn create_default_metaplex_rule_set( + context: &mut ProgramTestContext, + creator: Keypair, +) -> (Pubkey, AuthorizationData) { + let name = String::from("Metaplex Royalty Enforcement"); + let (ruleset_addr, _ruleset_bump) = + mpl_token_auth_rules::pda::find_rule_set_address(creator.pubkey(), name.clone()); + + get_primitive_rules!( + source_owned_by_sys_program, + dest_program_allow_list, + dest_pda_match, + source_program_allow_list, + source_pda_match, + dest_owned_by_sys_program, + nft_amount, + ); + + // (source is owned by system program && dest is on allow list && destination is a PDA) || + // (source is on allow list && source is a PDA && dest is owned by system program) + let transfer_rule = Rule::Any { + rules: vec![ + Rule::All { + rules: vec![ + source_owned_by_sys_program, + dest_program_allow_list, + dest_pda_match, + nft_amount.clone(), + ], + }, + Rule::All { + rules: vec![ + source_program_allow_list, + source_pda_match, + dest_owned_by_sys_program, + nft_amount, + ], + }, + ], + }; + + let owner_operation = Operation::Transfer { + scenario: TransferScenario::Holder, + }; + + let transfer_delegate_operation = Operation::Transfer { + scenario: TransferScenario::TransferDelegate, + }; + + let sale_delegate_operation = Operation::Transfer { + scenario: TransferScenario::SaleDelegate, + }; + + let mut royalty_rule_set = RuleSetV1::new(name, creator.pubkey()); + royalty_rule_set + .add(owner_operation.to_string(), transfer_rule.clone()) + .unwrap(); + royalty_rule_set + .add( + transfer_delegate_operation.to_string(), + transfer_rule.clone(), + ) + .unwrap(); + royalty_rule_set + .add(sale_delegate_operation.to_string(), transfer_rule.clone()) + .unwrap(); + + // Serialize the RuleSet using RMP serde. + let mut serialized_data = Vec::new(); + royalty_rule_set + .serialize(&mut Serializer::new(&mut serialized_data)) + .unwrap(); + + // Create a `create` instruction. + let create_ix = CreateOrUpdateBuilder::new() + .rule_set_pda(ruleset_addr) + .payer(creator.pubkey()) + .build(CreateOrUpdateArgs::V1 { + serialized_rule_set: serialized_data, + }) + .unwrap() + .instruction(); + + // Add it to a transaction. + let create_tx = Transaction::new_signed_with_payer( + &[create_ix], + Some(&creator.pubkey()), + &[&creator], + context.last_blockhash, + ); + + // Process the transaction. + context + .banks_client + .process_transaction(create_tx) + .await + .expect("creation should succeed"); + + // Client can add additional rules to the Payload but does not need to in this case. + let payload = Payload::new(); + let auth_data = AuthorizationData { payload }; + + (ruleset_addr, auth_data) +} diff --git a/token-metadata/program/tests/utils/rooster_manager.rs b/token-metadata/program/tests/utils/rooster_manager.rs new file mode 100644 index 0000000000..25c54755f9 --- /dev/null +++ b/token-metadata/program/tests/utils/rooster_manager.rs @@ -0,0 +1,130 @@ +use mpl_token_auth_rules::payload::Payload; +use rooster::{ + instruction::{delegate as rooster_delegate, init, withdraw, WithdrawArgs}, + pda::find_rooster_pda, + AuthorizationData, +}; +use solana_sdk::compute_budget::ComputeBudgetInstruction; +use spl_associated_token_account::get_associated_token_address; + +use super::*; + +pub struct RoosterManager { + pda: Pubkey, + authority: Keypair, + bump: u8, +} + +impl RoosterManager { + pub async fn init( + context: &mut ProgramTestContext, + authority: Keypair, + ) -> Result { + let (pda, bump) = find_rooster_pda(&authority.pubkey()); + + let ix = init(authority.pubkey(), pda); + + let tx = Transaction::new_signed_with_payer( + &[ix], + Some(&authority.pubkey()), + &[&authority], + context.last_blockhash, + ); + + context.banks_client.process_transaction(tx).await.unwrap(); + + Ok(RoosterManager { + pda, + authority, + bump, + }) + } + + pub async fn withdraw( + self, + context: &mut ProgramTestContext, + authority: &Keypair, + destination_owner: Pubkey, + mint: Pubkey, + metadata: Pubkey, + edition: Pubkey, + rule_set: Pubkey, + payload: Payload, + ) -> Result<(), BanksClientError> { + let args = WithdrawArgs { + auth_data: AuthorizationData::new(payload), + }; + + let token = get_associated_token_address(&self.pda(), &mint); + let destination = get_associated_token_address(&destination_owner, &mint); + + let compute_ix = ComputeBudgetInstruction::set_compute_unit_limit(800_000); + + let ix = withdraw( + authority.pubkey(), + self.pda, + token, + destination_owner, + destination, + mint, + metadata, + edition, + rule_set, + args, + ); + + let tx = Transaction::new_signed_with_payer( + &[compute_ix, ix], + Some(&authority.pubkey()), + &[authority], + context.last_blockhash, + ); + + context.banks_client.process_transaction(tx).await + } + + pub async fn delegate( + &self, + context: &mut ProgramTestContext, + delegate: &Keypair, + mint: Pubkey, + metadata: Pubkey, + edition: Pubkey, + authorization_rules: Option, + args: rooster::instruction::DelegateArgs, + ) -> Result<(), BanksClientError> { + let token = get_associated_token_address(&self.pda(), &mint); + + let ix = rooster_delegate( + delegate.pubkey(), + self.pda, + token, + mint, + metadata, + edition, + authorization_rules, + args, + ); + + let tx = Transaction::new_signed_with_payer( + &[ix], + Some(&delegate.pubkey()), + &[delegate], + context.last_blockhash, + ); + + context.banks_client.process_transaction(tx).await + } + + pub fn authority(&self) -> Pubkey { + self.authority.pubkey() + } + + pub fn pda(&self) -> Pubkey { + self.pda + } + + pub fn bump(&self) -> u8 { + self.bump + } +} diff --git a/token-metadata/program/tests/utils/vault.rs b/token-metadata/program/tests/utils/vault.rs deleted file mode 100644 index c4d6e9d77e..0000000000 --- a/token-metadata/program/tests/utils/vault.rs +++ /dev/null @@ -1,229 +0,0 @@ -use mpl_token_vault::{instruction, state::PREFIX}; -use solana_program::{pubkey::Pubkey, system_instruction}; -use solana_program_test::*; -use solana_sdk::{ - signature::{Keypair, Signer}, - transaction::Transaction, -}; - -use super::{create_mint, create_token_account, ExternalPrice, Metadata}; - -pub struct Vault { - pub keypair: Keypair, - pub mint: Keypair, - pub redeem_treasury: Keypair, - pub fraction_treasury: Keypair, -} - -impl Vault { - pub fn new() -> Self { - Vault { - keypair: Keypair::new(), - mint: Keypair::new(), - redeem_treasury: Keypair::new(), - fraction_treasury: Keypair::new(), - } - } - - pub async fn add_token_to_inactive_vault( - &self, - context: &mut ProgramTestContext, - amount: u64, - metadata: &Metadata, - ) -> Result<(Pubkey, Pubkey), BanksClientError> { - let vault_pubkey = self.keypair.pubkey(); - let metaplex_token_vault_id = mpl_token_vault::id(); - - let store = Keypair::new(); - let token_mint_pubkey = metadata.mint.pubkey(); - - let seeds = &[ - PREFIX.as_bytes(), - vault_pubkey.as_ref(), - token_mint_pubkey.as_ref(), - ]; - let (safety_deposit_box, _) = Pubkey::find_program_address(seeds, &metaplex_token_vault_id); - let seeds = &[ - PREFIX.as_bytes(), - metaplex_token_vault_id.as_ref(), - vault_pubkey.as_ref(), - ]; - let (authority, _) = Pubkey::find_program_address(seeds, &metaplex_token_vault_id); - create_token_account(context, &store, &token_mint_pubkey, &authority).await?; - - let tx = Transaction::new_signed_with_payer( - &[instruction::create_add_token_to_inactive_vault_instruction( - mpl_token_vault::id(), - safety_deposit_box, - metadata.token.pubkey(), - store.pubkey(), - self.keypair.pubkey(), - context.payer.pubkey(), - context.payer.pubkey(), - context.payer.pubkey(), - amount, - )], - Some(&context.payer.pubkey()), - &[&context.payer, &context.payer, &context.payer], - context.last_blockhash, - ); - context.banks_client.process_transaction(tx).await?; - - Ok((safety_deposit_box, store.pubkey())) - } - - pub async fn activate( - &self, - context: &mut ProgramTestContext, - number_of_shares: u64, - ) -> Result<(), BanksClientError> { - let metaplex_token_vault_id = mpl_token_vault::id(); - let vault_pubkey = self.keypair.pubkey(); - - let seeds = &[ - PREFIX.as_bytes(), - metaplex_token_vault_id.as_ref(), - vault_pubkey.as_ref(), - ]; - let (authority, _) = Pubkey::find_program_address(seeds, &metaplex_token_vault_id); - - let tx = Transaction::new_signed_with_payer( - &[instruction::create_activate_vault_instruction( - mpl_token_vault::id(), - self.keypair.pubkey(), - self.mint.pubkey(), - self.fraction_treasury.pubkey(), - authority, - context.payer.pubkey(), - number_of_shares, - )], - Some(&context.payer.pubkey()), - &[&context.payer], - context.last_blockhash, - ); - - context.banks_client.process_transaction(tx).await - } - - pub async fn combine( - &self, - context: &mut ProgramTestContext, - external_price: &ExternalPrice, - ) -> Result<(), BanksClientError> { - let outstanding_token_account = Keypair::new(); - let paying_token_account = Keypair::new(); - - let metaplex_token_vault_id = mpl_token_vault::id(); - let vault_pubkey = self.keypair.pubkey(); - - create_token_account( - context, - &outstanding_token_account, - &self.mint.pubkey(), - &context.payer.pubkey(), - ) - .await?; - create_token_account( - context, - &paying_token_account, - &external_price.price_mint.pubkey(), - &context.payer.pubkey(), - ) - .await?; - - let seeds = &[ - PREFIX.as_bytes(), - metaplex_token_vault_id.as_ref(), - vault_pubkey.as_ref(), - ]; - let (authority, _) = Pubkey::find_program_address(seeds, &metaplex_token_vault_id); - - let tx = Transaction::new_signed_with_payer( - &[instruction::create_combine_vault_instruction( - mpl_token_vault::id(), - self.keypair.pubkey(), - outstanding_token_account.pubkey(), - paying_token_account.pubkey(), - self.mint.pubkey(), - self.fraction_treasury.pubkey(), - self.redeem_treasury.pubkey(), - context.payer.pubkey(), - context.payer.pubkey(), - context.payer.pubkey(), - authority, - external_price.keypair.pubkey(), - )], - Some(&context.payer.pubkey()), - &[&context.payer, &context.payer, &context.payer], - context.last_blockhash, - ); - - context.banks_client.process_transaction(tx).await - } - - pub async fn create( - &self, - context: &mut ProgramTestContext, - external_price: &ExternalPrice, - ) -> Result<(), BanksClientError> { - let metaplex_token_vault_id = mpl_token_vault::id(); - let vault_pubkey = self.keypair.pubkey(); - - let seeds = &[ - PREFIX.as_bytes(), - metaplex_token_vault_id.as_ref(), - vault_pubkey.as_ref(), - ]; - let (authority, _) = Pubkey::find_program_address(seeds, &metaplex_token_vault_id); - - create_mint(context, &self.mint, &authority, Some(&authority), 0).await?; - create_token_account( - context, - &self.redeem_treasury, - &external_price.price_mint.pubkey(), - &authority, - ) - .await?; - create_token_account( - context, - &self.fraction_treasury, - &self.mint.pubkey(), - &authority, - ) - .await?; - - let rent = context.banks_client.get_rent().await.unwrap(); - let tx = Transaction::new_signed_with_payer( - &[ - system_instruction::create_account( - &context.payer.pubkey(), - &self.keypair.pubkey(), - rent.minimum_balance(mpl_token_vault::state::MAX_VAULT_SIZE), - mpl_token_vault::state::MAX_VAULT_SIZE as u64, - &mpl_token_vault::id(), - ), - instruction::create_init_vault_instruction( - mpl_token_vault::id(), - self.mint.pubkey(), - self.redeem_treasury.pubkey(), - self.fraction_treasury.pubkey(), - self.keypair.pubkey(), - context.payer.pubkey(), - external_price.keypair.pubkey(), - false, - ), - ], - Some(&context.payer.pubkey()), - &[&context.payer, &context.payer, &self.keypair], - context.last_blockhash, - ); - - context.banks_client.process_transaction(tx).await - } -} - -impl Default for Vault { - fn default() -> Self { - Self::new() - } -} diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000000..396a56f4b4 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,1967 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@babel/runtime@^7.12.5", "@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: + regenerator-runtime "^0.13.11" + +"@eslint/eslintrc@^1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95" + integrity sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.4.0" + globals "^13.15.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@hapi/hoek@^9.0.0": + version "9.3.0" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb" + integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ== + +"@hapi/topo@^5.0.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012" + integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@humanwhocodes/config-array@^0.11.6": + version "0.11.7" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.7.tgz#38aec044c6c828f6ed51d5d7ae3d9b9faf6dbb0f" + integrity sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw== + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + +"@metaplex-foundation/amman-client@^0.2.0": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/amman-client/-/amman-client-0.2.4.tgz#bcf9cb2b8fa0cdaec3b17a68e638c8cf0531d08a" + integrity sha512-AB2zKDrGyJtQqUmOswsZCflIuldYmsPvL/zIU/66JOD9HDo8yyz5ITCrs+/0QoW+CIJkotC5JEhuYuBnqLhmNw== + dependencies: + "@metaplex-foundation/cusper" "^0.0.2" + "@solana/spl-token-registry" "^0.2.2405" + "@solana/web3.js" "^1.66.2" + bn.js "^5.2.1" + debug "^4.3.3" + js-sha3 "^0.8.0" + socket.io-client "^4.4.1" + tweetnacl "^1.0.3" + +"@metaplex-foundation/amman@^0.12.0": + version "0.12.1" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/amman/-/amman-0.12.1.tgz#8d135fa4e4ac4667133762eca02e6cc221e7f656" + integrity sha512-F3cdHr11ByLGMCrSBRvRCf5uIlhLE+5sWaHT2ZzcE6zVyDta3gs/A12ZBzYZS8ugNETNpJySfB42kMp1VZwbUA== + dependencies: + "@metaplex-foundation/amman-client" "^0.2.0" + "@solana/spl-token" "^0.2.0" + "@solana/web3.js" "^1.63.1" + ansi-colors "^4.1.1" + bn.js "^5.2.0" + buffer-hexdump "^1.0.0" + date-fns "^2.28.0" + debug "^4.3.3" + deep-diff "^1.0.2" + diff "^5.1.0" + numeral "^2.0.6" + port-pid "^0.0.7" + socket.io "^4.4.1" + text-table "^0.2.0" + timeago.js "^4.0.2" + ts-essentials "^9.3.0" + wait-on "^6.0.0" + yargs "^17.3.0" + +"@metaplex-foundation/cusper@^0.0.2": + version "0.0.2" + resolved "https://registry.yarnpkg.com/@metaplex-foundation/cusper/-/cusper-0.0.2.tgz#dc2032a452d6c269e25f016aa4dd63600e2af975" + integrity sha512-S9RulC2fFCFOQraz61bij+5YCHhSO9llJegK8c8Y6731fSi6snUSQJdCUqYS8AIgR0TKbQvdvgSyIIdbDFZbBA== + +"@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== + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@project-serum/anchor@^0.19.0": + version "0.19.0" + resolved "https://registry.yarnpkg.com/@project-serum/anchor/-/anchor-0.19.0.tgz#79f1fbe7c3134860ccbfe458a0e09daf79644885" + integrity sha512-cs0LBmJOrL9eJ8MRNqitnzbpCT5QEzVdJmiIjfNV5YaGn1K9vISR7DtISj3Bdl3KBdLqii4CTw1mpHdi8iXUCg== + dependencies: + "@project-serum/borsh" "^0.2.2" + "@solana/web3.js" "^1.17.0" + base64-js "^1.5.1" + bn.js "^5.1.2" + bs58 "^4.0.1" + buffer-layout "^1.2.2" + camelcase "^5.3.1" + crypto-hash "^1.3.0" + eventemitter3 "^4.0.7" + find "^0.3.0" + js-sha256 "^0.9.0" + pako "^2.0.3" + snake-case "^3.0.4" + toml "^3.0.0" + +"@project-serum/borsh@^0.2.2": + version "0.2.5" + resolved "https://registry.yarnpkg.com/@project-serum/borsh/-/borsh-0.2.5.tgz#6059287aa624ecebbfc0edd35e4c28ff987d8663" + integrity sha512-UmeUkUoKdQ7rhx6Leve1SssMR/Ghv8qrEiyywyxSWg7ooV7StdpPBhciiy5eB3T0qU1BXvdRNC8TdrkxK7WC5Q== + dependencies: + bn.js "^5.1.2" + buffer-layout "^1.2.0" + +"@sideway/address@^4.1.3": + version "4.1.4" + resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.4.tgz#03dccebc6ea47fdc226f7d3d1ad512955d4783f0" + integrity sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw== + dependencies: + "@hapi/hoek" "^9.0.0" + +"@sideway/formula@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.0.tgz#fe158aee32e6bd5de85044be615bc08478a0a13c" + integrity sha512-vHe7wZ4NOXVfkoRb8T5otiENVlT7a3IAiw7H5M2+GO+9CDgcVUUsX1zalAztCmwyOr2RUTGJdgB+ZvSVqmdHmg== + +"@sideway/pinpoint@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df" + integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ== + +"@socket.io/component-emitter@~3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" + integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== + +"@solana/buffer-layout-utils@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@solana/buffer-layout-utils/-/buffer-layout-utils-0.2.0.tgz#b45a6cab3293a2eb7597cceb474f229889d875ca" + integrity sha512-szG4sxgJGktbuZYDg2FfNmkMi0DYQoVjN2h7ta1W1hPrwzarcFLBq9UpX1UjNXsNpT9dn+chgprtWGioUAr4/g== + dependencies: + "@solana/buffer-layout" "^4.0.0" + "@solana/web3.js" "^1.32.0" + bigint-buffer "^1.1.5" + bignumber.js "^9.0.1" + +"@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/spl-token-registry@^0.2.2405": + version "0.2.4574" + resolved "https://registry.yarnpkg.com/@solana/spl-token-registry/-/spl-token-registry-0.2.4574.tgz#13f4636b7bec90d2bb43bbbb83512cd90d2ce257" + integrity sha512-JzlfZmke8Rxug20VT/VpI2XsXlsqMlcORIUivF+Yucj7tFi7A0dXG7h+2UnD0WaZJw8BrUz2ABNkUnv89vbv1A== + dependencies: + cross-fetch "3.0.6" + +"@solana/spl-token@^0.2.0": + version "0.2.0" + resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.2.0.tgz#329bb6babb5de0f9c40035ddb1657f01a8347acd" + integrity sha512-RWcn31OXtdqIxmkzQfB2R+WpsJOVS6rKuvpxJFjvik2LyODd+WN58ZP3Rpjpro03fscGAkzlFuP3r42doRJgyQ== + dependencies: + "@solana/buffer-layout" "^4.0.0" + "@solana/buffer-layout-utils" "^0.2.0" + "@solana/web3.js" "^1.32.0" + start-server-and-test "^1.14.0" + +"@solana/web3.js@^1.17.0", "@solana/web3.js@^1.32.0", "@solana/web3.js@^1.63.1", "@solana/web3.js@^1.66.2": + 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" + "@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.7.0" + bs58 "^4.0.1" + buffer "6.0.1" + fast-stable-stringify "^1.0.0" + jayson "^3.4.4" + node-fetch "2" + rpc-websockets "^7.5.0" + superstruct "^0.14.2" + +"@types/connect@^3.4.33": + version "3.4.35" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" + integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== + dependencies: + "@types/node" "*" + +"@types/cookie@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" + integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== + +"@types/cors@^2.8.12": + version "2.8.13" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.13.tgz#b8ade22ba455a1b8cb3b5d3f35910fd204f84f94" + integrity sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA== + dependencies: + "@types/node" "*" + +"@types/json-schema@^7.0.9": + version "7.0.11" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" + integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== + +"@types/node@*", "@types/node@>=10.0.0": + version "18.11.12" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.12.tgz#89e7f8aa8c88abf432f9bd594888144d7dba10aa" + integrity sha512-FgD3NtTAKvyMmD44T07zz2fEf+OKwutgBCEVM8GcvMGVGaDktiLNTDvPwC/LUe3PinMW+X6CuLOF2Ui1mAlSXg== + +"@types/node@^12.12.54": + version "12.20.55" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" + integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== + +"@types/semver@^7.3.12": + version "7.3.13" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.13.tgz#da4bfd73f49bd541d28920ab0e2bf0ee80f71c91" + integrity sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw== + +"@types/ws@^7.4.4": + version "7.4.7" + resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" + integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww== + dependencies: + "@types/node" "*" + +"@typescript-eslint/eslint-plugin@^5.4.0": + version "5.46.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.46.0.tgz#9a96a713b9616c783501a3c1774c9e2b40217ad0" + integrity sha512-QrZqaIOzJAjv0sfjY4EjbXUi3ZOFpKfzntx22gPGr9pmFcTjcFw/1sS1LJhEubfAGwuLjNrPV0rH+D1/XZFy7Q== + dependencies: + "@typescript-eslint/scope-manager" "5.46.0" + "@typescript-eslint/type-utils" "5.46.0" + "@typescript-eslint/utils" "5.46.0" + debug "^4.3.4" + ignore "^5.2.0" + natural-compare-lite "^1.4.0" + regexpp "^3.2.0" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/parser@^5.4.0": + version "5.46.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.46.0.tgz#002d8e67122947922a62547acfed3347cbf2c0b6" + integrity sha512-joNO6zMGUZg+C73vwrKXCd8usnsmOYmgW/w5ZW0pG0RGvqeznjtGDk61EqqTpNrFLUYBW2RSBFrxdAZMqA4OZA== + dependencies: + "@typescript-eslint/scope-manager" "5.46.0" + "@typescript-eslint/types" "5.46.0" + "@typescript-eslint/typescript-estree" "5.46.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@5.46.0": + version "5.46.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.46.0.tgz#60790b14d0c687dd633b22b8121374764f76ce0d" + integrity sha512-7wWBq9d/GbPiIM6SqPK9tfynNxVbfpihoY5cSFMer19OYUA3l4powA2uv0AV2eAZV6KoAh6lkzxv4PoxOLh1oA== + dependencies: + "@typescript-eslint/types" "5.46.0" + "@typescript-eslint/visitor-keys" "5.46.0" + +"@typescript-eslint/type-utils@5.46.0": + version "5.46.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.46.0.tgz#3a4507b3b437e2fd9e95c3e5eea5ae16f79d64b3" + integrity sha512-dwv4nimVIAsVS2dTA0MekkWaRnoYNXY26dKz8AN5W3cBFYwYGFQEqm/cG+TOoooKlncJS4RTbFKgcFY/pOiBCg== + dependencies: + "@typescript-eslint/typescript-estree" "5.46.0" + "@typescript-eslint/utils" "5.46.0" + debug "^4.3.4" + tsutils "^3.21.0" + +"@typescript-eslint/types@5.46.0": + version "5.46.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.46.0.tgz#f4d76622a996b88153bbd829ea9ccb9f7a5d28bc" + integrity sha512-wHWgQHFB+qh6bu0IAPAJCdeCdI0wwzZnnWThlmHNY01XJ9Z97oKqKOzWYpR2I83QmshhQJl6LDM9TqMiMwJBTw== + +"@typescript-eslint/typescript-estree@5.46.0": + version "5.46.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.46.0.tgz#a6c2b84b9351f78209a1d1f2d99ca553f7fa29a5" + integrity sha512-kDLNn/tQP+Yp8Ro2dUpyyVV0Ksn2rmpPpB0/3MO874RNmXtypMwSeazjEN/Q6CTp8D7ExXAAekPEcCEB/vtJkw== + dependencies: + "@typescript-eslint/types" "5.46.0" + "@typescript-eslint/visitor-keys" "5.46.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.3.7" + tsutils "^3.21.0" + +"@typescript-eslint/utils@5.46.0": + version "5.46.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.46.0.tgz#600cd873ba471b7d8b0b9f35de34cf852c6fcb31" + integrity sha512-4O+Ps1CRDw+D+R40JYh5GlKLQERXRKW5yIQoNDpmXPJ+C7kaPF9R7GWl+PxGgXjB3PQCqsaaZUpZ9dG4U6DO7g== + dependencies: + "@types/json-schema" "^7.0.9" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.46.0" + "@typescript-eslint/types" "5.46.0" + "@typescript-eslint/typescript-estree" "5.46.0" + eslint-scope "^5.1.1" + eslint-utils "^3.0.0" + semver "^7.3.7" + +"@typescript-eslint/visitor-keys@5.46.0": + version "5.46.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.46.0.tgz#36d87248ae20c61ef72404bcd61f14aa2563915f" + integrity sha512-E13gBoIXmaNhwjipuvQg1ByqSAu/GbEpP/qzFihugJ+MomtoJtFAJG/+2DRPByf57B863m0/q7Zt16V9ohhANw== + dependencies: + "@typescript-eslint/types" "5.46.0" + eslint-visitor-keys "^3.3.0" + +JSONStream@^1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + +accepts@~1.3.4: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.8.0: + version "8.8.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.1.tgz#0a3f9cbecc4ec3bea6f0a80b66ae8dd2da250b73" + integrity sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA== + +ajv@^6.10.0, ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-colors@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +arg@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" + integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +axios@^0.25.0: + version "0.25.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.25.0.tgz#349cfbb31331a9b4453190791760a8d35b093e0a" + integrity sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g== + dependencies: + follow-redirects "^1.14.7" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base-x@^3.0.2: + version "3.0.9" + resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" + integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== + dependencies: + safe-buffer "^5.0.1" + +base64-js@^1.3.1, base64-js@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + +base64id@2.0.0, base64id@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" + integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== + +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" + +bignumber.js@^9.0.1: + version "9.1.1" + resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.1.1.tgz#c4df7dc496bd849d4c9464344c1aa74228b4dac6" + integrity sha512-pHm4LsMJ6lzgNGVfZHjMoO8sdoRhOzOH4MLmY65Jg70bpxCKu5iOHNJyfF6OyvYw7t8Fpf35RuzUyqnQsj8Vig== + +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" + +bluebird@3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" + integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== + +bn.js@^5.0.0, bn.js@^5.1.2, bn.js@^5.2.0, bn.js@^5.2.1: + 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: + bn.js "^5.2.0" + bs58 "^4.0.0" + text-encoding-utf-8 "^1.0.2" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +bs58@^4.0.0, bs58@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" + integrity sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw== + dependencies: + base-x "^3.0.2" + +buffer-hexdump@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-hexdump/-/buffer-hexdump-1.0.0.tgz#33cd809130c754b956c32cc15fcb258002ac77b9" + integrity sha512-Zfe5uJT9Y7J2cm2MVkmhRdkAqlbj0uw9oBaeXgOKUiXd+kdcABkp7+W+hQ8fCW/5TouBClXSzD8K5/XwVckDsg== + +buffer-layout@^1.2.0, buffer-layout@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/buffer-layout/-/buffer-layout-1.2.2.tgz#b9814e7c7235783085f9ca4966a0cfff112259d5" + integrity sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA== + +buffer@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.1.tgz#3cbea8c1463e5a0779e30b66d4c88c6ffa182ac2" + integrity sha512-rVAXBwEcEoYtxnHSO5iWyhzV/O1WMtkUYWlfdLS7FjU4PnSJJHEfHXi/uHPI5EwltmOA794gN3bm3/pzuctWjQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +buffer@~6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + +bufferutil@^4.0.1: + version "4.0.7" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.7.tgz#60c0d19ba2c992dd8273d3f73772ffc894c153ad" + integrity sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw== + dependencies: + node-gyp-build "^4.3.0" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +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== + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +check-more-types@2.24.0: + version "2.24.0" + resolved "https://registry.yarnpkg.com/check-more-types/-/check-more-types-2.24.0.tgz#1420ffb10fd444dcfc79b43891bbfffd32a84600" + integrity sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA== + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +commander@^2.20.3: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +cookie@~0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== + +cors@~2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +cross-fetch@3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.6.tgz#3a4040bc8941e653e0e9cf17f29ebcd177d3365c" + integrity sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ== + dependencies: + node-fetch "2.6.1" + +cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +crypto-hash@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/crypto-hash/-/crypto-hash-1.3.0.tgz#b402cb08f4529e9f4f09346c3e275942f845e247" + integrity sha512-lyAZ0EMyjDkVvz8WOeVnuCPvKVBXcMv1l5SVqO1yC7PzTwrD/pPje/BIRbWhMoPe436U+Y2nD7f5bFx0kt+Sbg== + +date-fns@^2.28.0: + version "2.29.3" + resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8" + integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA== + +debug@4.3.4, debug@^4.1.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +deep-diff@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-1.0.2.tgz#afd3d1f749115be965e89c63edc7abb1506b9c26" + integrity sha512-aWS3UIVH+NPGCD1kki+DCU9Dua032iSsO43LqQpcs4R3+dVv7tX0qBGjiVHJHjplsoUM2XRO/KB92glqc68awg== + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +delay@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" + integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== + +diff@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40" + integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw== + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +duplexer@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" + integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +engine.io-client@~6.2.3: + version "6.2.3" + resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-6.2.3.tgz#a8cbdab003162529db85e9de31575097f6d29458" + integrity sha512-aXPtgF1JS3RuuKcpSrBtimSjYvrbhKW9froICH4s0F3XQWLxsKNxqzG39nnvQZQnva4CMvUK63T7shevxRyYHw== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.1" + engine.io-parser "~5.0.3" + ws "~8.2.3" + xmlhttprequest-ssl "~2.0.0" + +engine.io-parser@~5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.4.tgz#0b13f704fa9271b3ec4f33112410d8f3f41d0fc0" + integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg== + +engine.io@~6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.2.1.tgz#e3f7826ebc4140db9bbaa9021ad6b1efb175878f" + integrity sha512-ECceEFcAaNRybd3lsGQKas3ZlMVjN3cyWwMP25D2i0zWfyiytVbTpRPa34qrr+FHddtpBVOmq4H/DCv1O0lZRA== + dependencies: + "@types/cookie" "^0.4.1" + "@types/cors" "^2.8.12" + "@types/node" ">=10.0.0" + accepts "~1.3.4" + base64id "2.0.0" + cookie "~0.4.1" + cors "~2.8.5" + debug "~4.3.1" + engine.io-parser "~5.0.3" + ws "~8.2.3" + +es6-promise@^4.0.3: + version "4.2.8" + resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" + integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== + +es6-promisify@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" + integrity sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ== + dependencies: + es6-promise "^4.0.3" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-config-prettier@^8.3.0: + version "8.5.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" + integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== + +eslint-plugin-prettier@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" + integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== + dependencies: + prettier-linter-helpers "^1.0.0" + +eslint-scope@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +eslint-scope@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" + integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-utils@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" + integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== + dependencies: + eslint-visitor-keys "^2.0.0" + +eslint-visitor-keys@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" + integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== + +eslint-visitor-keys@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" + integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== + +eslint@^8.3.0: + version "8.29.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.29.0.tgz#d74a88a20fb44d59c51851625bc4ee8d0ec43f87" + integrity sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg== + dependencies: + "@eslint/eslintrc" "^1.3.3" + "@humanwhocodes/config-array" "^0.11.6" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.10.0" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.1.1" + eslint-utils "^3.0.0" + eslint-visitor-keys "^3.3.0" + espree "^9.4.0" + esquery "^1.4.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.15.0" + grapheme-splitter "^1.0.4" + ignore "^5.2.0" + import-fresh "^3.0.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-sdsl "^4.1.4" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.1" + regexpp "^3.2.0" + strip-ansi "^6.0.1" + strip-json-comments "^3.1.0" + text-table "^0.2.0" + +espree@^9.4.0: + version "9.4.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.1.tgz#51d6092615567a2c2cff7833445e37c28c0065bd" + integrity sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg== + dependencies: + acorn "^8.8.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.3.0" + +esquery@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +event-stream@=3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" + integrity sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g== + dependencies: + duplexer "~0.1.1" + from "~0" + map-stream "~0.1.0" + pause-stream "0.0.11" + split "0.3" + stream-combiner "~0.0.4" + through "~2.3.1" + +eventemitter3@^4.0.7: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +execa@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +eyes@^0.1.8: + version "0.1.8" + resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" + integrity sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + +fast-glob@^3.2.9: + version "3.2.12" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" + integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +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== + +fastq@^1.6.0: + version "1.14.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.14.0.tgz#107f69d7295b11e0fccc264e1fc6389f623731ce" + integrity sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg== + dependencies: + reusify "^1.0.4" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +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" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/find/-/find-0.3.0.tgz#4082e8fc8d8320f1a382b5e4f521b9bc50775cb8" + integrity sha512-iSd+O4OEYV/I36Zl8MdYJO0xD82wH528SaCieTVHhclgiYNe9y+yPKSwK+A7/WsmHL1EZ+pYUJBXWTL5qofksw== + dependencies: + traverse-chain "~0.1.0" + +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + dependencies: + flatted "^3.1.0" + rimraf "^3.0.2" + +flatted@^3.1.0: + version "3.2.7" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" + integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== + +follow-redirects@^1.14.7: + version "1.15.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" + integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== + +from@~0: + version "0.1.7" + resolved "https://registry.yarnpkg.com/from/-/from-0.1.7.tgz#83c60afc58b9c56997007ed1a768b3ab303a44fe" + integrity sha512-twe20eF1OxVxp/ML/kq2p1uc6KvFK/+vs8WjEbeKmV2He22MKm7YF2ANIt+EOqhJ5L3K/SuuPhk0hWQDjOM23g== + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +globals@^13.15.0: + version "13.18.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.18.0.tgz#fb224daeeb2bb7d254cd2c640f003528b8d0c1dc" + integrity sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A== + dependencies: + type-fest "^0.20.2" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +grapheme-splitter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" + integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +ieee754@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + +ignore@^5.2.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.1.tgz#c2b1f76cb999ede1502f3a226a9310fdfe88d46c" + integrity sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA== + +import-fresh@^3.0.0, import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isomorphic-ws@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" + integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== + +jayson@^3.4.4: + version "3.7.0" + resolved "https://registry.yarnpkg.com/jayson/-/jayson-3.7.0.tgz#b735b12d06d348639ae8230d7a1e2916cb078f25" + integrity sha512-tfy39KJMrrXJ+mFcMpxwBvFDetS8LAID93+rycFglIQM4kl3uNR3W4lBLE/FFhsoUCEox5Dt2adVpDm/XtebbQ== + dependencies: + "@types/connect" "^3.4.33" + "@types/node" "^12.12.54" + "@types/ws" "^7.4.4" + JSONStream "^1.3.5" + commander "^2.20.3" + delay "^5.0.0" + es6-promisify "^5.0.0" + eyes "^0.1.8" + isomorphic-ws "^4.0.1" + json-stringify-safe "^5.0.1" + lodash "^4.17.20" + uuid "^8.3.2" + ws "^7.4.5" + +joi@^17.6.0: + version "17.7.0" + resolved "https://registry.yarnpkg.com/joi/-/joi-17.7.0.tgz#591a33b1fe1aca2bc27f290bcad9b9c1c570a6b3" + integrity sha512-1/ugc8djfn93rTE3WRKdCzGGt/EtiYKxITMO4Wiv6q5JL1gl9ePt4kBsl1S499nbosspfctIQTpYIhSmHA3WAg== + dependencies: + "@hapi/hoek" "^9.0.0" + "@hapi/topo" "^5.0.0" + "@sideway/address" "^4.1.3" + "@sideway/formula" "^3.0.0" + "@sideway/pinpoint" "^2.0.0" + +js-sdsl@^4.1.4: + version "4.2.0" + resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.2.0.tgz#278e98b7bea589b8baaf048c20aeb19eb7ad09d0" + integrity sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ== + +js-sha256@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" + integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== + +js-sha3@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" + integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json-stringify-safe@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA== + +jsonparse@^1.2.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" + integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== + +lazy-ass@1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" + integrity sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw== + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash@^4.17.20, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +map-stream@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/map-stream/-/map-stream-0.1.0.tgz#e56aa94c4c8055a16404a0674b78f215f7c8e194" + integrity sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.5: + version "1.2.7" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" + integrity sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +natural-compare-lite@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz#17b09581988979fddafe0201e931ba933c96cbb4" + integrity sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +netstats@0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/netstats/-/netstats-0.0.6.tgz#57072d42a6c420a931d1db9c4ca3379717305069" + integrity sha512-j5sdwaoLX/0x74+8bFdnoVEo3RUQexm5Lw615MVrgx7/FSQx88dZj+t5whwrDSrlsiHMYhKpn52p/6oMYHTZ2A== + +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + +node-fetch@2: + 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-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-gyp-build@^4.3.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" + integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +numeral@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/numeral/-/numeral-2.0.6.tgz#4ad080936d443c2561aed9f2197efffe25f4e506" + integrity sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA== + +object-assign@^4: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +optionator@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" + integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.3" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +pako@^2.0.3: + version "2.1.0" + resolved "https://registry.yarnpkg.com/pako/-/pako-2.1.0.tgz#266cc37f98c7d883545d11335c00fbd4062c9a86" + integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +pause-stream@0.0.11: + version "0.0.11" + resolved "https://registry.yarnpkg.com/pause-stream/-/pause-stream-0.0.11.tgz#fe5a34b0cbce12b5aa6a2b403ee2e73b602f1445" + integrity sha512-e3FBlXLmN/D1S+zHzanP4E/4Z60oFAa3O051qt1pxa7DEJWKAyil6upYVXCWadEnuoqa4Pkc9oUx9zsxYeRv8A== + dependencies: + through "~2.3" + +picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +port-pid@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/port-pid/-/port-pid-0.0.7.tgz#382cb46632e2efbda230765882aaa0029edf8144" + integrity sha512-2QkdCeuGLfu1jDxiiwOnafCgVFsMQXMEWvUVx9SMNq+4k6HDKiyA9YVRhmoMvJGg5ZhqEjblpj+1wdWMqn7scw== + dependencies: + netstats "0.0.6" + selective-whitespace "^1.0.0" + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +prettier@^2.7.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.1.tgz#4e1fd11c34e2421bc1da9aea9bd8127cd0a35efc" + integrity sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg== + +ps-tree@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ps-tree/-/ps-tree-1.2.0.tgz#5e7425b89508736cdd4f2224d028f7bb3f722ebd" + integrity sha512-0VnamPPYHl4uaU/nSFeZZpR21QAWRz+sRv4iW9+v/GS/J5U5iZB5BNN6J0RMoOvdx2gWM2+ZFMIm58q24e4UYA== + dependencies: + event-stream "=3.3.4" + +punycode@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" + integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +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== + +regexpp@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" + integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +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.17.2" + eventemitter3 "^4.0.7" + uuid "^8.3.2" + ws "^8.5.0" + optionalDependencies: + bufferutil "^4.0.1" + utf-8-validate "^5.0.2" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +rxjs@^7.5.4: + version "7.6.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.6.0.tgz#361da5362b6ddaa691a2de0b4f2d32028f1eb5a2" + integrity sha512-DDa7d8TFNUalGC9VqXvQ1euWNN7sc63TrUCuM9J998+ViviahMIjKSOU7rfcgFOF+FCD71BhDRv4hrFz+ImDLQ== + dependencies: + tslib "^2.1.0" + +safe-buffer@^5.0.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +selective-whitespace@^1.0.0: + version "1.0.4" + resolved "https://registry.yarnpkg.com/selective-whitespace/-/selective-whitespace-1.0.4.tgz#02d428b46b2c770398be9a538eb442b1e842fc5c" + integrity sha512-DyL/pLbb9poQNQOVndVohclAO8lq+6gEBW8q3H9c2fX+ODkugQMvjBilPjw09lrIuVRFRQ/nwhLdzn60sFh9lQ== + dependencies: + tokenize-whitespace "0.0.1" + +semver@^7.3.7: + version "7.3.8" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.8.tgz#07a78feafb3f7b32347d725e33de7e2a2df67798" + integrity sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A== + dependencies: + lru-cache "^6.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +signal-exit@^3.0.3: + version "3.0.7" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +snake-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c" + integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + +socket.io-adapter@~2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz#b50a4a9ecdd00c34d4c8c808224daa1a786152a6" + integrity sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg== + +socket.io-client@^4.4.1: + version "4.5.4" + resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-4.5.4.tgz#d3cde8a06a6250041ba7390f08d2468ccebc5ac9" + integrity sha512-ZpKteoA06RzkD32IbqILZ+Cnst4xewU7ZYK12aS1mzHftFFjpoMz69IuhP/nL25pJfao/amoPI527KnuhFm01g== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.2" + engine.io-client "~6.2.3" + socket.io-parser "~4.2.1" + +socket.io-parser@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.1.tgz#01c96efa11ded938dcb21cbe590c26af5eff65e5" + integrity sha512-V4GrkLy+HeF1F/en3SpUaM+7XxYXpuMUWLGde1kSSh5nQMN4hLrbPIkD+otwh6q9R6NOQBN4AMaOZ2zVjui82g== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.1" + +socket.io@^4.4.1: + version "4.5.4" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.5.4.tgz#a4513f06e87451c17013b8d13fdfaf8da5a86a90" + integrity sha512-m3GC94iK9MfIEeIBfbhJs5BqFibMtkRk8ZpKwG2QwxV0m/eEhPIV4ara6XCF1LWNAus7z58RodiZlAH71U3EhQ== + dependencies: + accepts "~1.3.4" + base64id "~2.0.0" + debug "~4.3.2" + engine.io "~6.2.1" + socket.io-adapter "~2.4.0" + socket.io-parser "~4.2.1" + +split@0.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/split/-/split-0.3.3.tgz#cd0eea5e63a211dfff7eb0f091c4133e2d0dd28f" + integrity sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA== + dependencies: + through "2" + +start-server-and-test@^1.14.0: + version "1.15.2" + resolved "https://registry.yarnpkg.com/start-server-and-test/-/start-server-and-test-1.15.2.tgz#3c4f9b358a0dc5ae03a96dd7d7ae9e25a3b24165" + integrity sha512-t5xJX04Hg7hqxiKHMJBz/n4zIMsE6G7hpAcerFAH+4Vh9le/LeyFcJERJM7WLiPygWF9TOg33oroJF1XOzJtYQ== + dependencies: + arg "^5.0.2" + bluebird "3.7.2" + check-more-types "2.24.0" + debug "4.3.4" + execa "5.1.1" + lazy-ass "1.6.0" + ps-tree "1.2.0" + wait-on "6.0.1" + +stream-combiner@~0.0.4: + version "0.0.4" + resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.0.4.tgz#4d5e433c185261dde623ca3f44c586bcf5c4ad14" + integrity sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw== + dependencies: + duplexer "~0.1.1" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +superstruct@^0.14.2: + version "0.14.2" + resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.14.2.tgz#0dbcdf3d83676588828f1cf5ed35cda02f59025b" + integrity sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +text-encoding-utf-8@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13" + integrity sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg== + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +through@2, "through@>=2.2.7 <3", through@~2.3, through@~2.3.1: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +timeago.js@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/timeago.js/-/timeago.js-4.0.2.tgz#724e8c8833e3490676c7bb0a75f5daf20e558028" + integrity sha512-a7wPxPdVlQL7lqvitHGGRsofhdwtkoSXPGATFuSOA2i1ZNQEPLrGnj68vOp2sOJTCFAQVXPeNMX/GctBaO9L2w== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +tokenize-whitespace@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tokenize-whitespace/-/tokenize-whitespace-0.0.1.tgz#3c207ab58948113225246285200563e3b4b5c6ff" + integrity sha512-OxbdMHTjVs0ccBMfjo1v5OH6bq8yhydewCd9n6xihgtZvI3wSqR00EsM86DjELLl6VtMkY2z99r5q+uNW79E+Q== + +toml@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/toml/-/toml-3.0.0.tgz#342160f1af1904ec9d204d03a5d61222d762c5ee" + integrity sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w== + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +traverse-chain@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/traverse-chain/-/traverse-chain-0.1.0.tgz#61dbc2d53b69ff6091a12a168fd7d433107e40f1" + integrity sha512-up6Yvai4PYKhpNp5PkYtx50m3KbwQrqDwbuZP/ItyL64YEWHAvH6Md83LFLV/GRSk/BoUVwwgUzX6SOQSbsfAg== + +ts-essentials@^9.3.0: + version "9.3.0" + resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-9.3.0.tgz#7e639c1a76b1805c3c60d6e1b5178da2e70aea02" + integrity sha512-XeiCboEyBG8UqXZtXl59bWEi4ZgOqRsogFDI6WDGIF1LmzbYiAkIwjkXN6zZWWl4re/lsOqMlYfe8KA0XiiEPw== + +tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.0.3, tslib@^2.1.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" + integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== + +tsutils@^3.21.0: + version "3.21.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" + integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== + dependencies: + tslib "^1.8.1" + +tweetnacl@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +utf-8-validate@^5.0.2: + version "5.0.10" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.10.tgz#d7d10ea39318171ca982718b6b96a8d2442571a2" + integrity sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ== + dependencies: + node-gyp-build "^4.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== + +vary@^1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +wait-on@6.0.1, wait-on@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-6.0.1.tgz#16bbc4d1e4ebdd41c5b4e63a2e16dbd1f4e5601e" + integrity sha512-zht+KASY3usTY5u2LgaNqn/Cd8MukxLGjdcZxT2ns5QzDmTFc4XoWBgC+C/na+sMRZTuVygQoMYwdcVjHnYIVw== + dependencies: + axios "^0.25.0" + joi "^17.6.0" + lodash "^4.17.21" + minimist "^1.2.5" + rxjs "^7.5.4" + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" + integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@^7.4.5: + version "7.5.9" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" + integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== + +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== + +ws@~8.2.3: + version "8.2.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.2.3.tgz#63a56456db1b04367d0b721a0b80cae6d8becbba" + integrity sha512-wBuoj1BDpC6ZQ1B7DWQBYVLphPWkm8i9Y0/3YdHjHKHiohOJ1ws+3OccDWtH+PoC9DZD5WOTrJvNbWvjS6JWaA== + +xmlhttprequest-ssl@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67" + integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.3.0: + version "17.6.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541" + integrity sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==