Skip to content

Commit

Permalink
Fix update delegate plugin permission (#47)
Browse files Browse the repository at this point in the history
  • Loading branch information
danenbm authored Apr 2, 2024
1 parent 9649fa9 commit 001301c
Show file tree
Hide file tree
Showing 5 changed files with 302 additions and 1 deletion.
2 changes: 1 addition & 1 deletion clients/js/test/plugins/asset/delegateTransfer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ test('it can revoke a delegate transfer plugin', async (t) => {
});
});

test('it cannot transfer after delegate has been revoked', async (t) => {
test('it cannot transfer after delegate authority has been revoked', async (t) => {
// Given a Umi instance and a new signer.
const umi = await createUmi();
const delegateAddress = generateSigner(umi);
Expand Down
133 changes: 133 additions & 0 deletions clients/js/test/plugins/asset/updateDelegate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import {
pluginAuthorityPair,
addPluginV1,
updatePluginV1,
updateV1,
addressPluginAuthority,
revokePluginAuthorityV1,
PluginType,
} from '../../../src';
import {
DEFAULT_ASSET,
Expand Down Expand Up @@ -123,3 +127,132 @@ test('it cannot update updateDelegate on asset with additional delegates', async
},
});
});

test('it updateDelegate can update an asset', async (t) => {
const umi = await createUmi();
const asset = await createAsset(umi, {
name: 'short',
uri: 'https://short.com',
});
const updateDelegate = generateSigner(umi);

await addPluginV1(umi, {
asset: asset.publicKey,
plugin: createPlugin({
type: 'UpdateDelegate',
}),
initAuthority: addressPluginAuthority(updateDelegate.publicKey),
}).sendAndConfirm(umi);

await assertAsset(t, umi, {
asset: asset.publicKey,
owner: umi.identity.publicKey,
updateAuthority: { type: 'Address', address: umi.identity.publicKey },
updateDelegate: {
authority: {
type: 'Address',
address: updateDelegate.publicKey,
},
additionalDelegates: [],
},
name: 'short',
uri: 'https://short.com',
});

await updateV1(umi, {
asset: asset.publicKey,
newName: 'Test Bread 2',
newUri: 'https://example.com/bread2',
authority: updateDelegate,
}).sendAndConfirm(umi);

await assertAsset(t, umi, {
...DEFAULT_ASSET,
asset: asset.publicKey,
owner: umi.identity.publicKey,
updateAuthority: { type: 'Address', address: umi.identity.publicKey },
updateDelegate: {
authority: {
type: 'Address',
address: updateDelegate.publicKey,
},
additionalDelegates: [],
},
name: 'Test Bread 2',
uri: 'https://example.com/bread2',
});
});

test('it updateDelegate cannot update an asset after delegate authority revoked', async (t) => {
const umi = await createUmi();
const asset = await createAsset(umi, {
name: 'short',
uri: 'https://short.com',
});
const updateDelegate = generateSigner(umi);

await addPluginV1(umi, {
asset: asset.publicKey,
plugin: createPlugin({
type: 'UpdateDelegate',
}),
initAuthority: addressPluginAuthority(updateDelegate.publicKey),
}).sendAndConfirm(umi);

await assertAsset(t, umi, {
asset: asset.publicKey,
owner: umi.identity.publicKey,
updateAuthority: { type: 'Address', address: umi.identity.publicKey },
updateDelegate: {
authority: {
type: 'Address',
address: updateDelegate.publicKey,
},
additionalDelegates: [],
},
name: 'short',
uri: 'https://short.com',
});

await revokePluginAuthorityV1(umi, {
asset: asset.publicKey,
pluginType: PluginType.UpdateDelegate,
}).sendAndConfirm(umi);

await assertAsset(t, umi, {
asset: asset.publicKey,
owner: umi.identity.publicKey,
updateAuthority: { type: 'Address', address: umi.identity.publicKey },
updateDelegate: {
authority: {
type: 'UpdateAuthority',
},
additionalDelegates: [],
},
name: 'short',
uri: 'https://short.com',
});

const result = updateV1(umi, {
asset: asset.publicKey,
newName: 'Test Bread 2',
newUri: 'https://example.com/bread2',
authority: updateDelegate,
}).sendAndConfirm(umi);

await t.throwsAsync(result, { name: 'NoApprovals' });

await assertAsset(t, umi, {
asset: asset.publicKey,
owner: umi.identity.publicKey,
updateAuthority: { type: 'Address', address: umi.identity.publicKey },
updateDelegate: {
authority: {
type: 'UpdateAuthority',
},
additionalDelegates: [],
},
name: 'short',
uri: 'https://short.com',
});
});
151 changes: 151 additions & 0 deletions clients/js/test/plugins/collection/updateDelegate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import {
createPlugin,
pluginAuthorityPair,
addressPluginAuthority,
updateV1,
updateCollectionPluginV1,
revokeCollectionPluginAuthorityV1,
} from '../../../src';
import {
DEFAULT_ASSET,
Expand Down Expand Up @@ -186,3 +188,152 @@ test('it cannot update updateDelegate on collection with additional delegates',
},
});
});

test('it updateDelegate on collection can update an asset', async (t) => {
// Given a Umi instance and a new signer.
const umi = await createUmi();
const updateDelegate = await generateSignerWithSol(umi);

// When we create a new account.
const collection = await createCollection(umi, {
plugins: [
pluginAuthorityPair({
type: 'UpdateDelegate',
}),
],
});

await approveCollectionPluginAuthorityV1(umi, {
collection: collection.publicKey,
pluginType: PluginType.UpdateDelegate,
newAuthority: addressPluginAuthority(updateDelegate.publicKey),
}).sendAndConfirm(umi);

await assertCollection(t, umi, {
...DEFAULT_COLLECTION,
collection: collection.publicKey,
updateAuthority: umi.identity.publicKey,
updateDelegate: {
authority: {
type: 'Address',
address: updateDelegate.publicKey,
},
additionalDelegates: [],
},
});

umi.identity = updateDelegate;
umi.payer = updateDelegate;
const owner = generateSigner(umi);
const asset = await createAsset(umi, {
collection: collection.publicKey,
owner,
authority: updateDelegate,
name: 'short',
uri: 'https://short.com',
});

await assertAsset(t, umi, {
...DEFAULT_ASSET,
asset: asset.publicKey,
owner: owner.publicKey,
updateAuthority: { type: 'Collection', address: collection.publicKey },
name: 'short',
uri: 'https://short.com',
});

await updateV1(umi, {
asset: asset.publicKey,
collection: collection.publicKey,
newName: 'Test Bread 2',
newUri: 'https://example.com/bread2',
authority: updateDelegate,
}).sendAndConfirm(umi);

await assertAsset(t, umi, {
...DEFAULT_ASSET,
asset: asset.publicKey,
owner: owner.publicKey,
updateAuthority: { type: 'Collection', address: collection.publicKey },
name: 'Test Bread 2',
uri: 'https://example.com/bread2',
});
});

test('it updateDelegate on collection cannot update an asset after delegate authority revoked', async (t) => {
// Given a Umi instance and a new signer.
const umi = await createUmi();
const updateDelegate = await generateSignerWithSol(umi);

// When we create a new account.
const collection = await createCollection(umi, {
plugins: [
pluginAuthorityPair({
type: 'UpdateDelegate',
}),
],
});

await approveCollectionPluginAuthorityV1(umi, {
collection: collection.publicKey,
pluginType: PluginType.UpdateDelegate,
newAuthority: addressPluginAuthority(updateDelegate.publicKey),
}).sendAndConfirm(umi);

await assertCollection(t, umi, {
...DEFAULT_COLLECTION,
collection: collection.publicKey,
updateAuthority: umi.identity.publicKey,
updateDelegate: {
authority: {
type: 'Address',
address: updateDelegate.publicKey,
},
additionalDelegates: [],
},
});

umi.identity = updateDelegate;
umi.payer = updateDelegate;
const owner = generateSigner(umi);
const asset = await createAsset(umi, {
collection: collection.publicKey,
owner,
authority: updateDelegate,
name: 'short',
uri: 'https://short.com',
});

await assertAsset(t, umi, {
...DEFAULT_ASSET,
asset: asset.publicKey,
owner: owner.publicKey,
updateAuthority: { type: 'Collection', address: collection.publicKey },
name: 'short',
uri: 'https://short.com',
});

await revokeCollectionPluginAuthorityV1(umi, {
collection: collection.publicKey,
pluginType: PluginType.UpdateDelegate,
}).sendAndConfirm(umi);

let result = updateV1(umi, {
asset: asset.publicKey,
collection: collection.publicKey,
newName: 'Test Bread 2',
newUri: 'https://example.com/bread2',
authority: updateDelegate,
}).sendAndConfirm(umi);

await t.throwsAsync(result, { name: 'NoApprovals' });

await assertAsset(t, umi, {
...DEFAULT_ASSET,
asset: asset.publicKey,
owner: owner.publicKey,
updateAuthority: { type: 'Collection', address: collection.publicKey },
name: 'short',
uri: 'https://short.com',
});
});
1 change: 1 addition & 0 deletions programs/mpl-core/src/plugins/lifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ impl PluginType {
pub fn check_update(plugin_type: &PluginType) -> CheckResult {
#[allow(clippy::match_single_binding)]
match plugin_type {
PluginType::UpdateDelegate => CheckResult::CanApprove,
_ => CheckResult::None,
}
}
Expand Down
16 changes: 16 additions & 0 deletions programs/mpl-core/src/plugins/update_delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,22 @@ impl PluginValidation for UpdateDelegate {
}
}

fn validate_update(
&self,
authority_info: &AccountInfo,
authority: &Authority,
) -> Result<ValidationResult, ProgramError> {
if authority
== (&Authority::Address {
address: *authority_info.key,
})
{
Ok(ValidationResult::Approved)
} else {
Ok(ValidationResult::Pass)
}
}

fn validate_update_plugin(
&self,
_authority_info: &AccountInfo,
Expand Down

0 comments on commit 001301c

Please sign in to comment.