From fe986ee4a972f4bd5b7f9df3b728e4b50cb37c02 Mon Sep 17 00:00:00 2001 From: marc0olo Date: Thu, 13 Jul 2023 15:52:02 +0200 Subject: [PATCH] transfer_all tests, change in burning logic to allow anybody to burn if allowed by owner --- .../contracts/AENSWrapping.aes | 10 ++++-- .../contracts/test/AENSWrappingCustomTTL.aes | 10 ++++-- .../smart-contracts/test/aensWrappingTest.js | 35 +++++++++++++------ 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/development/smart-contracts/contracts/AENSWrapping.aes b/development/smart-contracts/contracts/AENSWrapping.aes index 85521ed..ea48a3a 100644 --- a/development/smart-contracts/contracts/AENSWrapping.aes +++ b/development/smart-contracts/contracts/AENSWrapping.aes @@ -30,7 +30,7 @@ main contract AENSWrapping : IAEX141, IAENSWrapping = , emergency_reward: int , emergency_reward_block_window: int , can_receive_from_others: bool - , burnable_if_empty: bool } + , burnable_if_expired_or_empty: bool } record state = { owner: address @@ -557,6 +557,11 @@ main contract AENSWrapping : IAEX141, IAENSWrapping = // internal helper functions + function require_burnable_if_expired_or_empty(nft_id: int, owner: address) = + switch(get_nft_config(nft_id, owner)) + None => () // allow burning if no config is set + Some(cfg) => require(cfg.burnable_if_expired_or_empty, "BURNING_NOT_ALLOWED") + function require_matching_pointer_limit(pointers: map(string, AENS.pointee)) = require(Map.size(pointers) =< 32, "POINTER_LIMIT_EXCEEDED") @@ -674,8 +679,9 @@ main contract AENSWrapping : IAEX141, IAENSWrapping = put(state{ approvals = Map.delete(token_id, state.approvals) }) stateful function __burn_single(token_id: int) = - let owner = require_authorized(token_id) + let owner = require_exists(token_id) require_expired_if_wrapped(token_id) + require_burnable_if_expired_or_empty(token_id, owner) __remove_approval(token_id) put(state{ balances[owner] @balance = balance - 1 , total_supply = state.total_supply - 1 diff --git a/development/smart-contracts/contracts/test/AENSWrappingCustomTTL.aes b/development/smart-contracts/contracts/test/AENSWrappingCustomTTL.aes index 935f1e3..4fd885f 100644 --- a/development/smart-contracts/contracts/test/AENSWrappingCustomTTL.aes +++ b/development/smart-contracts/contracts/test/AENSWrappingCustomTTL.aes @@ -30,7 +30,7 @@ main contract AENSWrappingCustomTTL : IAEX141, IAENSWrapping = , emergency_reward: int , emergency_reward_block_window: int , can_receive_from_others: bool - , burnable_if_empty: bool } + , burnable_if_expired_or_empty: bool } record state = { owner: address @@ -557,6 +557,11 @@ main contract AENSWrappingCustomTTL : IAEX141, IAENSWrapping = // internal helper functions + function require_burnable_if_expired_or_empty(nft_id: int, owner: address) = + switch(get_nft_config(nft_id, owner)) + None => () // allow burning if no config is set + Some(cfg) => require(cfg.burnable_if_expired_or_empty, "BURNING_NOT_ALLOWED") + function require_matching_pointer_limit(pointers: map(string, AENS.pointee)) = require(Map.size(pointers) =< 32, "POINTER_LIMIT_EXCEEDED") @@ -674,8 +679,9 @@ main contract AENSWrappingCustomTTL : IAEX141, IAENSWrapping = put(state{ approvals = Map.delete(token_id, state.approvals) }) stateful function __burn_single(token_id: int) = - let owner = require_authorized(token_id) + let owner = require_exists(token_id) require_expired_if_wrapped(token_id) + require_burnable_if_expired_or_empty(token_id, owner) __remove_approval(token_id) put(state{ balances[owner] @balance = balance - 1 , total_supply = state.total_supply - 1 diff --git a/development/smart-contracts/test/aensWrappingTest.js b/development/smart-contracts/test/aensWrappingTest.js index 026e484..492ff92 100644 --- a/development/smart-contracts/test/aensWrappingTest.js +++ b/development/smart-contracts/test/aensWrappingTest.js @@ -58,7 +58,7 @@ describe('AENSWrapping', () => { emergency_reward: 1_000_000n, emergency_reward_block_window: 179_900n, can_receive_from_others: true, - burnable_if_empty: false + burnable_if_expired_or_empty: false } before(async () => { @@ -657,7 +657,7 @@ describe('AENSWrapping', () => { emergency_reward: 1_000n, emergency_reward_block_window: 1n, can_receive_from_others: true, - burnable_if_empty: true + burnable_if_expired_or_empty: true } await contract.set_nft_config(1, nftConfig); @@ -1191,10 +1191,6 @@ describe('AENSWrapping', () => { contract.burn(tokenId + 1n)) .to.be.rejectedWith(`Invocation failed: "TOKEN_NOT_EXISTS"`); - await expect( - contract.burn(tokenId, { onAccount: otherAccount })) - .to.be.rejectedWith(`Invocation failed: "ONLY_OWNER_APPROVED_OR_OPERATOR_CALL_ALLOWED"`); - const wrapSingleTestName = "wrapSingleTestName.chain"; const preClaimTx = await aeSdk.aensPreclaim(wrapSingleTestName); await aeSdk.aensClaim(wrapSingleTestName, preClaimTx.salt); @@ -1204,6 +1200,17 @@ describe('AENSWrapping', () => { await expect( contract.burn(tokenId)) .to.be.rejectedWith(`Invocation failed: "WRAPPED_NAMES_NOT_EXPIRED"`); + + // set global config to disallow burning of empty NFTs + await contract.set_global_config(globalConfig); + const tokenIdToBurn = (await contract.mint(aeSdk.selectedAddress)).decodedResult; + await expect( + contract.burn(tokenIdToBurn)) + .to.be.rejectedWith(`Invocation failed: "BURNING_NOT_ALLOWED"`); + + // passes after removing global config even if executed from other account + await contract.remove_global_config(); + await contract.burn(tokenIdToBurn, { onAccount: otherAccount }); }); it('wrap_and_mint', async () => { @@ -1259,7 +1266,7 @@ describe('AENSWrapping', () => { .to.be.rejectedWith(`Invocation failed: "POINTER_LIMIT_EXCEEDED"`); }); - it('transfer_single & transfer_multiple', async () => { + it('transfer_single, transfer_multiple & transfer_all', async () => { await claimNames(aensNames); const namesDelegationSigs = await getDelegationSignatures(aensNames, contractId); const sourceTokenId = (await contract.wrap_and_mint(namesDelegationSigs)).decodedResult; @@ -1285,18 +1292,24 @@ describe('AENSWrapping', () => { emergency_reward: 1_000n, emergency_reward_block_window: 1n, can_receive_from_others: false, - burnable_if_empty: false + burnable_if_expired_or_empty: false } // mint second target nft and set nft config to disallow receiving names and overrule global cfg - const secondTargetNftId = (await contract.mint(otherAccount.address, undefined, undefined, { onAccount: otherAccount })).decodedResult; + const secondTargetNftId = (await contract.mint(otherAccount.address, undefined, undefined, { onAccount: otherAccount })).decodedResult; await contract.set_nft_config(secondTargetNftId, nftConfig, { onAccount: otherAccount }); await expect( contract.transfer_multiple(sourceTokenId, secondTargetNftId, aensNames.slice(1))) .to.be.rejectedWith(`Invocation failed: "RECEIVING_NAME_NOT_ALLOWED"`); - // passes because firstTargetNft can still receive - await contract.transfer_multiple(sourceTokenId, firstTargetNftId, aensNames.slice(1)); + const emptySourceNftId = (await contract.mint(aeSdk.selectedAddress)).decodedResult; + + await expect( + contract.transfer_all(emptySourceNftId, firstTargetNftId, aensNames.slice(1))) + .to.be.rejectedWith(`Invocation failed: "NO_NAMES_WRAPPED"`); + + // passes because sourceTokenId has names wrapped and firstTargetNft can still receive + await contract.transfer_all(sourceTokenId, firstTargetNftId, aensNames.slice(1)); }); });