Skip to content

Commit

Permalink
Third party plugins staging to main (#107)
Browse files Browse the repository at this point in the history
* Add structs and instructions for third party plugins (#58)

* Add structs and ixs for third party plugins

* lifecyle_checks into a HashMap and add check_external_registry

* Use Vec for third party lifecycle checks

* Combine lifecycle hooks and allow custom seeds

* Regenerate IDL and clients

* Rename external plugin header to external plugin

* Also move external plugin data offset inside of
external plugin.

* Regenerate IDL and clients

* Add create_v2 and create_colleciton_v2 basic structure

* Regenerate IDL and clients

* Add external plugin init info container

* Regenerate IDL and clients

* Add init and update structs for external plugins

* Also limit hookable lifecycle events.

* Resolve other PR comments.

* Regenerate IDL and clients

* Move key information into init structs (#65)

* Move key info into external plugin init info

* Regenerate clients and IDL

* Danenbm/no init data (#66)

* Do not allow data init on 3P plugins

* Also no lifecycle checks for data store.

* Regenerate IDL and clients

* Change external plugin registry name (#67)

* change external plugins to external registry

* Regenerate IDL and clients

* Update custom JS deserializer

* Remove extra accounts type (#68)

* External plugin key change (#73)

* Adding WIP for adding external plugins.

* Updating some permissions and adding more tests.

* Adding new IXes and data auth.

* Adding max size parameter.

* Removing allocate.

* Forgot to save.

* Removing size and clear IX.

* Updating based on feedback.

* Updating based on feedback and adding to create.'

* Adding tests and removing force approve.

* Danenbm/update from main (#80)

* Serde Pubkey DisplayFromStr (#78)

* Update kinobi version

* Regenerate clients

* chore: Release mpl-core version 0.5.0

---------

Co-authored-by: danenbm <[email protected]>

* Adding remove plugin.

* Removing prints.

* Adding for collection.

* Nhan/js sdk v1 (#75)

- sdk v1
- retain mostly backwards compatibility some types have changed name: BasePluginAuthority -> PluginAuthority
- many generated types have changed from Type -> BaseType
- support external plugins

* Oracle account type (#82)

* Regenerate IDL and clients

* Add Oracle validation type

* Regenerate IDL and clients

* Update JS SDK for ValidationResultsOffset

* fix minor type issues

---------

Co-authored-by: Nhan Phan <[email protected]>

* formatting

* Fixing deser.

* Testing fo realz.

* Adding ExternalPluginNotFound Error.

* Moving duped code to helper function.

* Add external plugin validation functions  (#85)

* Add validations for external plugins on lifecycle events

* Remove external_plugin_validate from plugin_checks

* WIP.

* Moving data offset and length to the record level.

* Updating JS hooks.

* Call external plugin validations (#86)

* Add validations for external plugins on lifecycle events

* Remove external_plugin_validate from plugin_checks

* Call external plugin validations

* Discard ForceApproved in external plugin validation

* Fast exit if external validation rejects

* Also mark ForceApproved as unreachable for external plugins

* Adding missing params to function after rebase

* Add missing ForceApproved check

* Update ExternalRegistryRecordSafe and fix tests (#88)

* Finishing up update external plugin.

* Removing prints.

* Adding check to prevent duplicate external plugins.

* Whoops missed collections.

* Adding resize optimization.

* Oracle lifecycle validations (#89)

* Update ExternalRegistryRecordSafe and fix tests

* Add accounts, asset, and collection to PluginValidationContext

* Implement Oracle lifecycle validations

* Add new error codes

* Regenerate IDL and clients

* Derive custom PDA using Seeds Vec

* Make sure third party plugins can approve or reject before using output

* Use user program for Program Seed

* Change ExternalCheckResult to u32

* Regenerate IDL and clients

* Fixup add accounts (#94)

* Add missing accounts

* Format fix

* Add Address seed and remove Program seed (#95)

* Add missing accounts

* Format fix

* Remove program seed and add address seed

* Regenerate IDL and clients

* Update JS SDK Seed type

* Format fix

* Format fix

* Prevent Oracle from approving lifecycle events (#98)

* Prevent Oracle from approving lifecycle events

* Regenerate IDL and clients

* Change error to be Oracle specific and prevent Oracle from listening

* Lint fix (#97)

* Add default case for extraAccount seed handler

* Regenerate JS client

* Format fix

* Danenbm/oracle error code (#100)

* Regenerate IDL with changed error code

* Make check tighter to not allow zero checks

Also fix Rust client tests

* oracle test wip (#90)

* Require passing in oracle lifecycle checks Vec (#101)

* Regenerate IDL with changed error code

* Make check tighter to not allow zero checks

Also fix Rust client tests

* Require Oracle to provide lifecycle checks at init

* Regenerate IDL and clients

* update oracle init helper

* Fix Rust tests

* Require non-empty Vec

* Regenerate IDL and clients

* Change CanDeny to CanReject

* Regenerate IDL and clients

* Format fix

* Add test

* Add more tests

* More tests for invalid checks and multiple oracle test

* Prevent duplicate lifecycle checks in external plugins (#104)

* Prevent duplicate lifecycle check on external plugins

* Regenerate IDL and clients

* Add Rust tests

* Danenbm/lifecycle check requirements (#105)

* Require LifecycleHook to have lifecycle checks

Also do not update lifecycle checks when update value is None

* Regenerate IDL and clients

* Fix JS client and fix Rust tests

* Add tests

* Add Rust tests for updating with duplicate lifecycle events

* fix cyclic dep, create with plugin, update with key

---------

Co-authored-by: Nhan Phan <[email protected]>

* Block `LifecycleHook` and `DataStore` from being created or added later (#106)

* Block LifecycleHook and DataStore for now

* Skip tests for deactivated external plugins

* Add more Oracle update tests

* Add tests to verify LifecycleHook and DataStore cannot be added

* Ignore Rust update test

* Add collection tests

* fix external plugin offsets, better types, new tests (#109)

* Adding anchor types.

* Update.

* removed audit warning (#108)

* better types, new test

* Updating Kinobi dependency.

* Fixing kinobi reference.

* Bumping Kinobi version and fixing clients.

* Fixing mpl-utils version.

* Immutability plugins (#96)

* Add ImmutableMetadata && AddBlocker plugins

* Update autogenerated parts

* add tests

* change the order of enums

* update generated part

* added tests for ensuring that UA is the only one who can add the plugin

* added tests for ensuring that UA is the only one who can add the plugin for collection and nested plugins

* update tests

* add audit details to readme (#103)

* removed audit warning (#108)

* regenerated clients

* updated tests

* updated rust clients

* chore: Release mpl-core version 0.6.0

* Modify `update` and `update_plugin` to move external plugin offsets (#112)

Notes
Also combine asset and collection update functions to use the same processors.
Also fix offset calculations when updating external plugins (added test for this case).
More advanced refactor: Combine plugin updating into single utility function #113
Note this PR is built upon #109 so that it can be tested with the new tests from that branch.

* Deploy JS client v0.4.7

* update ava version, allow assertions on external plugins, check oracles in oracle tests

* minor add plugin interface change

* add additional test assert, formatting

---------

Co-authored-by: blockiosaurus <[email protected]>
Co-authored-by: Tony Boyle <[email protected]>
Co-authored-by: Kyrylo Stepanov <[email protected]>
Co-authored-by: blockiosaurus <[email protected]>
Co-authored-by: blockiosaurus <[email protected]>
Co-authored-by: Michael Danenberg <[email protected]>

* Improve Oracle account deserialization error handling (#115)

* Remap oracle account deserialization errors

Also fix extra space on last error message

* Regenerate IDL and clients

* Add tests and more error handling to Oracle borrowing and slicing

* Change test name

* Oracle uninitialized variant (#116)

* Add uninitialized variant to OracleValidation

* Regenerate IDL and clients

* Handle uninitialized kind in JS

* Add test for empty account

* Add ability to specify custom program for custom PDA (#117)

* Add ability to specify custom program for custom PDA

* Regenerate IDL and JS SDK

* Add customProgramId JS handling

* Add test

* Update oracle example js package

* bump version v1-alpha

* bump version v1-alpha

* Run CI on third-party-plugins-staging (#122)

* Run on third-party-plugins-staging for now

* Format fix

* Nhan/rename external plugin (#119)

* bump version v1-alpha

* bump version v1-alpha

* initial rename

* Rename Oracle pda - stacked onto rename external plugin (#121)

* Format fix

* Rename oracle pda to base_address_config in program

* Regenerate IDL and clients

* Update oracle SDK functions

* Format JS and fix all tests

* rename to external plugin adapater

* formatting

* rename back to external validation result

* use internal validationresult type

* rename a few more things

* minor nits, comments

---------

Co-authored-by: Michael Danenberg <[email protected]>

* remove unused remove collection plugin type field (#123)

* bump version v1-alpha

* remove unused type

* Add simple test to use new SDK to add Oracle plugin (#125)

* slightly refactor collection plugin types, add test (#124)

* bump alpha version

* bump version v1-alpha

* add types, bump version

* add fetch helpers, reduce strictness for umi context ix types, fix derive bug, better asset vs. collection plugin types (#126)

* bump version v1-alpha

* better deprecation message

* lint, more flexible arg

---------

Co-authored-by: blockiosaurus <[email protected]>
Co-authored-by: blockiosaurus <[email protected]>
Co-authored-by: danenbm <[email protected]>
Co-authored-by: Nhan Phan <[email protected]>
Co-authored-by: Nhan Phan <[email protected]>
Co-authored-by: Tony Boyle <[email protected]>
Co-authored-by: Kyrylo Stepanov <[email protected]>
Co-authored-by: blockiosaurus <[email protected]>
  • Loading branch information
9 people authored May 20, 2024
1 parent e2b10c5 commit 9053d3b
Show file tree
Hide file tree
Showing 267 changed files with 26,661 additions and 2,151 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Main

on:
push:
branches: [main]
branches: [main, third-party-plugins-staging]
pull_request:
branches: [main]
branches: [main, third-party-plugins-staging]

env:
CACHE: true
Expand Down
11 changes: 6 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

258 changes: 133 additions & 125 deletions clients/js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,138 +38,146 @@ A Umi-compatible JavaScript library for the project.
4. Examples
```ts
// Create an asset
const assetAddress = generateSigner(umi);
const owner = generateSigner(umi);

await createV1(umi, {
name: 'Test Asset',
uri: 'https://example.com/asset.json',
asset: assetAddress,
owner: owner.publicKey, // optional, will default to payer
}).sendAndConfirm(umi);

// Create a collection
const collectionUpdateAuthority = generateSigner(umi);
const collectionAddress = generateSigner(umi);
await createCollectionV1(umi, {
name: 'Test Collection',
uri: 'https://example.com/collection.json',
collection: collectionAddress,
updateAuthority: collectionUpdateAuthority.publicKey, // optional, defaults to payer
}).sendAndConfirm(umi);

// Create an asset in a collection, the authority must be the updateAuthority of the collection
await createV1(umi, {
name: 'Test Asset',
uri: 'https://example.com/asset.json',
asset: assetAddress,
collection: collectionAddress.publicKey,
authority: collectionUpdateAuthority, // optional, defaults to payer
}).sendAndConfirm(umi);

// Transfer an asset
const recipient = generateSigner(umi);
await transferV1(umi, {
asset: assetAddress.publicKey,
newOwner: recipient.publicKey,
}).sendAndConfirm(umi);

// Transfer an asset in a collection
await transferV1(umi, {
asset: assetAddress.publicKey,
newOwner: recipient.publicKey,
collection: collectionAddress.publicKey,
}).sendAndConfirm(umi);

// Fetch an asset
const asset = await fetchAssetV1(umi, assetAddress.publicKey);

// GPA fetch assets by owner
const assetsByOwner = await getAssetV1GpaBuilder(umi)
.whereField('key', Key.AssetV1)
.whereField('owner', owner.publicKey)
.getDeserialized();

// GPA fetch assets by collection
const assetsByCollection = await getAssetV1GpaBuilder(umi)
.whereField('key', Key.AssetV1)
.whereField(
'updateAuthority',
updateAuthority('Collection', [collectionAddress.publicKey])
)
.getDeserialized();

// DAS API (RPC based indexing) fetch assets by owner/collection
// Coming soon
const assetAddress = generateSigner(umi);
const owner = generateSigner(umi);

await create(umi, {
name: 'Test Asset',
uri: 'https://example.com/asset.json',
asset: assetAddress,
owner: owner.publicKey, // optional, will default to payer
}).sendAndConfirm(umi);

// Fetch an asset
const asset = await fetchAssetV1(umi, assetAddress.publicKey);

// Create a collection
const collectionUpdateAuthority = generateSigner(umi);
const collectionAddress = generateSigner(umi);
await createCollection(umi, {
name: 'Test Collection',
uri: 'https://example.com/collection.json',
collection: collectionAddress,
updateAuthority: collectionUpdateAuthority.publicKey, // optional, defaults to payer
}).sendAndConfirm(umi);

// Fetch a collection
const collection = await fetchCollectionV1(umi, collectionAddress.publicKey);

// Create an asset in a collection, the authority must be the updateAuthority of the collection
await create(umi, {
name: 'Test Asset',
uri: 'https://example.com/asset.json',
asset: assetAddress,
collection,
authority: collectionUpdateAuthority, // optional, defaults to payer
}).sendAndConfirm(umi);

// Transfer an asset
const recipient = generateSigner(umi);
await transfer(umi, {
asset,
newOwner: recipient.publicKey,
}).sendAndConfirm(umi);

// Transfer an asset in a collection
await transfer(umi, {
asset,
newOwner: recipient.publicKey,
collection,
}).sendAndConfirm(umi);

// GPA fetch assets by owner
const assetsByOwner = await getAssetV1GpaBuilder(umi)
.whereField('key', Key.AssetV1)
.whereField('owner', owner.publicKey)
.getDeserialized();

// GPA fetch assets by collection
const assetsByCollection = await getAssetV1GpaBuilder(umi)
.whereField('key', Key.AssetV1)
.whereField(
'updateAuthority',
updateAuthority('Collection', [collectionAddress.publicKey])
)
.getDeserialized();

// DAS API (RPC based indexing) fetch assets by owner/collection
// Coming soon

```
5. Some advanced examples
```ts
// Freezing an asset
const assetAddress = generateSigner(umi);
const freezeDelegate = generateSigner(umi);

await addPluginV1(umi, {
asset: assetAddress.publicKey,
// adds the owner-managed freeze plugin to the asset
plugin: createPlugin({
type: 'FreezeDelegate',
data: {
frozen: true,
},
}),
const umi = await createUmi();

// Freezing an asset
const assetAddress = generateSigner(umi);
const freezeDelegate = generateSigner(umi);

await addPlugin(umi, {
asset: assetAddress.publicKey,
// adds the owner-managed freeze plugin to the asset
plugin: {
type: 'FreezeDelegate',
frozen: true,

// Optionally set the authority to a delegate who can unfreeze. If unset, this will be the Owner
// This is functionally the same as calling addPlugin and approvePluginAuthority separately.
// Freezing with a delegate is commonly used for escrowless staking programs.
initAuthority: addressPluginAuthority(freezeDelegate.publicKey),
}).sendAndConfirm(umi);

// Unfreezing an asset with a delegate
// Revoking an authority will revert the authority back to the owner for owner-managed plugins
await revokePluginAuthorityV1(umi, {
asset: assetAddress.publicKey,
pluginType: PluginType.FreezeDelegate,
authority: freezeDelegate,
}).sendAndConfirm(umi);

// Create a collection with royalties
const collectionAddress = generateSigner(umi);
const creator1 = generateSigner(umi);
const creator2 = generateSigner(umi);

await createCollectionV1(umi, {
name: 'Test Collection',
uri: 'https://example.com/collection.json',
collection: collectionAddress,
plugins: [
pluginAuthorityPair({
type: 'Royalties',
data: {
basisPoints: 500,
creators: [
{
address: creator1.publicKey,
percentage: 20,
},
{
address: creator2.publicKey,
percentage: 80,
},
],
ruleSet: ruleSet('None'), // Compatibility rule set
},
}),
],
}).sendAndConfirm(umi);

// Create an asset in a collection.
// Assets in a collection will inherit the collection's authority-managed plugins, in this case the royalties plugin
await createV1(umi, {
name: 'Test Asset',
uri: 'https://example.com/asset.json',
asset: assetAddress,
collection: collectionAddress.publicKey,
}).sendAndConfirm(umi);
authority: {
type: 'Address',
address: freezeDelegate.publicKey,
},
}
}).sendAndConfirm(umi);

// Unfreezing an asset with a delegate
// Revoking an authority will revert the authority back to the owner for owner-managed plugins
await revokePluginAuthority(umi, {
asset: assetAddress.publicKey,
plugin: {
type: 'FreezeDelegate',
},
authority: freezeDelegate,
}).sendAndConfirm(umi);

// Create a collection with royalties
const collectionAddress = generateSigner(umi);
const creator1 = generateSigner(umi);
const creator2 = generateSigner(umi);

await createCollection(umi, {
name: 'Test Collection',
uri: 'https://example.com/collection.json',
collection: collectionAddress,
plugins: [
{
type: 'Royalties',
basisPoints: 500,
creators: [
{
address: creator1.publicKey,
percentage: 20,
},
{
address: creator2.publicKey,
percentage: 80,
},
],
ruleSet: ruleSet('None'), // Compatibility rule set

},
],
}).sendAndConfirm(umi);

// Create an asset in a collection.
// Assets in a collection will inherit the collection's authority-managed plugins, in this case the royalties plugin
await create(umi, {
name: 'Test Asset',
uri: 'https://example.com/asset.json',
asset: assetAddress,
collection: await fetchCollectionV1(umi, collectionAddress.publicKey),
}).sendAndConfirm(umi);
```

You can learn more about this library's API by reading its generated [TypeDoc documentation](https://mpl-core-js-docs.vercel.app).
Expand Down
14 changes: 7 additions & 7 deletions clients/js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@metaplex-foundation/mpl-core",
"version": "0.4.7",
"version": "1.0.0-alpha.6",
"description": "Digital Assets",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
Expand All @@ -25,27 +25,27 @@
"repository": "https://github.com/metaplex-foundation/mpl-core.git",
"author": "Metaplex Maintainers <[email protected]>",
"license": "Apache-2.0",
"dependencies": {},
"peerDependencies": {
"@metaplex-foundation/umi": ">=0.8.2 < 1",
"@noble/hashes": "^1.3.1"
},
"devDependencies": {
"@ava/typescript": "^5.0.0",
"@metaplex-foundation/mpl-core-oracle-example": "^0.0.2",
"@metaplex-foundation/mpl-toolbox": "^0.8.0",
"@ava/typescript": "^3.0.1",
"@metaplex-foundation/umi": "^0.8.2",
"@metaplex-foundation/umi-bundle-tests": "^0.8.2",
"@metaplex-foundation/umi": "^0.8.10",
"@metaplex-foundation/umi-bundle-tests": "^0.8.10",
"@solana/web3.js": "^1.73.0",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.46.1",
"ava": "^5.1.0",
"ava": "^6.1.3",
"bs58": "5.0.0",
"eslint": "^8.0.1",
"eslint-config-airbnb-typescript": "^17.0.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-prettier": "^4.2.1",
"prettier": "^2.8.8",
"prettier": "^3.2.5",
"rimraf": "^3.0.2",
"typedoc": "^0.23.16",
"typedoc-plugin-expand-object-like-types": "^0.1.1",
Expand Down
Loading

0 comments on commit 9053d3b

Please sign in to comment.