From 9cb16d9a84e8d9f89f5f4a7fdfbf7ec39f1756b6 Mon Sep 17 00:00:00 2001 From: dcota <32775237+DaigaroCota@users.noreply.github.com> Date: Mon, 5 Feb 2024 22:22:49 -0800 Subject: [PATCH 1/2] fix: add token uri and memoir to release key --- src/MgdL2NFTEscrow.sol | 44 ++++++++++++++++++++++---------- src/voucher/Mgd1155L2Voucher.sol | 7 ++--- src/voucher/Mgd721L2Voucher.sol | 7 ++--- src/voucher/MgdL2BaseNFT.sol | 2 +- src/voucher/MgdL2BaseVoucher.sol | 15 +++++++++-- 5 files changed, 53 insertions(+), 22 deletions(-) diff --git a/src/MgdL2NFTEscrow.sol b/src/MgdL2NFTEscrow.sol index 1ca6307..bc24220 100644 --- a/src/MgdL2NFTEscrow.sol +++ b/src/MgdL2NFTEscrow.sol @@ -140,7 +140,7 @@ contract MgdL2NFTEscrow is Initializable, IERC721Receiver, IERC1155Receiver { /// @param nft token address to redeem /// @param tokenId to redeeem /// @param amount to redeem - /// @param owner who will receive nft + /// @param receiver who will receive nft /// @param blockHash of tx in L2 where {MgdL2NFTVoucher.redeemVoucherToL1(...)} was called /// @param marketData status of voucher when redeem call was initiated in L2 function getRedeemClearanceKey( @@ -148,16 +148,19 @@ contract MgdL2NFTEscrow is Initializable, IERC721Receiver, IERC1155Receiver { address nft, uint256 tokenId, uint256 amount, - address owner, + address receiver, bytes32 blockHash, - MgdL1MarketData calldata marketData + MgdL1MarketData calldata marketData, + string memory tokenURI, + bytes memory tokenIdMemoir ) external view returns (bool) { - uint256 key = - _generateL1RedeemKey(voucherId, nft, tokenId, amount, owner, blockHash, marketData); + uint256 key = _generateL1RedeemKey( + voucherId, nft, tokenId, amount, receiver, blockHash, marketData, tokenURI, tokenIdMemoir + ); return redeemClearance[key]; } @@ -180,13 +183,14 @@ contract MgdL2NFTEscrow is Initializable, IERC721Receiver, IERC1155Receiver { address receiver, bytes32 blockHash, MgdL1MarketData calldata marketData, - string calldata tokenURI, - bytes calldata memoir + string memory tokenURI, + bytes memory memoir ) external { - uint256 key = - _generateL1RedeemKey(voucherId, nft, tokenId, amount, receiver, blockHash, marketData); + uint256 key = _generateL1RedeemKey( + voucherId, nft, tokenId, amount, receiver, blockHash, marketData, tokenURI, memoir + ); if (!redeemClearance[key]) { revert MgdL2NFTEscrow__releaseFromEscrow_notClearedOrAlreadyReleased(); } @@ -326,16 +330,30 @@ contract MgdL2NFTEscrow is Initializable, IERC721Receiver, IERC1155Receiver { address nft, uint256 tokenId, uint256 amount, - address owner, + address receiver, bytes32 blockHash, - MgdL1MarketData calldata marketData + MgdL1MarketData calldata marketData, + string memory tokenURI, + bytes memory tokenIdMemoir ) internal pure returns (uint256 key) { - key = - uint256(keccak256(abi.encode(voucherId, nft, tokenId, amount, owner, blockHash, marketData))); + if (tokenId == _REF_NUMBER) { + bytes32 hashedUriMemoir = keccak256(abi.encode(tokenURI, tokenIdMemoir)); + key = uint256( + keccak256( + abi.encode( + voucherId, nft, tokenId, amount, receiver, blockHash, marketData, hashedUriMemoir + ) + ) + ); + } else { + key = uint256( + keccak256(abi.encode(voucherId, nft, tokenId, amount, receiver, blockHash, marketData)) + ); + } } function _sendEscrowNoticeToL2(uint256 voucherId, bool state, TypeNFT nftType) internal { diff --git a/src/voucher/Mgd1155L2Voucher.sol b/src/voucher/Mgd1155L2Voucher.sol index c703a5e..a28889a 100644 --- a/src/voucher/Mgd1155L2Voucher.sol +++ b/src/voucher/Mgd1155L2Voucher.sol @@ -245,11 +245,12 @@ contract Mgd1155L2Voucher is MgdL2BaseVoucher, ERC1155Permit, Almost1155Upgradea _mint(owner, voucherId, representedAmount, ""); _voucherMarketData[voucherId] = marketData; - if (memoir.length > 0) { - _tokenIdMemoir[voucherId] = memoir; - } if (generatedL1VoucherId == 0) { + _tokenURIs[voucherId] = tokenURI; + if (memoir.length > 0) { + _tokenIdMemoir[voucherId] = memoir; + } emit MintGoldDustNFTMinted( voucherId, tokenURI, diff --git a/src/voucher/Mgd721L2Voucher.sol b/src/voucher/Mgd721L2Voucher.sol index 0d2983c..156d495 100644 --- a/src/voucher/Mgd721L2Voucher.sol +++ b/src/voucher/Mgd721L2Voucher.sol @@ -222,11 +222,12 @@ contract Mgd721L2Voucher is MgdL2BaseVoucher, ERC721Permit, Almost721Upgradeable _safeMint(owner, voucherId); _voucherMarketData[voucherId] = marketData; - if (memoir.length > 0) { - _tokenIdMemoir[voucherId] = memoir; - } if (generatedL1VoucherId == 0) { + _tokenURIs[voucherId] = tokenURI; + if (memoir.length > 0) { + _tokenIdMemoir[voucherId] = memoir; + } emit MintGoldDustNFTMinted( voucherId, tokenURI, diff --git a/src/voucher/MgdL2BaseNFT.sol b/src/voucher/MgdL2BaseNFT.sol index 47e93f6..36f4877 100644 --- a/src/voucher/MgdL2BaseNFT.sol +++ b/src/voucher/MgdL2BaseNFT.sol @@ -49,7 +49,7 @@ abstract contract MgdL2BaseNFT is Initializable, PausableUpgradeable, Reentrancy // voucherId => struct MgdL1MarketData mapping(uint256 => MgdL1MarketData) internal _voucherMarketData; - + mapping(uint256 => string) internal _tokenURIs; mapping(uint256 => bytes) internal _tokenIdMemoir; /** diff --git a/src/voucher/MgdL2BaseVoucher.sol b/src/voucher/MgdL2BaseVoucher.sol index 28882fe..44ee4a0 100644 --- a/src/voucher/MgdL2BaseVoucher.sol +++ b/src/voucher/MgdL2BaseVoucher.sol @@ -287,8 +287,19 @@ abstract contract MgdL2BaseVoucher is MgdL2BaseNFT { returns (uint256 key, bytes32 blockHash) { blockHash = blockhash(block.number); - key = - uint256(keccak256(abi.encode(voucherId, nft, tokenId, amount, owner, blockHash, marketData))); + if (tokenId == _REF_NUMBER) { + bytes32 hashedUriMemoir = + keccak256(abi.encode(_tokenURIs[voucherId], _tokenIdMemoir[voucherId])); + key = uint256( + keccak256( + abi.encode(voucherId, nft, tokenId, amount, owner, blockHash, marketData, hashedUriMemoir) + ) + ); + } else { + key = uint256( + keccak256(abi.encode(voucherId, nft, tokenId, amount, owner, blockHash, marketData)) + ); + } } function _clearVoucherData(uint256 voucherId) internal { From 168e4d9b26ec2a1419c0ef948fbdf19e47cbd357 Mon Sep 17 00:00:00 2001 From: dcota <32775237+DaigaroCota@users.noreply.github.com> Date: Mon, 5 Feb 2024 22:23:18 -0800 Subject: [PATCH 2/2] chore: update test for release key generated with token uri and memoir --- test/foundry/RedeemingVouchersTests.t.sol | 82 ++++++++++++++++------- test/foundry/utils/Helpers.t.sol | 24 +++++-- 2 files changed, 76 insertions(+), 30 deletions(-) diff --git a/test/foundry/RedeemingVouchersTests.t.sol b/test/foundry/RedeemingVouchersTests.t.sol index e718be3..bd68904 100644 --- a/test/foundry/RedeemingVouchersTests.t.sol +++ b/test/foundry/RedeemingVouchersTests.t.sol @@ -70,7 +70,7 @@ contract RedeemingVoucherTests is CommonSigners, BaseL2Constants, MgdTestConstan /// Local constants: mock data to mint NFTs string private constant _TOKEN_URI = "https://ipfs.nowhere.example/"; uint256 private constant _ROYALTY_PERCENT = 10; - string private constant _MEMOIR = "A memoir"; + bytes private constant _MEMOIR = bytes("A memoir"); uint40 private constant _EDITIONS = 5; address[4] private _COLLABS = [address(0), address(0), address(0), address(0)]; uint256[5] private _COLLABS_PERCENTAGE = [0, 0, 0, 0, 0]; @@ -193,11 +193,10 @@ contract RedeemingVoucherTests is CommonSigners, BaseL2Constants, MgdTestConstan // 8.- Mint NFTs: 2 on Ethereum and 2 on L2; 1 each for 721 and 1155 vm.startPrank(Bob.addr); - _721tokenId = nft721.mintNft(_TOKEN_URI, _ROYALTY_PERCENT, 1, bytes(_MEMOIR)); - _1155tokenId = nft1155.mintNft(_TOKEN_URI, _ROYALTY_PERCENT, _EDITIONS, bytes(_MEMOIR)); - nativeVoucherIdFor721 = l2voucher721.mintNft(_TOKEN_URI, _ROYALTY_PERCENT, 1, bytes(_MEMOIR)); - nativeVoucherIdFor1155 = - l2voucher1155.mintNft(_TOKEN_URI, _ROYALTY_PERCENT, _EDITIONS, bytes(_MEMOIR)); + _721tokenId = nft721.mintNft(_TOKEN_URI, _ROYALTY_PERCENT, 1, _MEMOIR); + _1155tokenId = nft1155.mintNft(_TOKEN_URI, _ROYALTY_PERCENT, _EDITIONS, _MEMOIR); + nativeVoucherIdFor721 = l2voucher721.mintNft(_TOKEN_URI, _ROYALTY_PERCENT, 1, _MEMOIR); + nativeVoucherIdFor1155 = l2voucher1155.mintNft(_TOKEN_URI, _ROYALTY_PERCENT, _EDITIONS, _MEMOIR); // 9.- Transfer NFTs to escrow vm.recordLogs(); @@ -338,7 +337,7 @@ contract RedeemingVoucherTests is CommonSigners, BaseL2Constants, MgdTestConstan function test_redeemVoucher721EventsAndRedeemKey() public { MgdL1MarketData memory marketData = l2voucher721.getVoucherMarketData(_721VId); (uint256 expectedRedeemKey, bytes32 blockHash) = - generate_L1RedeemKey(_721VId, address(nft721), _721tokenId, 1, Bob.addr, marketData); + generate_L1RedeemKey(_721VId, address(nft721), _721tokenId, 1, Bob.addr, marketData, "", ""); bytes memory message = abi.encodeWithSelector(MgdL2NFTEscrow.setRedeemClearanceKey.selector, expectedRedeemKey, true); @@ -358,7 +357,7 @@ contract RedeemingVoucherTests is CommonSigners, BaseL2Constants, MgdTestConstan function test_redeemVoucher1155EventsAndRedeemKey() public { MgdL1MarketData memory marketData = l2voucher1155.getVoucherMarketData(_1155VId); (uint256 expectedRedeemKey, bytes32 blockHash) = generate_L1RedeemKey( - _1155VId, address(nft1155), _1155tokenId, _EDITIONS, Bob.addr, marketData + _1155VId, address(nft1155), _1155tokenId, _EDITIONS, Bob.addr, marketData, "", "" ); bytes memory message = @@ -386,10 +385,11 @@ contract RedeemingVoucherTests is CommonSigners, BaseL2Constants, MgdTestConstan function test_settingClearanceToRedeem() public { MgdL1MarketData memory market721Data = l2voucher721.getVoucherMarketData(_721VId); MgdL1MarketData memory market1155Data = l2voucher1155.getVoucherMarketData(_1155VId); - (uint256 redeem721Key, bytes32 block1155Hash) = - generate_L1RedeemKey(_721VId, address(nft721), _721tokenId, 1, Bob.addr, market721Data); + (uint256 redeem721Key, bytes32 block1155Hash) = generate_L1RedeemKey( + _721VId, address(nft721), _721tokenId, 1, Bob.addr, market721Data, "", "" + ); (uint256 redeem1155Key, bytes32 block721Hash) = generate_L1RedeemKey( - _1155VId, address(nft1155), _1155tokenId, _EDITIONS, Bob.addr, market1155Data + _1155VId, address(nft1155), _1155tokenId, _EDITIONS, Bob.addr, market1155Data, "", "" ); vm.startPrank(L1_CROSSDOMAIN_MESSENGER); escrow.setRedeemClearanceKey(redeem721Key, true); @@ -397,13 +397,21 @@ contract RedeemingVoucherTests is CommonSigners, BaseL2Constants, MgdTestConstan vm.stopPrank(); assertEq( escrow.getRedeemClearanceKey( - _721VId, address(nft721), _721tokenId, 1, Bob.addr, block1155Hash, market721Data + _721VId, address(nft721), _721tokenId, 1, Bob.addr, block1155Hash, market721Data, "", "" ), true ); assertEq( escrow.getRedeemClearanceKey( - _1155VId, address(nft1155), _1155tokenId, _EDITIONS, Bob.addr, block721Hash, market1155Data + _1155VId, + address(nft1155), + _1155tokenId, + _EDITIONS, + Bob.addr, + block721Hash, + market1155Data, + "", + "" ), true ); @@ -412,7 +420,7 @@ contract RedeemingVoucherTests is CommonSigners, BaseL2Constants, MgdTestConstan function test_clearanceToRedeemEvents() public { MgdL1MarketData memory marketData = l2voucher721.getVoucherMarketData(_721VId); (uint256 redeemKey,) = - generate_L1RedeemKey(_721VId, address(nft721), _721tokenId, 1, Bob.addr, marketData); + generate_L1RedeemKey(_721VId, address(nft721), _721tokenId, 1, Bob.addr, marketData, "", ""); vm.prank(L1_CROSSDOMAIN_MESSENGER); vm.expectEmit(true, false, false, true, address(escrow)); emit RedeemClearanceKey(redeemKey, true); @@ -423,10 +431,11 @@ contract RedeemingVoucherTests is CommonSigners, BaseL2Constants, MgdTestConstan vm.assume(foe != address(0) && foe != L1_CROSSDOMAIN_MESSENGER && foe != company.owner()); MgdL1MarketData memory market721Data = l2voucher721.getVoucherMarketData(_721VId); MgdL1MarketData memory market1155Data = l2voucher1155.getVoucherMarketData(_1155VId); - (uint256 redeem721Key,) = - generate_L1RedeemKey(_721VId, address(nft721), _721tokenId, 1, Bob.addr, market721Data); + (uint256 redeem721Key,) = generate_L1RedeemKey( + _721VId, address(nft721), _721tokenId, 1, Bob.addr, market721Data, "", "" + ); (uint256 redeem1155Key,) = generate_L1RedeemKey( - _1155VId, address(nft1155), _1155tokenId, _EDITIONS, Bob.addr, market1155Data + _1155VId, address(nft1155), _1155tokenId, _EDITIONS, Bob.addr, market1155Data, "", "" ); vm.startPrank(foe); vm.expectRevert(MgdL2NFTEscrow.MgdL2NFTEscrow__onlyCrossAuthorized_notAllowed.selector); @@ -439,7 +448,7 @@ contract RedeemingVoucherTests is CommonSigners, BaseL2Constants, MgdTestConstan function test_releaseFromEscrow721AndEvents() public { MgdL1MarketData memory marketData = l2voucher721.getVoucherMarketData(_721VId); (uint256 redeemKey, bytes32 blockHash) = - generate_L1RedeemKey(_721VId, address(nft721), _721tokenId, 1, Bob.addr, marketData); + generate_L1RedeemKey(_721VId, address(nft721), _721tokenId, 1, Bob.addr, marketData, "", ""); vm.prank(L1_CROSSDOMAIN_MESSENGER); escrow.setRedeemClearanceKey(redeemKey, true); vm.prank(Bob.addr); @@ -455,7 +464,14 @@ contract RedeemingVoucherTests is CommonSigners, BaseL2Constants, MgdTestConstan function test_releaseL2NativeFromEscrow721AndEvents() public { MgdL1MarketData memory marketData = l2voucher721.getVoucherMarketData(nativeVoucherIdFor721); (uint256 redeemKey, bytes32 blockHash) = generate_L1RedeemKey( - nativeVoucherIdFor721, address(nft721), REF_NUMBER, 1, Bob.addr, marketData + nativeVoucherIdFor721, + address(nft721), + REF_NUMBER, + 1, + Bob.addr, + marketData, + _TOKEN_URI, + _MEMOIR ); uint256 newTokenId = nft721._tokenIds() + 1; vm.prank(L1_CROSSDOMAIN_MESSENGER); @@ -473,7 +489,7 @@ contract RedeemingVoucherTests is CommonSigners, BaseL2Constants, MgdTestConstan blockHash, marketData, _TOKEN_URI, - bytes(_MEMOIR) + _MEMOIR ); VmSafe.Log[] memory entries721 = vm.getRecordedLogs(); uint256 resultNewTokenId = uint256(entries721[2].topics[1]); @@ -485,7 +501,7 @@ contract RedeemingVoucherTests is CommonSigners, BaseL2Constants, MgdTestConstan function test_releaseFromEscrow1155AndEvents() public { MgdL1MarketData memory marketData = l2voucher1155.getVoucherMarketData(_1155VId); (uint256 redeemKey, bytes32 blockHash) = generate_L1RedeemKey( - _1155VId, address(nft1155), _1155tokenId, _EDITIONS, Bob.addr, marketData + _1155VId, address(nft1155), _1155tokenId, _EDITIONS, Bob.addr, marketData, "", "" ); vm.prank(L1_CROSSDOMAIN_MESSENGER); escrow.setRedeemClearanceKey(redeemKey, true); @@ -505,7 +521,7 @@ contract RedeemingVoucherTests is CommonSigners, BaseL2Constants, MgdTestConstan // We override marketdata to simulate actions in L2 marketData.primarySaleL2QuantityToSell = partialAmount; (uint256 redeemKey, bytes32 blockHash) = generate_L1RedeemKey( - _1155VId, address(nft1155), _1155tokenId, partialAmount, Bob.addr, marketData + _1155VId, address(nft1155), _1155tokenId, partialAmount, Bob.addr, marketData, "", "" ); vm.prank(L1_CROSSDOMAIN_MESSENGER); escrow.setRedeemClearanceKey(redeemKey, true); @@ -532,7 +548,14 @@ contract RedeemingVoucherTests is CommonSigners, BaseL2Constants, MgdTestConstan function test_releaseL2NativeFromEscrow1155() public { MgdL1MarketData memory marketData = l2voucher1155.getVoucherMarketData(nativeVoucherIdFor1155); (uint256 redeemKey, bytes32 blockHash) = generate_L1RedeemKey( - nativeVoucherIdFor1155, address(nft1155), REF_NUMBER, _EDITIONS, Bob.addr, marketData + nativeVoucherIdFor1155, + address(nft1155), + REF_NUMBER, + _EDITIONS, + Bob.addr, + marketData, + _TOKEN_URI, + _MEMOIR ); uint256 newTokenId = nft1155._tokenIds() + 1; vm.prank(L1_CROSSDOMAIN_MESSENGER); @@ -552,7 +575,7 @@ contract RedeemingVoucherTests is CommonSigners, BaseL2Constants, MgdTestConstan blockHash, marketData, _TOKEN_URI, - bytes(_MEMOIR) + _MEMOIR ); VmSafe.Log[] memory entries1155 = vm.getRecordedLogs(); uint256 resultNewTokenId = uint256(entries1155[2].topics[1]); @@ -567,7 +590,14 @@ contract RedeemingVoucherTests is CommonSigners, BaseL2Constants, MgdTestConstan // Replace partialAmount in marketData to simulate actions in L2 marketData.primarySaleL2QuantityToSell = partialAmount; (uint256 redeemKey, bytes32 blockHash) = generate_L1RedeemKey( - nativeVoucherIdFor1155, address(nft1155), REF_NUMBER, partialAmount, Bob.addr, marketData + nativeVoucherIdFor1155, + address(nft1155), + REF_NUMBER, + partialAmount, + Bob.addr, + marketData, + _TOKEN_URI, + _MEMOIR ); uint256 newTokenId = nft1155._tokenIds() + 1; vm.prank(L1_CROSSDOMAIN_MESSENGER); @@ -587,7 +617,7 @@ contract RedeemingVoucherTests is CommonSigners, BaseL2Constants, MgdTestConstan blockHash, marketData, _TOKEN_URI, - bytes(_MEMOIR) + _MEMOIR ); VmSafe.Log[] memory entries1155 = vm.getRecordedLogs(); uint256 resultNewTokenId = uint256(entries1155[2].topics[1]); diff --git a/test/foundry/utils/Helpers.t.sol b/test/foundry/utils/Helpers.t.sol index 3f0007b..fd2e6a4 100644 --- a/test/foundry/utils/Helpers.t.sol +++ b/test/foundry/utils/Helpers.t.sol @@ -5,6 +5,9 @@ import {Test} from "forge-std/Test.sol"; import {MgdL1MarketData} from "../../../src/MgdL2NFTEscrow.sol"; contract Helpers is Test { + uint256 private constant _REF_NUMBER = + 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; + function generate_packedSignature( bytes32 digest, uint256 signerPrivKey @@ -57,15 +60,28 @@ contract Helpers is Test { uint256 tokenId, uint256 amount, address receiver, - MgdL1MarketData memory marketData + MgdL1MarketData memory marketData, + string memory tokenURI, + bytes memory tokenIdMemoir ) internal view returns (uint256 key, bytes32 blockHash) { blockHash = blockhash(block.number); - key = uint256( - keccak256(abi.encode(voucherId, nft, tokenId, amount, receiver, blockHash, marketData)) - ); + if (tokenId == _REF_NUMBER) { + bytes32 hashedUriMemoir = keccak256(abi.encode(tokenURI, tokenIdMemoir)); + key = uint256( + keccak256( + abi.encode( + voucherId, nft, tokenId, amount, receiver, blockHash, marketData, hashedUriMemoir + ) + ) + ); + } else { + key = uint256( + keccak256(abi.encode(voucherId, nft, tokenId, amount, receiver, blockHash, marketData)) + ); + } } }