diff --git a/src/ERC1155A.sol b/src/ERC1155A.sol index b3585ba..764c3ff 100644 --- a/src/ERC1155A.sol +++ b/src/ERC1155A.sol @@ -279,42 +279,43 @@ abstract contract ERC1155A is IERC1155A, IERC1155Errors { // -------------------- /// @inheritdoc IERC1155A - function transmuteToERC20(address owner, uint256 id, uint256 amount) external override { - if (owner == address(0)) revert ZERO_ADDRESS(); + function transmuteToERC20(address owner, uint256 id, uint256 amount, address receiver) external override { + if (owner == address(0) || receiver == address(0)) revert ZERO_ADDRESS(); /// @dev an approval is needed to burn _burn(owner, msg.sender, id, amount); address aERC20Token = aErc20TokenId[id]; if (aERC20Token == address(0)) revert AERC20_NOT_REGISTERED(); - IaERC20(aERC20Token).mint(owner, amount); - emit TransmutedToERC20(owner, id, amount); + IaERC20(aERC20Token).mint(receiver, amount); + emit TransmutedToERC20(owner, id, amount,receiver); } /// @inheritdoc IERC1155A - function transmuteToERC1155A(address owner, uint256 id, uint256 amount) external override { - if (owner == address(0)) revert ZERO_ADDRESS(); + function transmuteToERC1155A(address owner, uint256 id, uint256 amount, address receiver) external override { + if (owner == address(0) || receiver == address(0)) revert ZERO_ADDRESS(); address aERC20Token = aErc20TokenId[id]; if (aERC20Token == address(0)) revert AERC20_NOT_REGISTERED(); /// @dev an approval is needed to burn IaERC20(aERC20Token).burn(owner, msg.sender, amount); - _mint(owner, msg.sender, id, amount, EMPTY_BYTES); + _mint(receiver, msg.sender, id, amount, EMPTY_BYTES); - emit TransmutedToERC1155A(owner, id, amount); + emit TransmutedToERC1155A(owner, id, amount, receiver); } /// @inheritdoc IERC1155A function transmuteBatchToERC20( address owner, uint256[] calldata ids, - uint256[] calldata amounts + uint256[] calldata amounts, + address receiver ) external override { - if (owner == address(0)) revert ZERO_ADDRESS(); + if (owner == address(0) || receiver == address(0)) revert ZERO_ADDRESS(); uint256 idsLength = ids.length; // Saves MLOADs. if (idsLength != amounts.length) revert LENGTH_MISMATCH(); @@ -326,22 +327,23 @@ abstract contract ERC1155A is IERC1155A, IERC1155Errors { address aERC20Token = aErc20TokenId[ids[i]]; if (aERC20Token == address(0)) revert AERC20_NOT_REGISTERED(); - IaERC20(aERC20Token).mint(owner, amounts[i]); + IaERC20(aERC20Token).mint(receiver, amounts[i]); } - emit TransmutedBatchToERC20(owner, ids, amounts); + emit TransmutedBatchToERC20(owner, ids, amounts,receiver); } /// @inheritdoc IERC1155A function transmuteBatchToERC1155A( address owner, uint256[] calldata ids, - uint256[] calldata amounts + uint256[] calldata amounts, + address receiver ) external override { - if (owner == address(0)) revert ZERO_ADDRESS(); + if (owner == address(0) || receiver == address(0)) revert ZERO_ADDRESS(); uint256 idsLength = ids.length; // Saves MLOADs. if (idsLength != amounts.length) revert LENGTH_MISMATCH(); @@ -359,9 +361,9 @@ abstract contract ERC1155A is IERC1155A, IERC1155Errors { IaERC20(aERC20Token).burn(owner, msg.sender, amount); } - _batchMint(owner, msg.sender, ids, amounts, EMPTY_BYTES); + _batchMint(receiver, msg.sender, ids, amounts, EMPTY_BYTES); - emit TransmutedBatchToERC1155A(owner, ids, amounts); + emit TransmutedBatchToERC1155A(owner, ids, amounts, receiver); } // aERC20 Registration diff --git a/src/interfaces/IERC1155A.sol b/src/interfaces/IERC1155A.sol index f7de02d..852da4d 100644 --- a/src/interfaces/IERC1155A.sol +++ b/src/interfaces/IERC1155A.sol @@ -16,16 +16,16 @@ interface IERC1155A is IERC1155 { event ApprovalForOne(address indexed owner, address indexed spender, uint256 id, uint256 amount); /// @notice event emitted when an ERC1155A id is transmuted to an aERC20 - event TransmutedToERC20(address indexed user, uint256 id, uint256 amount); + event TransmutedToERC20(address indexed user, uint256 id, uint256 amount, address indexed receiver); /// @notice event emitted when an aERC20 is transmuted to an ERC1155 id - event TransmutedToERC1155A(address indexed user, uint256 id, uint256 amount); + event TransmutedToERC1155A(address indexed user, uint256 id, uint256 amount, address indexed receiver); /// @notice event emitted when multiple ERC1155A ids are transmuted to aERC20s - event TransmutedBatchToERC20(address indexed user, uint256[] ids, uint256[] amounts); + event TransmutedBatchToERC20(address indexed user, uint256[] ids, uint256[] amounts, address indexed receiver); /// @notice event emitted when multiple aERC20s are transmuted to ERC1155A ids - event TransmutedBatchToERC1155A(address indexed user, uint256[] ids, uint256[] amounts); + event TransmutedBatchToERC1155A(address indexed user, uint256[] ids, uint256[] amounts, address indexed receiver); ////////////////////////////////////////////////////////////// // ERRORS // @@ -142,28 +142,32 @@ interface IERC1155A is IERC1155 { external returns (bool); - /// @param onBehalfOf address of the user on whose behalf this transmutation is happening + /// @param owner address of the user on whose behalf this transmutation is happening /// @param id id of the ERC20s to transmute to aErc20 /// @param amount amount of the ERC20s to transmute to aErc20 - function transmuteToERC20(address onBehalfOf, uint256 id, uint256 amount) external; + /// @param receiver address of the user to receive the aErc20 token + function transmuteToERC20(address owner, uint256 id, uint256 amount, address receiver) external; - /// @param onBehalfOf address of the user on whose behalf this transmutation is happening + /// @param owner address of the user on whose behalf this transmutation is happening /// @param id id of the ERC20s to transmute to erc1155 /// @param amount amount of the ERC20s to transmute to erc1155 - function transmuteToERC1155A(address onBehalfOf, uint256 id, uint256 amount) external; + /// @param receiver address of the user to receive the erc1155 token id + function transmuteToERC1155A(address owner, uint256 id, uint256 amount, address receiver) external; /// @notice Use transmuteBatchToERC20 to transmute multiple ERC1155 ids into separate ERC20 /// Easier to transmute to 1155A than to transmute back to aErc20 because of ERC1155 beauty! - /// @param onBehalfOf address of the user on whose behalf this transmutation is happening + /// @param owner address of the user on whose behalf this transmutation is happening /// @param ids ids of the ERC1155A to transmute /// @param amounts amounts of the ERC1155A to transmute - function transmuteBatchToERC20(address onBehalfOf, uint256[] memory ids, uint256[] memory amounts) external; + /// @param receiver address of the user to receive the aErc20 tokens + function transmuteBatchToERC20(address owner, uint256[] memory ids, uint256[] memory amounts, address receiver) external; /// @notice Use transmuteBatchToERC1155A to transmute multiple ERC20 ids into separate ERC1155 - /// @param onBehalfOf address of the user on whose behalf this transmutation is happening + /// @param owner address of the user on whose behalf this transmutation is happening /// @param ids ids of the ERC20 to transmute /// @param amounts amounts of the ERC20 to transmute - function transmuteBatchToERC1155A(address onBehalfOf, uint256[] memory ids, uint256[] memory amounts) external; + /// @param receiver address of the user to receive the erc1155 token ids + function transmuteBatchToERC1155A(address owner, uint256[] memory ids, uint256[] memory amounts, address receiver) external; /// @notice payable to allow any implementing cross-chain protocol to be paid for fees for relaying this action to /// various chain diff --git a/src/test/ERC1155.t.sol b/src/test/ERC1155.t.sol index 0f26ec9..26b0e71 100644 --- a/src/test/ERC1155.t.sol +++ b/src/test/ERC1155.t.sol @@ -481,22 +481,22 @@ contract ERC1155Test is DSTestPlus, ERC1155TokenReceiver { function testFailZeroAddressTransmuteBatchToERC20() public { /// zero address check on transmuteBatchToERC20 - token.transmuteBatchToERC20(address(0), new uint256[](1), new uint256[](1)); + token.transmuteBatchToERC20(address(0), new uint256[](1), new uint256[](1), address(0)); } function testFailZeroAddressTransmuteBatchToERC1155A() public { /// zero address check on transmuteBatchToERC1155A - token.transmuteBatchToERC1155A(address(0), new uint256[](1), new uint256[](1)); + token.transmuteBatchToERC1155A(address(0), new uint256[](1), new uint256[](1), address(0)); } function testFailZeroAddressTransmuteToERC20() public { /// zero address check on transmuteToERC20 - token.transmuteToERC20(address(0), 1, 100); + token.transmuteToERC20(address(0), 1, 100, address(0)); } function testFailZeroAddressTransmuteToERC1155A() public { /// zero address check on transmuteToERC1155A - token.transmuteToERC1155A(address(0), 1, 100); + token.transmuteToERC1155A(address(0), 1, 100, address(0)); } function testFailArrayMismatchIncreaseAllowanceForMany() public { @@ -512,11 +512,11 @@ contract ERC1155Test is DSTestPlus, ERC1155TokenReceiver { } function testFailArrayMismatchTransmuteBatchToERC20() public { - token.transmuteBatchToERC20(address(0xBEEF), new uint256[](1), new uint256[](2)); + token.transmuteBatchToERC20(address(0xBEEF), new uint256[](1), new uint256[](2), address(0xBEEF)); } function testFailArrayMismatchTransmuteBatchToERC1155A() public { - token.transmuteBatchToERC1155A(address(0xBEEF), new uint256[](1), new uint256[](2)); + token.transmuteBatchToERC1155A(address(0xBEEF), new uint256[](1), new uint256[](2), address(0xBEEF)); } function testFailMintToNonERC155Recipient() public { diff --git a/src/test/ERC1155_A.t.sol b/src/test/ERC1155_A.t.sol index 00f8545..fa0a3c6 100644 --- a/src/test/ERC1155_A.t.sol +++ b/src/test/ERC1155_A.t.sol @@ -206,7 +206,7 @@ contract ERC1155ATest is Test { vm.stopPrank(); vm.startPrank(alice); - MockedERC1155A.transmuteToERC20(alice, id, THOUSAND_E18); + MockedERC1155A.transmuteToERC20(alice, id, THOUSAND_E18, alice); assertEq(MockedERC1155A.balanceOf(alice, id), 0); uint256 aERC20Balance = aERC20Token.balanceOf(alice); @@ -215,7 +215,7 @@ contract ERC1155ATest is Test { aERC20Token.approve(address(MockedERC1155A), aERC20Balance); /// NOTE: Test if 1:1 between 1155 and 20 always holds - MockedERC1155A.transmuteToERC1155A(alice, id, aERC20Balance); + MockedERC1155A.transmuteToERC1155A(alice, id, aERC20Balance, alice); assertEq(MockedERC1155A.balanceOf(alice, id), THOUSAND_E18); @@ -236,16 +236,16 @@ contract ERC1155ATest is Test { vm.startPrank(alice); vm.expectRevert(IERC1155A.AERC20_NOT_REGISTERED.selector); - MockedERC1155A.transmuteToERC20(alice, id, THOUSAND_E18); + MockedERC1155A.transmuteToERC20(alice, id, THOUSAND_E18, alice); vm.expectRevert(IERC1155A.AERC20_NOT_REGISTERED.selector); - MockedERC1155A.transmuteToERC1155A(alice, id, THOUSAND_E18); + MockedERC1155A.transmuteToERC1155A(alice, id, THOUSAND_E18, alice); vm.expectRevert(IERC1155A.AERC20_NOT_REGISTERED.selector); - MockedERC1155A.transmuteBatchToERC20(alice, ids, amounts); + MockedERC1155A.transmuteBatchToERC20(alice, ids, amounts, alice); vm.expectRevert(IERC1155A.AERC20_NOT_REGISTERED.selector); - MockedERC1155A.transmuteBatchToERC1155A(alice, ids, amounts); + MockedERC1155A.transmuteBatchToERC1155A(alice, ids, amounts, alice); } function testAERC20CreationSingleApprove() public { @@ -259,7 +259,7 @@ contract ERC1155ATest is Test { MockedERC1155A.setApprovalForOne(bob, id, THOUSAND_E18); vm.prank(bob); - MockedERC1155A.transmuteToERC20(alice, id, THOUSAND_E18); + MockedERC1155A.transmuteToERC20(alice, id, THOUSAND_E18, alice); assertEq(MockedERC1155A.balanceOf(alice, id), 0); assertEq(MockedERC1155A.allowance(alice, bob, id), 0); @@ -274,7 +274,7 @@ contract ERC1155ATest is Test { vm.prank(bob); /// NOTE: Test if 1:1 between 1155 and 20 always holds - MockedERC1155A.transmuteToERC1155A(alice, id, aERC20Balance); + MockedERC1155A.transmuteToERC1155A(alice, id, aERC20Balance, alice); assertEq(MockedERC1155A.balanceOf(alice, id), THOUSAND_E18); @@ -292,7 +292,7 @@ contract ERC1155ATest is Test { MockedERC1155A.setApprovalForOne(bob, id, THOUSAND_E18); vm.prank(bob); - MockedERC1155A.transmuteToERC20(alice, id, THOUSAND_E18); + MockedERC1155A.transmuteToERC20(alice, id, THOUSAND_E18, alice); assertEq(MockedERC1155A.balanceOf(alice, id), 0); assertEq(MockedERC1155A.allowance(alice, bob, id), 0); @@ -306,7 +306,7 @@ contract ERC1155ATest is Test { vm.prank(bob); vm.expectRevert(abi.encodeWithSelector(IERC20Errors.ERC20InsufficientAllowance.selector, bob, 0, aERC20Balance)); - MockedERC1155A.transmuteToERC1155A(alice, id, aERC20Balance); + MockedERC1155A.transmuteToERC1155A(alice, id, aERC20Balance, alice); } function testAERC20CreationBatch() public { @@ -328,7 +328,7 @@ contract ERC1155ATest is Test { amounts[0] = THOUSAND_E18; amounts[1] = THOUSAND_E18; - MockedERC1155A.transmuteBatchToERC20(alice, ids, amounts); + MockedERC1155A.transmuteBatchToERC20(alice, ids, amounts, alice); assertEq(MockedERC1155A.balanceOf(alice, ids[0]), 0); assertEq(MockedERC1155A.balanceOf(alice, ids[1]), 0); @@ -337,7 +337,7 @@ contract ERC1155ATest is Test { assertEq(aERC20Token2.balanceOf(alice), THOUSAND_E18); /// NOTE: Test if 1:1 between 1155 and 20 always holds - MockedERC1155A.transmuteBatchToERC1155A(alice, ids, amounts); + MockedERC1155A.transmuteBatchToERC1155A(alice, ids, amounts, alice); assertEq(MockedERC1155A.balanceOf(alice, ids[0]), THOUSAND_E18); assertEq(MockedERC1155A.balanceOf(alice, ids[1]), THOUSAND_E18); @@ -370,7 +370,7 @@ contract ERC1155ATest is Test { vm.prank(bob); - MockedERC1155A.transmuteBatchToERC20(alice, ids, amounts); + MockedERC1155A.transmuteBatchToERC20(alice, ids, amounts, alice); vm.startPrank(alice); assertEq(MockedERC1155A.balanceOf(alice, ids[0]), 0); @@ -387,7 +387,7 @@ contract ERC1155ATest is Test { vm.prank(bob); /// NOTE: Test if 1:1 between 1155 and 20 always holds - MockedERC1155A.transmuteBatchToERC1155A(alice, ids, amounts); + MockedERC1155A.transmuteBatchToERC1155A(alice, ids, amounts, alice); assertEq(MockedERC1155A.balanceOf(alice, ids[0]), THOUSAND_E18); assertEq(MockedERC1155A.balanceOf(alice, ids[1]), THOUSAND_E18); @@ -419,7 +419,7 @@ contract ERC1155ATest is Test { vm.prank(bob); - MockedERC1155A.transmuteBatchToERC20(alice, ids, amounts); + MockedERC1155A.transmuteBatchToERC20(alice, ids, amounts, alice); vm.startPrank(alice); assertEq(MockedERC1155A.balanceOf(alice, ids[0]), 0); @@ -434,7 +434,7 @@ contract ERC1155ATest is Test { vm.prank(bob); vm.expectRevert(abi.encodeWithSelector(IERC20Errors.ERC20InsufficientAllowance.selector, bob, 0, THOUSAND_E18)); /// NOTE: Test if 1:1 between 1155 and 20 always holds - MockedERC1155A.transmuteBatchToERC1155A(alice, ids, amounts); + MockedERC1155A.transmuteBatchToERC1155A(alice, ids, amounts, alice); } function testAERC20CreationrAlreadyRegistered() public { @@ -445,4 +445,39 @@ contract ERC1155ATest is Test { vm.expectRevert(IERC1155A.AERC20_ALREADY_REGISTERED.selector); aERC20Token = aERC20(MockedERC1155A.registerAERC20(1)); } + + function testAERC20TransmuteRoundTripToReceiver() public { + vm.startPrank(deployer); + uint256 id = 3; + MockedERC1155A.mint(alice, id, THOUSAND_E18, ""); + aERC20 aERC20Token = aERC20(MockedERC1155A.registerAERC20(id)); + vm.stopPrank(); + vm.startPrank(alice); + + MockedERC1155A.transmuteToERC20(alice, id, THOUSAND_E18, bob); + assertEq(MockedERC1155A.balanceOf(alice, id), 0); + assertEq(MockedERC1155A.balanceOf(bob, id), 0); + + uint256 aliceaERC20Balance = aERC20Token.balanceOf(alice); + assertEq(aliceaERC20Balance, 0); + + uint256 bobaERC20Balance = aERC20Token.balanceOf(bob); + assertEq(bobaERC20Balance, THOUSAND_E18); + + vm.stopPrank(); + + vm.startPrank(bob); + + aERC20Token.approve(address(MockedERC1155A), bobaERC20Balance); + + /// NOTE: Test if 1:1 between 1155 and 20 always holds + MockedERC1155A.transmuteToERC1155A(bob, id, bobaERC20Balance, alice); + + assertEq(MockedERC1155A.balanceOf(alice, id), THOUSAND_E18); + assertEq(MockedERC1155A.balanceOf(bob, id), 0); + + assertEq(aERC20Token.balanceOf(address(bob)), 0); + assertEq(aERC20Token.balanceOf(address(alice)), 0); + vm.stopPrank(); + } }