--- a/pallets/refungible/src/lib.rs +++ b/pallets/refungible/src/lib.rs @@ -399,6 +399,7 @@ pub fn burn_token_unchecked( collection: &RefungibleHandle, + owner: &T::CrossAccountId, token_id: TokenId, ) -> DispatchResult { let burnt = >::get(collection.id) @@ -411,7 +412,15 @@ >::remove((collection.id, token_id)); >::remove_prefix((collection.id, token_id), None); >::remove_prefix((collection.id, token_id), None); - // TODO: ERC721 transfer event + + >::deposit_log( + ERC721Events::Transfer { + from: *owner.as_eth(), + to: H160::default(), + token_id: token_id.into(), + } + .to_log(collection_id_to_address(collection.id)), + ); Ok(()) } @@ -453,12 +462,12 @@ >::remove((collection.id, owner, token)); >::unnest_if_nested(owner, collection.id, token); >::insert((collection.id, owner), account_balance); - Self::burn_token_unchecked(collection, token)?; + Self::burn_token_unchecked(collection, owner, token)?; >::deposit_log( - ERC721Events::Transfer { + ERC20Events::Transfer { from: *owner.as_eth(), to: H160::default(), - token_id: token.into(), + value: amount.into(), } .to_log(collection_id_to_address(collection.id)), ); @@ -490,6 +499,17 @@ >::unnest_if_nested(owner, collection.id, token); >::remove((collection.id, token, owner)); >::insert((collection.id, owner), account_balance); + + if let Some(user) = Self::token_owner(collection.id, token) { + >::deposit_log( + ERC721Events::Transfer { + from: erc::ADDRESS_FOR_PARTIALLY_OWNED_TOKENS, + to: *user.as_eth(), + token_id: token.into(), + } + .to_log(collection_id_to_address(collection.id)), + ); + } } else { >::insert((collection.id, token, owner), balance); } @@ -981,30 +1001,46 @@ for (i, token) in data.into_iter().enumerate() { let token_id = first_token_id + i as u32 + 1; - for (user, amount) in token.users.into_iter() { - if amount == 0 { - continue; - } + let receivers = token + .users + .into_iter() + .filter(|(_, amount)| *amount > 0) + .collect::>(); + if let [(user, _)] = receivers.as_slice() { + // if there is exactly one receiver >::deposit_log( - ERC20Events::Transfer { + ERC721Events::Transfer { from: H160::default(), to: *user.as_eth(), - value: amount.into(), + token_id: token_id.into(), } - .to_log(T::EvmTokenAddressMapping::token_to_address( - collection.id, - TokenId(token_id), - )), + .to_log(collection_id_to_address(collection.id)), ); + } else if let [_, ..] = receivers.as_slice() { + // if there is more than one receiver >::deposit_log( ERC721Events::Transfer { from: H160::default(), - to: *user.as_eth(), + to: erc::ADDRESS_FOR_PARTIALLY_OWNED_TOKENS, token_id: token_id.into(), } .to_log(collection_id_to_address(collection.id)), ); + } + + for (user, amount) in receivers.into_iter() { + >::deposit_log( + ERC20Events::Transfer { + from: H160::default(), + to: *user.as_eth(), + value: amount.into(), + } + .to_log(T::EvmTokenAddressMapping::token_to_address( + collection.id, + TokenId(token_id), + )), + ); >::deposit_event(CommonEvent::ItemCreated( collection.id, TokenId(token_id), --- a/tests/src/eth/reFungible.test.ts +++ b/tests/src/eth/reFungible.test.ts @@ -15,7 +15,7 @@ // along with Unique Network. If not, see . import {createCollectionExpectSuccess, transfer, UNIQUE} from '../util/helpers'; -import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, evmCollection, evmCollectionHelpers, GAS_ARGS, getCollectionAddressFromResult, itWeb3, normalizeEvents, recordEthFee, tokenIdToAddress} from './util/helpers'; +import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, evmCollection, evmCollectionHelpers, GAS_ARGS, getCollectionAddressFromResult, itWeb3, normalizeEvents, recordEthFee, recordEvents, tokenIdToAddress} from './util/helpers'; import reFungibleAbi from './reFungibleAbi.json'; import reFungibleTokenAbi from './reFungibleTokenAbi.json'; import {expect} from 'chai'; @@ -224,8 +224,7 @@ { const result = await contract.methods.burn(tokenId).send(); const events = normalizeEvents(result.events); - - expect(events).to.be.deep.equal([ + expect(events).to.include.deep.members([ { address: collectionIdAddress, event: 'Transfer', @@ -333,10 +332,8 @@ await tokenContract.methods.repartition(2).send(); await tokenContract.methods.transfer(receiver, 1).send(); - let transfer; - contract.events.Transfer({}, function(_error: any, event: any){ transfer = event;}); - await tokenContract.methods.transfer(receiver, 1).send(); - const events = normalizeEvents([transfer]); + const events = await recordEvents(contract, async () => + await tokenContract.methods.transfer(receiver, 1).send()); expect(events).to.deep.equal([ { address: collectionIdAddress, @@ -366,11 +363,9 @@ await tokenContract.methods.repartition(2).send(); - let transfer; - contract.events.Transfer({}, function(_error: any, event: any){ transfer = event;}); - await tokenContract.methods.transfer(receiver, 1).send(); - - const events = normalizeEvents([transfer]); + const events = await recordEvents(contract, async () => + await tokenContract.methods.transfer(receiver, 1).send()); + expect(events).to.deep.equal([ { address: collectionIdAddress, --- a/tests/src/eth/reFungibleToken.test.ts +++ b/tests/src/eth/reFungibleToken.test.ts @@ -277,7 +277,7 @@ { const result = await contract.methods.transferFrom(owner, receiver, 49).send({from: spender}); const events = normalizeEvents(result.events); - expect(events).to.be.deep.equal([ + expect(events).to.include.deep.members([ { address, event: 'Transfer', @@ -329,7 +329,7 @@ { const result = await contract.methods.transfer(receiver, 50).send({from: owner}); const events = normalizeEvents(result.events); - expect(events).to.be.deep.equal([ + expect(events).to.include.deep.members([ { address, event: 'Transfer', @@ -442,6 +442,38 @@ }, ]); }); + + itWeb3('Receiving Transfer event on burning into full ownership', async ({web3, api, privateKeyWrapper}) => { + const caller = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const receiver = await createEthAccountWithBalance(api, web3, privateKeyWrapper); + const helper = evmCollectionHelpers(web3, caller); + const result = await helper.methods.createRefungibleCollection('Mint collection', '6', '6').send(); + const {collectionIdAddress, collectionId} = await getCollectionAddressFromResult(api, result); + const contract = evmCollection(web3, caller, collectionIdAddress, {type: 'ReFungible'}); + + const tokenId = await contract.methods.nextTokenId().call(); + await contract.methods.mint(caller, tokenId).send(); + + const address = tokenIdToAddress(collectionId, tokenId); + + const tokenContract = new web3.eth.Contract(reFungibleTokenAbi as any, address, {from: caller, ...GAS_ARGS}); + await tokenContract.methods.repartition(2).send(); + await tokenContract.methods.transfer(receiver, 1).send(); + + const events = await recordEvents(contract, async () => + await tokenContract.methods.burnFrom(caller, 1).send()); + expect(events).to.deep.equal([ + { + address: collectionIdAddress, + event: 'Transfer', + args: { + from: '0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF', + to: receiver, + tokenId, + }, + }, + ]); + }); }); describe('Refungible: Fees', () => {