Skip to content

Commit

Permalink
revoke_single, revoke_multiple & revoke_all logic + tests
Browse files Browse the repository at this point in the history
  • Loading branch information
marc0olo committed Jul 5, 2023
1 parent caa1163 commit 74d53e5
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 6 deletions.
25 changes: 19 additions & 6 deletions development/smart-contracts/contracts/AENSWrapping.aes
Expand Up @@ -67,6 +67,8 @@ main contract AENSWrapping : IAEX141, IAENSWrapping =
| NameExtend(string, int, int, address)
// name, nft_id_old, nft_id_new
| NameTransfer(string, int, int)
// name, nft_id
| NameRevoke(string, int)
// nft_id, caller, reward
| Reward(int, address, int)

Expand Down Expand Up @@ -304,21 +306,25 @@ main contract AENSWrapping : IAEX141, IAENSWrapping =
/// @param nft_id the id of the NFT where the AENS name is wrapped into
/// @param name the AENS name to revoke
stateful entrypoint revoke_single(nft_id: int, name: string) =
// TODO
()
require_authorized(nft_id)
require_not_expired(nft_id)
__revoke(nft_id, name)

/// @notice revokes multiple AENS names wrapped in the NFT, removes metadata
/// @param nft_id the id of the NFT where the AENS name is wrapped into
/// @param names the AENS names to revoke
stateful entrypoint revoke_multiple(nft_id: int, names: Set.set(string)) =
// TODO
()
require_authorized(nft_id)
require_not_expired(nft_id)
List.foreach(Set.to_list(names), (n) => __revoke(nft_id, n))

/// @notice revokes all AENS names wrapped in the NFT, removes metadata and burns the NFT
/// @param nft_id the id of the NFT where the AENS name is wrapped into
stateful entrypoint revoke_all(nft_id: int) =
// TODO
()
require_authorized(nft_id)
require_not_expired(nft_id)
let nft_metadata_map = require_names_wrapped(nft_id)
List.foreach(Map.to_list(nft_metadata_map), (val) => __revoke(nft_id, Pair.fst(val)))

/// @notice transfers a single AENS name to another NFT by updating metadata of both NFTs, updates expiry of name to match expiry of already wrapped names
/// @param nft_id_old the id of the NFT that currently wraps the AENS name
Expand Down Expand Up @@ -493,6 +499,13 @@ main contract AENSWrapping : IAEX141, IAENSWrapping =
stateful entrypoint transfer_multiple_nfts(recipient: address, nft_ids: Set.set(int), data: option(string)) =
List.foreach(Set.to_list(nft_ids), (id) => __transfer_single_nft(recipient, id, data))

stateful function __revoke(nft_id: int, name: string) =
let nft_metadata_map = require_name_wrapped(nft_id, name)
AENS.revoke(Contract.address, name)
put(state{ metadata[nft_id] @nft_metadata = MetadataMap(Map.delete(name, nft_metadata_map)) })
put(state{ name_to_token @ val = Map.delete(name, val) })
Chain.event(NameRevoke(name, nft_id))

stateful function __transfer_single_nft(to: address, token_id: int, data: option(string)) =
let from = require_authorized(token_id)
require(from != to, "SENDER_MUST_NOT_BE_RECEIVER")
Expand Down
Expand Up @@ -11,6 +11,8 @@ contract interface IAENSWrapping =
| NameExtend(string, int, int, address)
// name, nft_id_old, nft_id_new
| NameTransfer(string, int, int)
// name, nft_id
| NameRevoke(string, int)
// nft_id, caller, reward
| Reward(int, address, int)

Expand Down
108 changes: 108 additions & 0 deletions development/smart-contracts/test/aensWrappingTest.js
Expand Up @@ -739,6 +739,7 @@ describe('AENSWrapping', () => {
assert.equal(extendAllForRewardTx.decodedEvents[0].args[1], otherAccount.address);
assert.equal(extendAllForRewardTx.decodedEvents[0].args[2], globalConfig.reward);
});

it('extend_all_for_reward (emergency reward)', async () => {
// prepare: claim and wrap names
await claimNames(aensNames);
Expand Down Expand Up @@ -792,6 +793,7 @@ describe('AENSWrapping', () => {
assert.equal(extendAllForRewardTx.decodedEvents[0].args[1], otherAccount.address);
assert.equal(extendAllForRewardTx.decodedEvents[0].args[2], globalConfig.emergency_reward);
});

it('burn & burn_multiple_nfts', async () => {
// prepare: mint 3 different NFTs
await contract.mint(aeSdk.selectedAddress);
Expand All @@ -808,6 +810,7 @@ describe('AENSWrapping', () => {

// burn a single NFT
const burnTx = await contract.burn(2);
console.log(`Gas used (burn): ${burnTx.result.gasUsed}`);

// check Burn event
assert.equal(burnTx.decodedEvents[0].name, 'Burn');
Expand All @@ -823,6 +826,7 @@ describe('AENSWrapping', () => {
assert.deepEqual(ownedTokens, [1n, 3n]);

const burnMultipleNftsTx = await contract.burn_multiple_nfts([1,3]);
console.log(`Gas used (burn_multiple_nfts with 2 nfts): ${burnMultipleNftsTx.result.gasUsed}`);

// check Burn events
assert.equal(burnMultipleNftsTx.decodedEvents[0].name, 'Burn');
Expand All @@ -843,6 +847,110 @@ describe('AENSWrapping', () => {
// TODO test burning with expired names
// blocked by https://github.com/aeternity/aeproject/issues/470
});

it('revoke_single', async () => {
// prepare: claim and wrap names
await claimNames(aensNames);
const namesDelegationSigs = await getDelegationSignatures(aensNames, contractId);
await contract.wrap_and_mint(namesDelegationSigs);

// pre revocation checks
const expirationHeight = (await contract.get_expiration_by_nft_id(1)).decodedResult;
let nftDataOne = (await contract.get_nft_data(1)).decodedResult;
assert.deepEqual(nftDataOne, {id: 1n, owner: aeSdk.selectedAddress, owner_config: undefined, names: aensNames, expiration_height: expirationHeight});
await expectNftMetadataMap(1, getExpectedNftMetadataMap(aensNames));

const revokeSingleTx = await contract.revoke_single(1, aensNames[0]);
console.log(`Gas used (revoke_single): ${revokeSingleTx.result.gasUsed}`);

// check NameRevoke event
assert.equal(revokeSingleTx.decodedEvents[0].name, 'NameRevoke');
assert.equal(revokeSingleTx.decodedEvents[0].args[0], aensNames[0]);
assert.equal(revokeSingleTx.decodedEvents[0].args[1], 1);

// after revocation checks
nftDataOne = (await contract.get_nft_data(1)).decodedResult;
assert.deepEqual(nftDataOne, {id: 1n, owner: aeSdk.selectedAddress, owner_config: undefined, names: aensNames.slice(1), expiration_height: expirationHeight});
await expectNftMetadataMap(1, getExpectedNftMetadataMap(aensNames.slice(1)));
try {
await aeSdk.aensQuery(aensNames[0]);
} catch(e) {
assert.equal(e.statusCode, 404);
assert.equal(e.details.reason, "Name revoked");
}
});

it('revoke_multiple', async () => {
// prepare: claim and wrap names
await claimNames(aensNames);
const namesDelegationSigs = await getDelegationSignatures(aensNames, contractId);
await contract.wrap_and_mint(namesDelegationSigs);

// pre revocation checks
const expirationHeight = (await contract.get_expiration_by_nft_id(1)).decodedResult;
let nftDataOne = (await contract.get_nft_data(1)).decodedResult;
assert.deepEqual(nftDataOne, {id: 1n, owner: aeSdk.selectedAddress, owner_config: undefined, names: aensNames, expiration_height: expirationHeight});
await expectNftMetadataMap(1, getExpectedNftMetadataMap(aensNames));

const revokeMultipleTx = await contract.revoke_multiple(1, aensNames.slice(1));
console.log(`Gas used (revoke_multiple) with ${aensNames.slice(1).length} names: ${revokeMultipleTx.result.gasUsed}`);

// check NameRevoke events
for(let i=0; i<aensNames.slice(1).length; i++) {
assert.equal(revokeMultipleTx.decodedEvents[i].name, 'NameRevoke');
assert.equal(revokeMultipleTx.decodedEvents[i].args[0], aensNames.slice(1)[aensNames.slice(1).length-(i+1)]);
assert.equal(revokeMultipleTx.decodedEvents[i].args[1], 1);
}

// after revocation checks
nftDataOne = (await contract.get_nft_data(1)).decodedResult;
assert.deepEqual(nftDataOne, {id: 1n, owner: aeSdk.selectedAddress, owner_config: undefined, names: [aensNames[0]], expiration_height: expirationHeight});
await expectNftMetadataMap(1, getExpectedNftMetadataMap([aensNames[0]]));
for(let i=0; i<aensNames.slice(1).length; i++) {
try {
await aeSdk.aensQuery(aensNames.slice(1)[i]);
} catch(e) {
assert.equal(e.statusCode, 404);
assert.equal(e.details.reason, "Name revoked");
}
}
});

it('revoke_all', async () => {
// prepare: claim and wrap names
await claimNames(aensNames);
const namesDelegationSigs = await getDelegationSignatures(aensNames, contractId);
await contract.wrap_and_mint(namesDelegationSigs);

// pre revocation checks
const expirationHeight = (await contract.get_expiration_by_nft_id(1)).decodedResult;
let nftDataOne = (await contract.get_nft_data(1)).decodedResult;
assert.deepEqual(nftDataOne, {id: 1n, owner: aeSdk.selectedAddress, owner_config: undefined, names: aensNames, expiration_height: expirationHeight});
await expectNftMetadataMap(1, getExpectedNftMetadataMap(aensNames));

const revokeAllTx = await contract.revoke_all(1);
console.log(`Gas used (revoke_all) with ${aensNames.length} names: ${revokeAllTx.result.gasUsed}`);

// check NameRevoke events
for(let i=0; i<aensNames.length; i++) {
assert.equal(revokeAllTx.decodedEvents[i].name, 'NameRevoke');
assert.equal(revokeAllTx.decodedEvents[i].args[0], aensNames[aensNames.length-(i+1)]);
assert.equal(revokeAllTx.decodedEvents[i].args[1], 1);
}

// after revocation checks
nftDataOne = (await contract.get_nft_data(1)).decodedResult;
assert.deepEqual(nftDataOne, {id: 1n, owner: aeSdk.selectedAddress, owner_config: undefined, names: [], expiration_height: expirationHeight});
await expectNftMetadataMap(1, new Map());
for(let i=0; i<aensNames.length; i++) {
try {
await aeSdk.aensQuery(aensNames[i]);
} catch(e) {
assert.equal(e.statusCode, 404);
assert.equal(e.details.reason, "Name revoked");
}
}
});
});
});
});
2 changes: 2 additions & 0 deletions docs/contract-interface.md
Expand Up @@ -57,6 +57,8 @@ contract interface IAENSWrapping : IAEX141 =
| NameExtend(string, int, int, address)
// name, nft_id_old, nft_id_new
| NameTransfer(string, int, int)
// name, nft_id
| NameRevoke(string, int)
// nft_id, caller, reward
| Reward(int, address, int)
Expand Down

0 comments on commit 74d53e5

Please sign in to comment.