From cf629b1dcf9c04a1395346c99279902f118248e2 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 28 Mar 2024 19:21:06 +0700 Subject: [PATCH 01/49] feat: added kakarot rpc --- hardhat.config.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hardhat.config.ts b/hardhat.config.ts index 513be85..5932a6c 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -26,6 +26,10 @@ const config: HardhatUserConfig = { /** * @dev Testnets */ + kakarot: { + url: `${process.env.KAKAROT_RPC_URL}`, + accounts: [`${DEPLOYER_PRIVATE_KEY}`], + }, sepolia: { url: `${process.env.SEPOLIA_RPC_URL}`, accounts: [`${DEPLOYER_PRIVATE_KEY}`], From 8655daad12ea358b0658ee39610edd89fccb002a Mon Sep 17 00:00:00 2001 From: 0xneves Date: Mon, 1 Apr 2024 20:45:33 +0700 Subject: [PATCH 02/49] fix: before moved out of context and changed into beforeEach in canceling swaps --- test/TestSwaplace.test.ts | 58 ++++++++++----------------------------- 1 file changed, 15 insertions(+), 43 deletions(-) diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index 76bcdc0..b6af0f6 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -445,13 +445,13 @@ describe("Swaplace", async function () { }); describe("Canceling Swaps", () => { - context("Canceling Swaps", () => { - var swap: Swap; - before(async () => { - swap = await mockSwap(); - await Swaplace.connect(owner).createSwap(swap); - }); + var swap: Swap; + beforeEach(async () => { + swap = await mockSwap(); + await Swaplace.connect(owner).createSwap(swap); + }); + context("Canceling Swaps", () => { it("Should be able to {cancelSwap} a Swap", async function () { const lastSwap = await Swaplace.totalSwaps(); await expect(await Swaplace.connect(owner).cancelSwap(lastSwap)) @@ -461,21 +461,16 @@ describe("Swaplace", async function () { it("Should not be able to {acceptSwap} a canceled a Swap", async function () { const lastSwap = await Swaplace.totalSwaps(); - await expect( - Swaplace.connect(owner).acceptSwap(lastSwap, receiver.address), - ) - .to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`) - .withArgs(0); + await Swaplace.connect(owner).acceptSwap(lastSwap, receiver.address), + await expect( + Swaplace.connect(owner).acceptSwap(lastSwap, receiver.address), + ) + .to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`) + .withArgs(0); }); }); context("Reverts when canceling Swaps", () => { - var swap: Swap; - before(async () => { - swap = await mockSwap(); - await Swaplace.connect(owner).createSwap(swap); - }); - it("Should revert when {owner} is not {msg.sender}", async function () { const lastSwap = await Swaplace.totalSwaps(); await expect(Swaplace.connect(allowed).cancelSwap(lastSwap)) @@ -498,31 +493,8 @@ describe("Swaplace", async function () { describe("Fetching Swaps", () => { var swap: Swap; - before(async () => { - MockERC20 = await deploy("MockERC20", deployer); - MockERC721 = await deploy("MockERC721", deployer); - - await MockERC721.mint(owner.address, 1); - await MockERC20.mint(allowed.address, 1000); - - const bidingAddr = [MockERC721.address]; - const bidingAmountOrId = [1]; - - const askingAddr = [MockERC20.address]; - const askingAmountOrId = [1000]; - - const currentTimestamp = (await blocktimestamp()) * 2; - const config = await Swaplace.packData(zeroAddress, currentTimestamp); - - swap = await composeSwap( - owner.address, - config, - bidingAddr, - bidingAmountOrId, - askingAddr, - askingAmountOrId, - ); - + beforeEach(async () => { + swap = await mockSwap(); await Swaplace.connect(owner).createSwap(swap); }); @@ -531,7 +503,7 @@ describe("Swaplace", async function () { const fetchedSwap = await Swaplace.getSwap(lastSwap); expect(fetchedSwap.owner).not.to.be.equals(zeroAddress); - // swap.allowed can be the zero address and shoul not be trusted for validation + // swap.allowed can be the zero address and should not be trusted for validation expect(fetchedSwap.expiry).not.to.be.equals(0); expect(fetchedSwap.biding.length).to.be.greaterThan(0); expect(fetchedSwap.asking.length).to.be.greaterThan(0); From e22c041c49efc8cf58d65cee2c3cb372a9eacd20 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Tue, 2 Apr 2024 04:33:39 +0700 Subject: [PATCH 03/49] feat: encodeConfig with native ether payment --- contracts/SwapFactory.sol | 41 ++++++--- contracts/Swaplace.sol | 123 ++++++++++++++++---------- contracts/echidna/TestSwapFactory.sol | 20 ++++- contracts/interfaces/IErrors.sol | 20 ++--- contracts/interfaces/ISwap.sol | 7 +- contracts/interfaces/ISwapFactory.sol | 26 ++++-- contracts/interfaces/ISwaplace.sol | 9 +- 7 files changed, 157 insertions(+), 89 deletions(-) diff --git a/contracts/SwapFactory.sol b/contracts/SwapFactory.sol index 5ec37fd..0293c98 100644 --- a/contracts/SwapFactory.sol +++ b/contracts/SwapFactory.sol @@ -18,6 +18,8 @@ import {ISwapFactory} from "./interfaces/ISwapFactory.sol"; * - The `allowed` address is the address that can accept the Swap. If the allowed * address is the zero address, then anyone can accept the Swap. * - The `expiry` date is the timestamp that the Swap will be available to accept. + * - The `recipient` is the address that will receive the ETH. + * - The `value` is the amount of ETH that the recipient will receive. * - The `biding` are the assets that the owner is offering. * - The `asking` are the assets that the owner wants in exchange. * @@ -49,33 +51,44 @@ abstract contract SwapFactory is ISwapFactory, ISwap, IErrors { function makeSwap( address owner, address allowed, - uint256 expiry, + uint32 expiry, + uint8 recipient, + uint56 value, Asset[] memory biding, Asset[] memory asking ) public view virtual returns (Swap memory) { - if (expiry < block.timestamp) revert InvalidExpiry(expiry); - - if (biding.length == 0 || asking.length == 0) revert InvalidAssetsLength(); - - uint256 config = packData(allowed, expiry); - + if (expiry < block.timestamp) revert InvalidExpiry(); + uint256 config = encodeConfig(allowed, expiry, recipient, value); return Swap(owner, config, biding, asking); } /** - * @dev See {ISwapFactory-packData}. + * @dev See {ISwapFactory-encodeConfig}. */ - function packData( + function encodeConfig( address allowed, - uint256 expiry + uint32 expiry, + uint8 recipient, + uint56 value ) public pure returns (uint256) { - return (uint256(uint160(allowed)) << 96) | uint256(expiry); + return + (uint256(uint160(allowed)) << 96) | + (uint256(expiry) << 64) | + (uint256(recipient) << 56) | + uint256(value); } /** - * @dev See {ISwapFactory-parseData}. + * @dev See {ISwapFactory-decodeConfig}. */ - function parseData(uint256 config) public pure returns (address, uint256) { - return (address(uint160(config >> 96)), uint256(config & ((1 << 96) - 1))); + function decodeConfig( + uint256 config + ) public pure returns (address, uint32, uint8, uint56) { + return ( + address(uint160(config >> 96)), + uint32(config >> 64), + uint8(config >> 56), + uint56(config) + ); } } diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index 901dfb5..64e985e 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -20,21 +20,40 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { /// @dev Mapping of Swap ID to Swap struct. See {ISwap-Swap}. mapping(uint256 => Swap) private _swaps; + /** + * @dev See {ISwaplace-getSwap}. + */ + function getSwap(uint256 swapId) public view returns (Swap memory) { + return _swaps[swapId]; + } + + /** + * @dev Getter function for _totalSwaps. + */ + function totalSwaps() public view returns (uint256) { + return _totalSwaps; + } + /** * @dev See {ISwaplace-createSwap}. */ - function createSwap(Swap calldata swap) public returns (uint256) { - if (swap.owner != msg.sender) revert InvalidAddress(msg.sender); + function createSwap(Swap calldata swap) public payable returns (uint256) { + if (swap.owner != msg.sender) revert InvalidAddress(); assembly { sstore(_totalSwaps.slot, add(sload(_totalSwaps.slot), 1)) } uint256 swapId = _totalSwaps; - _swaps[swapId] = swap; - (address allowed, ) = parseData(swap.config); + (address allowed, , uint8 recipient, uint56 value) = decodeConfig( + swap.config + ); + + if (value > 0 && recipient == 0) { + if (value * 1e12 != msg.value) revert InvalidValue(); + } emit SwapCreated(swapId, msg.sender, allowed); @@ -44,43 +63,31 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { /** * @dev See {ISwaplace-acceptSwap}. */ - function acceptSwap(uint256 swapId, address receiver) public returns (bool) { + function acceptSwap( + uint256 swapId, + address receiver + ) public payable returns (bool) { Swap memory swap = _swaps[swapId]; - (address allowed, uint256 expiry) = parseData(swap.config); - - if (allowed != address(0) && allowed != msg.sender) - revert InvalidAddress(msg.sender); - - if (expiry < block.timestamp) revert InvalidExpiry(expiry); + ( + address allowed, + uint32 expiry, + uint8 recipient, + uint56 value + ) = decodeConfig(swap.config); + if (allowed != address(0) && allowed != msg.sender) revert InvalidAddress(); + if (expiry < block.timestamp) revert InvalidExpiry(); _swaps[swapId].config = 0; - Asset[] memory assets = swap.asking; + _transferFrom(msg.sender, swap.owner, swap.asking); + _transferFrom(swap.owner, receiver, swap.biding); - for (uint256 i = 0; i < assets.length; ) { - ITransfer(assets[i].addr).transferFrom( - msg.sender, - swap.owner, - assets[i].amountOrId - ); - assembly { - i := add(i, 1) - } - } - - assets = swap.biding; - - for (uint256 i = 0; i < assets.length; ) { - ITransfer(assets[i].addr).transferFrom( - swap.owner, - receiver, - assets[i].amountOrId - ); - assembly { - i := add(i, 1) - } - } + if (value > 0) + if (recipient == 0) _payNativeEth(receiver, value * 1e12); + else if (recipient > 0 && value * 1e12 == msg.value) + _payNativeEth(swap.owner, value * 1e12); + else revert InvalidValue(); emit SwapAccepted(swapId, swap.owner, msg.sender); @@ -91,22 +98,47 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { * @dev See {ISwaplace-cancelSwap}. */ function cancelSwap(uint256 swapId) public { - if (_swaps[swapId].owner != msg.sender) revert InvalidAddress(msg.sender); - - (, uint256 expiry) = parseData(_swaps[swapId].config); + Swap memory swap = _swaps[swapId]; + if (swap.owner != msg.sender) revert InvalidAddress(); - if (expiry < block.timestamp) revert InvalidExpiry(expiry); + (, uint32 expiry, uint8 recipient, uint56 value) = decodeConfig( + swap.config + ); + if (expiry < block.timestamp) revert InvalidExpiry(); _swaps[swapId].config = 0; + if (value > 0 && recipient == 0) _payNativeEth(msg.sender, value * 1e12); + emit SwapCanceled(swapId, msg.sender); } /** - * @dev See {ISwaplace-getSwap}. + * @dev Send and amount of native Ether to the receiver. */ - function getSwap(uint256 swapId) public view returns (Swap memory) { - return _swaps[swapId]; + function _payNativeEth(address receiver, uint256 value) internal { + (bool success, ) = receiver.call{value: value}(""); + if (!success) revert InvalidValue(); + } + + /** + * @dev Transfer 'assets' from 'from' to 'to'. + * Where 0x23b872dd is the selector of the `transferFrom` function. + */ + function _transferFrom( + address from, + address to, + Asset[] memory assets + ) internal { + for (uint256 i; i < assets.length; ) { + (bool success, ) = address(assets[i].addr).call( + abi.encodeWithSelector(0x23b872dd, from, to, assets[i].amountOrId) + ); + if (!success) revert InvalidCall(); + assembly { + i := add(i, 1) + } + } } /** @@ -119,11 +151,4 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { interfaceID == type(IERC165).interfaceId || interfaceID == type(ISwaplace).interfaceId; } - - /** - * @dev Getter function for _totalSwaps. - */ - function totalSwaps() public view returns (uint256) { - return _totalSwaps; - } } diff --git a/contracts/echidna/TestSwapFactory.sol b/contracts/echidna/TestSwapFactory.sol index b8ff11b..3ebd53a 100644 --- a/contracts/echidna/TestSwapFactory.sol +++ b/contracts/echidna/TestSwapFactory.sol @@ -32,12 +32,14 @@ contract TestFactory is SwapFactory { Swap memory swap = makeSwap( owner, address(0), - block.timestamp + 1000, + uint32(block.timestamp + 1000), + 0, + 0, make_asset_array(addr, amountOrId), make_asset_array(addr, amountOrId) ); - (, uint256 expiry) = parseData(swap.config); + (, uint32 expiry, , ) = decodeConfig(swap.config); assert(expiry > block.timestamp); assert(swap.biding.length > 0); @@ -46,14 +48,24 @@ contract TestFactory is SwapFactory { } function echidna_revert_invalid_expiry() public view { - makeSwap(address(0), address(0), block.timestamp - 100, _asset, _asset); + makeSwap( + address(0), + address(0), + uint32(block.timestamp - 100), + 0, + 0, + _asset, + _asset + ); } function echidna_revert_invalid_length() public view { makeSwap( address(0), address(0), - block.timestamp + 100, + uint32(block.timestamp + 100), + 0, + 0, new Asset[](0), new Asset[](0) ); diff --git a/contracts/interfaces/IErrors.sol b/contracts/interfaces/IErrors.sol index 25e3f99..1395042 100644 --- a/contracts/interfaces/IErrors.sol +++ b/contracts/interfaces/IErrors.sol @@ -8,20 +8,20 @@ interface IErrors { /** * @dev Displayed when the caller is not the owner of the swap. */ - error InvalidAddress(address caller); + error InvalidAddress(); /** - * @dev Displayed when the amount of {ISwap-Asset} has a length of zero. - * - * NOTE: The `biding` or `asking` array must not be empty to avoid mistakes - * when creating a swap. Assuming one side of the swap is empty, the - * correct approach should be the usage of {transferFrom} and we reinforce - * this behavior by requiring the length of the array to be bigger than zero. + * @dev Displayed when the `expiry` date is in the past. */ - error InvalidAssetsLength(); + error InvalidExpiry(); /** - * @dev Displayed when the `expiry` date is in the past. + * @dev Displayed when the `msg.value` doesn't match the swap request. + */ + error InvalidValue(); + + /** + * @dev Displayed when a low level call failed to execute. */ - error InvalidExpiry(uint256 timestamp); + error InvalidCall(); } diff --git a/contracts/interfaces/ISwap.sol b/contracts/interfaces/ISwap.sol index 693f162..2b10721 100644 --- a/contracts/interfaces/ISwap.sol +++ b/contracts/interfaces/ISwap.sol @@ -24,8 +24,11 @@ interface ISwap { * * It is composed of: * - `owner` of the Swap. - * - `config` represents two packed values: 'allowed' for the allowed address - * to accept the swap and 'expiry' for the expiration date of the swap. + * - `config` represents two packed values: + * - - `allowed` for the allowed address to accept the swap + * - - `expiry` for the expiration date of the swap. + * - - `recipient` for the address that will receive the ETH. + * - - `value` for the amount of ETH that the recipient will receive. * - `biding` assets that are being bided by the owner. * - `asking` assets that are being asked by the owner. * diff --git a/contracts/interfaces/ISwapFactory.sol b/contracts/interfaces/ISwapFactory.sol index 70a3d43..82ca967 100644 --- a/contracts/interfaces/ISwapFactory.sol +++ b/contracts/interfaces/ISwapFactory.sol @@ -27,23 +27,33 @@ interface ISwapFactory { function makeSwap( address owner, address allowed, - uint256 expiry, + uint32 expiry, + uint8 recipient, + uint56 value, ISwap.Asset[] memory assets, ISwap.Asset[] memory asking ) external view returns (ISwap.Swap memory); /** - * @dev Packs `allowed` and the `expiry`. - * This function returns the bitwise packing of `allowed` and `expiry` as a uint256. + * @dev This function returns the bitwise packing as a uint256. */ - function packData( + function encodeConfig( address allowed, - uint256 expiry + uint32 expiry, + uint8 recipient, + uint56 value ) external pure returns (uint256); /** - * @dev Parsing the `config`. - * This function returns the extracted values of `allowed` and `expiry`. + * @dev Decode the `config` variable. + * + * This function returns the extracted values of: + * - `allowed` + * - `expiry` + * - `recipient` + * - `value` */ - function parseData(uint256 config) external pure returns (address, uint256); + function decodeConfig( + uint256 config + ) external pure returns (address, uint32, uint8, uint56); } diff --git a/contracts/interfaces/ISwaplace.sol b/contracts/interfaces/ISwaplace.sol index 5dedc9d..bc6f217 100644 --- a/contracts/interfaces/ISwaplace.sol +++ b/contracts/interfaces/ISwaplace.sol @@ -41,7 +41,9 @@ interface ISwaplace { * * Emits a {SwapCreated} event. */ - function createSwap(ISwap.Swap calldata Swap) external returns (uint256); + function createSwap( + ISwap.Swap calldata Swap + ) external payable returns (uint256); /** * @dev Accepts a Swap. Once the Swap is accepted, the expiry is set @@ -59,7 +61,10 @@ interface ISwaplace { * NOTE: The expiry is set to 0, because if the Swap is expired it * will revert, preventing reentrancy attacks. */ - function acceptSwap(uint256 swapId, address receiver) external returns (bool); + function acceptSwap( + uint256 swapId, + address receiver + ) external payable returns (bool); /** * @dev Cancels an active Swap by setting the expiry to zero. From 6ccbaefb1f4fcfd4179d711f4f55a76e8befb74d Mon Sep 17 00:00:00 2001 From: 0xneves Date: Tue, 2 Apr 2024 04:35:32 +0700 Subject: [PATCH 04/49] fix: grammar --- contracts/Swaplace.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index 64e985e..717cf29 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -9,7 +9,7 @@ import {SwapFactory} from "./SwapFactory.sol"; /** * @author @0xneves | @blockful_io * @dev Swaplace is a Decentralized Feeless DEX. It has no owners, it cannot be stopped. - * Its cern is to facilitate swaps between virtual assets following the ERC standard. + * It's core is to facilitate swaps between virtual assets following the ERC standard. * Users can propose or accept swaps by allowing Swaplace to move their assets using the * `approve` or `permit` function. */ From 2a73b3feb309eb5145e054863190a82d1b380cf6 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 3 Apr 2024 14:31:15 +0700 Subject: [PATCH 05/49] fix: grammar on _payNativeEther --- contracts/Swaplace.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index 717cf29..e8a9fa5 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -114,7 +114,7 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { } /** - * @dev Send and amount of native Ether to the receiver. + * @dev Send an amount of native Ether to the receiver address. */ function _payNativeEth(address receiver, uint256 value) internal { (bool success, ) = receiver.call{value: value}(""); From 095f5b675329b572bb766cdc47c73e1ce928e582 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 3 Apr 2024 14:33:32 +0700 Subject: [PATCH 06/49] fix: better narrative for contract description --- contracts/Swaplace.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index e8a9fa5..89b4e93 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -8,10 +8,10 @@ import {SwapFactory} from "./SwapFactory.sol"; /** * @author @0xneves | @blockful_io - * @dev Swaplace is a Decentralized Feeless DEX. It has no owners, it cannot be stopped. - * It's core is to facilitate swaps between virtual assets following the ERC standard. + * @dev Swaplace is a decentralized and feeless DEX/OTC. Ownerless, it cannot be stopped. + * It's core is to facilitate swaps between virtual assets using the ERC standard. * Users can propose or accept swaps by allowing Swaplace to move their assets using the - * `approve` or `permit` function. + * `approve`, `permit` or similar functions. */ contract Swaplace is SwapFactory, ISwaplace, IERC165 { /// @dev Swap Identifier counter. From e583f7637a562a45fdf7466a6709578cbe2db267 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 3 Apr 2024 14:38:26 +0700 Subject: [PATCH 07/49] fix: Swap struct description --- contracts/interfaces/ISwap.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/interfaces/ISwap.sol b/contracts/interfaces/ISwap.sol index 2b10721..c1fd5b7 100644 --- a/contracts/interfaces/ISwap.sol +++ b/contracts/interfaces/ISwap.sol @@ -23,14 +23,14 @@ interface ISwap { * @dev The Swap struct is the heart of Swaplace. * * It is composed of: - * - `owner` of the Swap. - * - `config` represents two packed values: - * - - `allowed` for the allowed address to accept the swap - * - - `expiry` for the expiration date of the swap. + * - `owner` creator of the Swap. + * - `config` configuration of four packed values: + * - - `allowed` for the allowed address to accept the swap. + * - - `expiry` for the expiration date of the swap in unix time. * - - `recipient` for the address that will receive the ETH. * - - `value` for the amount of ETH that the recipient will receive. - * - `biding` assets that are being bided by the owner. - * - `asking` assets that are being asked by the owner. + * - `biding` assets offered by the swap creator. + * - `asking` assets asked by the swap creator. * * NOTE: When `allowed` address is the zero address, anyone can accept the Swap. */ From 9f35feecbfce18d36306669da5680feb2d876b46 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Sat, 6 Apr 2024 22:59:45 +0700 Subject: [PATCH 08/49] fix: swapId should start at 1 --- .env.sample | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.sample b/.env.sample index 7d6c4a9..9c426f7 100644 --- a/.env.sample +++ b/.env.sample @@ -8,7 +8,7 @@ AMOUNT=1000 # Token ID of ERC721 to be minted for signer address. TOKEN_ID=1 # The swap to be accepted by the acceptee. -SWAP_ID=3 +SWAP_ID=1 # These are public known private keys and are here as an example. # You should change the private keys to your own private keys. From 432d18ce8f06dd9997e74e551d362af8bc3cd0db Mon Sep 17 00:00:00 2001 From: 0xneves Date: Fri, 12 Apr 2024 02:51:57 +0700 Subject: [PATCH 09/49] fix: enhancing documentation descriptions --- contracts/Swaplace.sol | 2 +- contracts/interfaces/ISwapFactory.sol | 22 +++++++++------------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index 89b4e93..9b4922e 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -123,7 +123,7 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { /** * @dev Transfer 'assets' from 'from' to 'to'. - * Where 0x23b872dd is the selector of the `transferFrom` function. + * The selector of the `transferFrom` function in bytes4: 0x23b872dd */ function _transferFrom( address from, diff --git a/contracts/interfaces/ISwapFactory.sol b/contracts/interfaces/ISwapFactory.sol index 82ca967..0b442bd 100644 --- a/contracts/interfaces/ISwapFactory.sol +++ b/contracts/interfaces/ISwapFactory.sol @@ -8,8 +8,8 @@ import {ISwap} from "./ISwap.sol"; */ interface ISwapFactory { /** - * @dev Constructs an asset struct that works for ERC20 or ERC721. - * This function is a utility to easily create an `Asset` struct on-chain or off-chain. + * @dev Constructs an asset struct that works for token standards. + * This function is a utility to easily create an `Asset` type on-chain or off-chain. */ function makeAsset( address addr, @@ -21,8 +21,7 @@ interface ISwapFactory { * * Requirements: * - * - `expiry` cannot be in the past timestamp. - * - `biding` and `asking` cannot be empty. + * - `expiry` cannot be in the past. */ function makeSwap( address owner, @@ -35,7 +34,7 @@ interface ISwapFactory { ) external view returns (ISwap.Swap memory); /** - * @dev This function returns the bitwise packing as a uint256. + * @dev This function uses bitwise to return a uint256. */ function encodeConfig( address allowed, @@ -45,15 +44,12 @@ interface ISwapFactory { ) external pure returns (uint256); /** - * @dev Decode the `config` variable. - * - * This function returns the extracted values of: - * - `allowed` - * - `expiry` - * - `recipient` - * - `value` + * @dev Decode `config` into their respective variables. */ function decodeConfig( uint256 config - ) external pure returns (address, uint32, uint8, uint56); + ) + external + pure + returns (address allowed, uint32 expiry, uint8 recipient, uint56 value); } From c5ededfa57d968daa54f7cc4cb27cd7d8805255d Mon Sep 17 00:00:00 2001 From: 0xneves Date: Fri, 12 Apr 2024 02:53:08 +0700 Subject: [PATCH 10/49] fix: enhance documentation of makeAsset --- contracts/interfaces/ISwapFactory.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/interfaces/ISwapFactory.sol b/contracts/interfaces/ISwapFactory.sol index 0b442bd..18aa5cc 100644 --- a/contracts/interfaces/ISwapFactory.sol +++ b/contracts/interfaces/ISwapFactory.sol @@ -8,8 +8,7 @@ import {ISwap} from "./ISwap.sol"; */ interface ISwapFactory { /** - * @dev Constructs an asset struct that works for token standards. - * This function is a utility to easily create an `Asset` type on-chain or off-chain. + * @dev Constructs an {ISwap-Asset} struct to work with token standards. */ function makeAsset( address addr, From 60d5a356a0d9c85259558563dd6cf36793cae39d Mon Sep 17 00:00:00 2001 From: 0xneves Date: Fri, 12 Apr 2024 06:30:31 +0700 Subject: [PATCH 11/49] feat: adding ERC1155 mock and test --- contracts/mock/MockERC1155.sol | 37 ++++++++++++++++++++++++++++++++++ test/TestMockContracts.test.ts | 23 +++++++++++++++++---- test/utils/utils.ts | 6 ++++-- 3 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 contracts/mock/MockERC1155.sol diff --git a/contracts/mock/MockERC1155.sol b/contracts/mock/MockERC1155.sol new file mode 100644 index 0000000..b5904c0 --- /dev/null +++ b/contracts/mock/MockERC1155.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; + +contract MockERC1155 is ERC1155 { + using Strings for uint256; + + string private _name; + string private _symbol; + + function name() public view returns (string memory) { + return _name; + } + + function symbol() public view returns (string memory) { + return _symbol; + } + + constructor() + ERC1155("ipfs://QmQJnHseE9VPw5qVxuEhxTiZ7avzgkCdFz69rg86UvTZdk/") + { + _name = "MockERC1155"; + _symbol = "M1155"; + } + + function mint(address to, uint256 id, uint256 amount) public { + _mint(to, id, amount, ""); + } + + function tokenURI( + uint256 tokenId + ) public view virtual returns (string memory) { + return uri(tokenId); + } +} diff --git a/test/TestMockContracts.test.ts b/test/TestMockContracts.test.ts index 545b125..b7be7eb 100644 --- a/test/TestMockContracts.test.ts +++ b/test/TestMockContracts.test.ts @@ -8,6 +8,7 @@ describe("Swaplace", async function () { // The deployed contracts let MockERC20: Contract; let MockERC721: Contract; + let MockERC1155: Contract; // The signers of the test let deployer: SignerWithAddress; @@ -18,27 +19,32 @@ describe("Swaplace", async function () { [deployer, owner, acceptee] = await ethers.getSigners(); MockERC20 = await deploy("MockERC20", deployer); MockERC721 = await deploy("MockERC721", deployer); + MockERC1155 = await deploy("MockERC1155", deployer); }); it("Should test the {mint} function", async function () { - // Testing the mint of ERC20 await MockERC20.mint(owner.address, 1000); expect(await MockERC20.balanceOf(owner.address)).to.be.equals(1000); - // Testing the mint of ERC721 await MockERC721.mint(owner.address, 1); expect(await MockERC721.balanceOf(owner.address)).to.be.equals(1); + + await MockERC1155.mint(owner.address, 1, 5); }); it("Should test the {approve} function", async function () { - // Testing the approval of ERC20 await MockERC20.connect(owner).approve(acceptee.address, 1000); expect( await MockERC20.allowance(owner.address, acceptee.address), ).to.be.equals("1000"); - // Testing the approval of ERC721 + await MockERC721.connect(owner).approve(acceptee.address, 1); expect(await MockERC721.getApproved(1)).to.be.equals(acceptee.address); + + await MockERC1155.connect(owner).setApprovalForAll(acceptee.address, true); + expect( + await MockERC1155.isApprovedForAll(owner.address, acceptee.address), + ).to.be.equals(true); }); it("Should test the {transferFrom} function", async function () { @@ -54,5 +60,14 @@ describe("Swaplace", async function () { ); expect(await MockERC721.balanceOf(owner.address)).to.be.equals(0); expect(await MockERC721.balanceOf(acceptee.address)).to.be.equals(1); + // Testing the transfer of ERC1155 + await MockERC1155.connect(owner).safeTransferFrom( + owner.address, + acceptee.address, + 1, + 5, + "0x", + ); + expect(await MockERC1155.balanceOf(acceptee.address, 1)).to.be.equals(5); }); }); diff --git a/test/utils/utils.ts b/test/utils/utils.ts index 486ad40..6c217bd 100644 --- a/test/utils/utils.ts +++ b/test/utils/utils.ts @@ -32,7 +32,8 @@ export async function deploy(contractName: any, signer: any) { throw new Error( `Error getting the Contract Factory for ${contractName}. Make sure the contract is compiled, the type-chain generated - and a valid Ethereum Address for signer set in hardhat.config.ts.`, + and a valid Ethereum Address for signer set in hardhat.config.ts. + ${error}`, ); } @@ -44,7 +45,8 @@ export async function deploy(contractName: any, signer: any) { `Error deploying the Contract ${contractName}. Make sure the network is correct, that you have a valid Ethereum Address for signer with enough funds for the transaction. The gas settings might - as well be lower than the amount required by the network at the moment.`, + as well be lower than the amount required by the network at the moment. + ${error}`, ); } From 2e7190532f2d083430256a37cafc502f1cb81538 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Fri, 12 Apr 2024 06:30:59 +0700 Subject: [PATCH 12/49] refactor: adding URI to the mock erc721 --- contracts/mock/MockERC721.sol | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/contracts/mock/MockERC721.sol b/contracts/mock/MockERC721.sol index e763dc5..f9e63ff 100644 --- a/contracts/mock/MockERC721.sol +++ b/contracts/mock/MockERC721.sol @@ -12,4 +12,10 @@ contract MockERC721 is ERC721 { totalSupply++; _mint(to, id); } + + function tokenURI( + uint256 + ) public view virtual override returns (string memory) { + return "ipfs://QmQJnHseE9VPw5qVxuEhxTiZ7avzgkCdFz69rg86UvTZdk/"; + } } From c707b9936680ca92233694d736ac6c6fd6c2dae7 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Fri, 12 Apr 2024 06:31:13 +0700 Subject: [PATCH 13/49] refactor: including new encoder in the pipeline --- test/TestSwapFactory.test.ts | 131 +++++++++++++++++++++++++------- test/TestSwaplace.test.ts | 142 +++++++++++++++++++++++------------ test/utils/SwapFactory.ts | 68 +++++++++++++---- 3 files changed, 250 insertions(+), 91 deletions(-) diff --git a/test/TestSwapFactory.test.ts b/test/TestSwapFactory.test.ts index bb1079d..b434fb4 100644 --- a/test/TestSwapFactory.test.ts +++ b/test/TestSwapFactory.test.ts @@ -2,7 +2,14 @@ import { expect } from "chai"; import { Contract } from "ethers"; import { ethers } from "hardhat"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; -import { Asset, makeAsset, makeSwap, composeSwap } from "./utils/SwapFactory"; +import { + Asset, + makeAsset, + makeSwap, + composeSwap, + encodeConfig, + decodeConfig, +} from "./utils/SwapFactory"; import { blocktimestamp, deploy } from "./utils/utils"; describe("Swaplace Factory", async function () { @@ -28,11 +35,11 @@ describe("Swaplace Factory", async function () { it("Should be able to {makeAsset} for ERC20 and ERC721", async function () { var asset: Asset = await makeAsset(MockERC20.address, 1000); expect(asset.addr).to.be.equals(MockERC20.address); - expect(asset.amountOrId).to.be.equals("1000"); + expect(asset.amountOrId).to.be.equals(1000); var asset: Asset = await makeAsset(MockERC721.address, 1); expect(asset.addr).to.be.equals(MockERC721.address); - expect(asset.amountOrId).to.be.equals("1"); + expect(asset.amountOrId).to.be.equals(1); }); it("Should be able to {makeAsset} in the off-chain matching on-chain", async function () { @@ -40,16 +47,47 @@ describe("Swaplace Factory", async function () { var asset: Asset = await makeAsset(MockERC20.address, 1000); expect(asset.addr).to.be.equals(MockERC20.address); - expect(asset.amountOrId).to.be.equals("1000"); + expect(asset.amountOrId).to.be.equals(1000); + }); + + it("Should be able to encode and decode config using off-chain", async function () { + const currentTimestamp = (await blocktimestamp()) + 2000000; + const configOnChain = await Swaplace.encodeConfig( + Swaplace.address, + currentTimestamp, + 0, + 0, + ); + const configOffChain = await encodeConfig( + Swaplace.address, + currentTimestamp, + 0, + 0, + ); + expect(configOnChain).to.be.equals(configOffChain); + + const [allowed, expiry, recipient, value] = await Swaplace.decodeConfig( + configOnChain, + ); + const decodedConfig = await decodeConfig(configOffChain); + expect(BigInt(allowed)).to.be.equals(decodedConfig.allowed); + expect(expiry).to.be.equals(decodedConfig.expiry); + expect(recipient).to.be.equals(decodedConfig.recipient); + expect(value).to.be.equals(decodedConfig.value); }); it("Should be able to {makeSwap} with ERC20 and ERC721", async function () { - const currentTimestamp = (await blocktimestamp()) * 2; + const currentTimestamp = (await blocktimestamp()) + 2000000; const ERC20Asset: Asset = await makeAsset(MockERC20.address, 1000); const ERC721Asset: Asset = await makeAsset(MockERC721.address, 1); - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); const swap = await makeSwap( owner.address, @@ -58,7 +96,7 @@ describe("Swaplace Factory", async function () { [ERC721Asset], ); - const [allowed, expiry] = await Swaplace.parseData(swap.config); + const [allowed, expiry, ,] = await Swaplace.decodeConfig(swap.config); expect(swap.owner).to.be.equals(owner.address); expect(expiry).to.be.equals(currentTimestamp); @@ -68,12 +106,17 @@ describe("Swaplace Factory", async function () { }); it("Should be able to {makeSwap} in the off-chain matching on-chain", async function () { - const currentTimestamp = (await blocktimestamp()) * 2; + const currentTimestamp = (await blocktimestamp()) + 2000000; const ERC20Asset: Asset = await makeAsset(MockERC20.address, 1000); const ERC721Asset: Asset = await makeAsset(MockERC721.address, 1); - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); const swap = await makeSwap( owner.address, @@ -86,12 +129,14 @@ describe("Swaplace Factory", async function () { owner.address, zeroAddress, currentTimestamp, + 0, + 0, [ERC20Asset], [ERC721Asset], ); - const [allowed, expiry] = await Swaplace.parseData(swap.config); - const [onChainAllowed, onChainExpiry] = await Swaplace.parseData( + const [allowed, expiry, ,] = await Swaplace.decodeConfig(swap.config); + const [onChainAllowed, onChainExpiry, ,] = await Swaplace.decodeConfig( onChainSwap.config, ); @@ -111,12 +156,17 @@ describe("Swaplace Factory", async function () { }); it("Should be able to {makeSwap} with multiple assets", async function () { - const currentTimestamp = (await blocktimestamp()) * 2; + const currentTimestamp = (await blocktimestamp()) + 2000000; const ERC20Asset = await makeAsset(MockERC20.address, 1000); const ERC721Asset = await makeAsset(MockERC721.address, 1); - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); const swap = await makeSwap( owner.address, @@ -125,7 +175,7 @@ describe("Swaplace Factory", async function () { [ERC20Asset, ERC721Asset], ); - const [, expiry] = await Swaplace.parseData(swap.config); + const [, expiry, ,] = await Swaplace.decodeConfig(swap.config); expect(swap.owner).to.be.equals(owner.address); expect(expiry).to.be.equals(expiry); @@ -136,7 +186,7 @@ describe("Swaplace Factory", async function () { }); it("Should be able to {composeSwap} using both ERC20, ERC721", async function () { - const currentTimestamp = (await blocktimestamp()) * 2; + const currentTimestamp = (await blocktimestamp()) + 2000000; const bidingAddr = [MockERC20.address, MockERC721.address]; const bidingAmountOrId = [1000, 1]; @@ -144,7 +194,12 @@ describe("Swaplace Factory", async function () { const askingAddr = [MockERC721.address]; const askingAmountOrId = [2]; - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); const swap = await composeSwap( owner.address, @@ -155,7 +210,7 @@ describe("Swaplace Factory", async function () { askingAmountOrId, ); - const [allowed, expiry] = await Swaplace.parseData(swap.config); + const [allowed, expiry, ,] = await Swaplace.decodeConfig(swap.config); expect(swap.owner).to.be.equals(owner.address); expect(allowed).to.be.equals(zeroAddress); @@ -172,7 +227,7 @@ describe("Swaplace Factory", async function () { const askingAmountOrId = [2]; try { - const config = await Swaplace.packData(zeroAddress, expiry); + const config = await Swaplace.encodeConfig(zeroAddress, expiry, 0, 0); await composeSwap( owner.address, config, @@ -187,7 +242,7 @@ describe("Swaplace Factory", async function () { }); it("Should revert using {composeSwap} with owner as address zero", async function () { - const currentTimestamp = (await blocktimestamp()) * 2; + const currentTimestamp = (await blocktimestamp()) + 2000000; const bidingAddr = [MockERC20.address]; const bidingAmountOrId = [1000]; @@ -196,7 +251,12 @@ describe("Swaplace Factory", async function () { const askingAmountOrId = [2]; try { - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); await composeSwap( zeroAddress, config, @@ -211,7 +271,7 @@ describe("Swaplace Factory", async function () { }); it("Should revert using {composeSwap} with empty assets", async function () { - const currentTimestamp = (await blocktimestamp()) * 2; + const currentTimestamp = (await blocktimestamp()) + 2000000; const bidingAddr = [MockERC20.address]; const bidingAmountOrId = [1000]; @@ -220,7 +280,12 @@ describe("Swaplace Factory", async function () { const askingAmountOrId: any[] = []; try { - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); await composeSwap( owner.address, config, @@ -235,7 +300,7 @@ describe("Swaplace Factory", async function () { }); it("Should revert using {composeSwap} with empty assets length", async function () { - const currentTimestamp = (await blocktimestamp()) * 2; + const currentTimestamp = (await blocktimestamp()) + 2000000; const bidingAddr = [MockERC20.address]; const bidingAmountOrId = [1000]; @@ -244,7 +309,12 @@ describe("Swaplace Factory", async function () { const askingAmountOrId = [1, 999, 777]; try { - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); await composeSwap( owner.address, config, @@ -258,12 +328,17 @@ describe("Swaplace Factory", async function () { } }); - it("Should ensure packData() and parseData() return the right values", async function () { - const currentTimestamp = (await blocktimestamp()) * 2; + it("Should ensure encodeConfig() and decodeConfig() return the right values", async function () { + const currentTimestamp = (await blocktimestamp()) + 2000000; - const config = await Swaplace.packData(acceptee.address, currentTimestamp); + const config = await Swaplace.encodeConfig( + acceptee.address, + currentTimestamp, + 0, + 0, + ); - const [allowed, expiry] = await Swaplace.parseData(config); + const [allowed, expiry, ,] = await Swaplace.decodeConfig(config); expect(allowed).to.be.equals(acceptee.address); expect(expiry).to.be.equals(currentTimestamp); diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index b6af0f6..8feccd1 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -32,8 +32,14 @@ describe("Swaplace", async function () { const askingAddr = [MockERC20.address]; const askingAmountOrId = [50]; - const currentTimestamp = (await blocktimestamp()) * 2; - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const currentTimestamp = (await blocktimestamp()) + 1000000; + + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); const swap: Swap = await composeSwap( owner.address, @@ -63,8 +69,13 @@ describe("Swaplace", async function () { const askingAddr = [MockERC20.address]; const askingAmountOrId = [50]; - const currentTimestamp = (await blocktimestamp()) * 2; - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); const swap: Swap = await composeSwap( owner.address, @@ -91,8 +102,13 @@ describe("Swaplace", async function () { ]; const askingAmountOrId = [50, 100, 150]; - const currentTimestamp = (await blocktimestamp()) * 2; - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); const swap: Swap = await composeSwap( owner.address, @@ -123,8 +139,13 @@ describe("Swaplace", async function () { ]; const askingAmountOrId = [50, 100, 150]; - const currentTimestamp = (await blocktimestamp()) * 2; - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); const swap: Swap = await composeSwap( owner.address, @@ -147,8 +168,13 @@ describe("Swaplace", async function () { const askingAddr = [MockERC721.address]; const askingAmountOrId = [4]; - const currentTimestamp = (await blocktimestamp()) * 2; - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); const swap: Swap = await composeSwap( owner.address, @@ -175,8 +201,13 @@ describe("Swaplace", async function () { ]; const askingAmountOrId = [4, 5, 6]; - const currentTimestamp = (await blocktimestamp()) * 2; - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); const swap: Swap = await composeSwap( owner.address, @@ -207,8 +238,13 @@ describe("Swaplace", async function () { ]; const askingAmountOrId = [4, 5, 6]; - const currentTimestamp = (await blocktimestamp()) * 2; - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); const swap: Swap = await composeSwap( owner.address, @@ -228,9 +264,9 @@ describe("Swaplace", async function () { context("Reverts when creating Swaps", () => { it("Should revert when {owner} is not {msg.sender}", async function () { const swap = await mockSwap(); - await expect(Swaplace.connect(allowed).createSwap(swap)) - .to.be.revertedWithCustomError(Swaplace, `InvalidAddress`) - .withArgs(allowed.address); + await expect( + Swaplace.connect(allowed).createSwap(swap), + ).to.be.revertedWithCustomError(Swaplace, `InvalidAddress`); }); }); }); @@ -253,8 +289,13 @@ describe("Swaplace", async function () { const askingAddr = [MockERC20.address]; const askingAmountOrId = [1000]; - const currentTimestamp = (await blocktimestamp()) * 2; - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); swap = await composeSwap( owner.address, @@ -328,9 +369,14 @@ describe("Swaplace", async function () { await MockERC721.connect(allowed).approve(Swaplace.address, 10); const swap = await mockSwap(); - const [, expiry] = await Swaplace.parseData(swap.config); + const [, expiry, ,] = await Swaplace.decodeConfig(swap.config); - swap.config = await Swaplace.packData(allowed.address, expiry); + swap.config = await Swaplace.encodeConfig( + allowed.address, + expiry, + 0, + 0, + ); await expect(await Swaplace.connect(owner).createSwap(swap)) .to.emit(Swaplace, "SwapCreated") @@ -377,26 +423,22 @@ describe("Swaplace", async function () { await Swaplace.totalSwaps(), receiver.address, ), - ) - .to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`) - .withArgs(0); + ).to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`); }); it("Should revert when {expiry} is smaller than {block.timestamp}", async function () { await Swaplace.connect(owner).createSwap(swap); - const [, expiry] = await Swaplace.parseData(swap.config); + const [, expiry, ,] = await Swaplace.decodeConfig(swap.config); - await network.provider.send("evm_increaseTime", [expiry * 2]); + await network.provider.send("evm_increaseTime", [2000000]); await expect( Swaplace.connect(owner).acceptSwap( await Swaplace.totalSwaps(), receiver.address, ), - ) - .to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`) - .withArgs(expiry); + ).to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`); }); it("Should revert when {allowance} is not provided", async function () { @@ -409,7 +451,7 @@ describe("Swaplace", async function () { await Swaplace.totalSwaps(), receiver.address, ), - ).to.be.revertedWith(`ERC721: caller is not token owner or approved`); + ).to.be.revertedWithCustomError(Swaplace, `InvalidCall`); }); it("Should revert when {acceptSwap} as not allowed to P2P Swap", async function () { @@ -421,8 +463,13 @@ describe("Swaplace", async function () { const swap = await mockSwap(); - const [, expiry] = await Swaplace.parseData(swap.config); - swap.config = await Swaplace.packData(deployer.address, expiry); + const [, expiry, ,] = await Swaplace.decodeConfig(swap.config); + swap.config = await Swaplace.encodeConfig( + deployer.address, + expiry, + 0, + 0, + ); await expect(await Swaplace.connect(owner).createSwap(swap)) .to.emit(Swaplace, "SwapCreated") @@ -437,9 +484,7 @@ describe("Swaplace", async function () { await Swaplace.totalSwaps(), receiver.address, ), - ) - .to.be.revertedWithCustomError(Swaplace, "InvalidAddress") - .withArgs(allowed.address); + ).to.be.revertedWithCustomError(Swaplace, "InvalidAddress"); }); }); }); @@ -464,29 +509,27 @@ describe("Swaplace", async function () { await Swaplace.connect(owner).acceptSwap(lastSwap, receiver.address), await expect( Swaplace.connect(owner).acceptSwap(lastSwap, receiver.address), - ) - .to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`) - .withArgs(0); + ).to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`); }); }); context("Reverts when canceling Swaps", () => { it("Should revert when {owner} is not {msg.sender}", async function () { const lastSwap = await Swaplace.totalSwaps(); - await expect(Swaplace.connect(allowed).cancelSwap(lastSwap)) - .to.be.revertedWithCustomError(Swaplace, `InvalidAddress`) - .withArgs(allowed.address); + await expect( + Swaplace.connect(allowed).cancelSwap(lastSwap), + ).to.be.revertedWithCustomError(Swaplace, `InvalidAddress`); }); it("Should revert when {expiry} is smaller than {block.timestamp}", async function () { - const [, expiry] = await Swaplace.parseData(swap.config); + const [, expiry, ,] = await Swaplace.decodeConfig(swap.config); - await network.provider.send("evm_increaseTime", [expiry * 2]); + await network.provider.send("evm_increaseTime", [2000000]); const lastSwap = await Swaplace.totalSwaps(); - await expect(Swaplace.connect(owner).cancelSwap(lastSwap)) - .to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`) - .withArgs(expiry); + await expect( + Swaplace.connect(owner).cancelSwap(lastSwap), + ).to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`); }); }); }); @@ -502,9 +545,10 @@ describe("Swaplace", async function () { const lastSwap = await Swaplace.totalSwaps(); const fetchedSwap = await Swaplace.getSwap(lastSwap); + const [, expiry, ,] = await Swaplace.decodeConfig(swap.config); + expect(fetchedSwap.owner).not.to.be.equals(zeroAddress); - // swap.allowed can be the zero address and should not be trusted for validation - expect(fetchedSwap.expiry).not.to.be.equals(0); + expect(expiry).not.to.be.equals(0); expect(fetchedSwap.biding.length).to.be.greaterThan(0); expect(fetchedSwap.asking.length).to.be.greaterThan(0); }); @@ -514,7 +558,7 @@ describe("Swaplace", async function () { const fetchedSwap = await Swaplace.getSwap(imaginarySwapId); // swap.allowed can be the zero address and shoul not be trusted for validation expect(fetchedSwap.owner).to.be.deep.equals(zeroAddress); - const [fetchedAllowed, fetchedExpiry] = await Swaplace.parseData( + const [fetchedAllowed, fetchedExpiry, ,] = await Swaplace.decodeConfig( fetchedSwap.config, ); expect(fetchedAllowed).to.be.deep.equals(zeroAddress); diff --git a/test/utils/SwapFactory.ts b/test/utils/SwapFactory.ts index 9879aa8..1629d87 100644 --- a/test/utils/SwapFactory.ts +++ b/test/utils/SwapFactory.ts @@ -5,7 +5,7 @@ import { ethers } from "hardhat"; */ export interface Asset { addr: string; - amountOrId: bigint; + amountOrId: bigint | number; } /** @@ -13,17 +13,55 @@ export interface Asset { */ export interface Swap { owner: string; - config: number; + config: bigint; biding: Asset[]; asking: Asset[]; } +/** + * @dev See {ISwapFactory-encodeConfig}. + */ +export async function encodeConfig( + allowed: string, + expiry: bigint | number, + recipient: bigint | number, + value: bigint | number, +): Promise { + return ( + (BigInt(allowed) << BigInt(96)) | + (BigInt(expiry) << BigInt(64)) | + (BigInt(recipient) << BigInt(56)) | + BigInt(value) + ); +} + +/** + * @dev See {ISwapFactory-decodeConfig}. + */ +export async function decodeConfig(config: bigint): Promise<{ + allowed: string; + expiry: bigint | number; + recipient: bigint | number; + value: bigint | number; +}> { + // ethers check sum address + return { + allowed: + config >> BigInt(96) == BigInt(0) + ? ethers.constants.AddressZero + : ethers.utils.getAddress((config >> BigInt(96)).toString(16)), + expiry: (config >> BigInt(64)) & ((BigInt(1) << BigInt(32)) - BigInt(1)), + recipient: (config >> BigInt(56)) & ((BigInt(1) << BigInt(8)) - BigInt(1)), + value: config & ((BigInt(1) << BigInt(56)) - BigInt(1)), + }; +} + /** * @dev See {ISwapFactory-makeAsset}. */ export async function makeAsset( addr: string, - amountOrId: number | bigint, + amountOrId: bigint | number, ): Promise { // validate if its an ethereum address if (!ethers.utils.isAddress(addr)) { @@ -43,7 +81,7 @@ export async function makeAsset( */ const asset: Asset = { addr: addr, - amountOrId: typeof amountOrId == "number" ? BigInt(amountOrId) : amountOrId, + amountOrId: amountOrId, }; return asset; @@ -53,8 +91,8 @@ export async function makeAsset( * @dev See {ISwapFactory-makeSwap}. */ export async function makeSwap( - owner: any, - config: any, + owner: string, + config: bigint, biding: Asset[], asking: Asset[], ) { @@ -108,12 +146,12 @@ export async function makeSwap( * - `askingAddr` and `askingAmountOrId` must have the same length. */ export async function composeSwap( - owner: any, - config: any, - bidingAddr: any[], - bidingAmountOrId: any[], - askingAddr: any[], - askingAmountOrId: any[], + owner: string, + config: bigint, + bidingAddr: string[], + bidingAmountOrId: bigint[] | number[], + askingAddr: string[], + askingAmountOrId: bigint[] | number[], ) { // lenght of addresses and their respective amounts must be equal if ( @@ -124,12 +162,12 @@ export async function composeSwap( } // push new assets to the array of bids and asks - const biding: any[] = []; + const biding: Asset[] = []; bidingAddr.forEach(async (addr, index) => { biding.push(await makeAsset(addr, bidingAmountOrId[index])); }); - const asking: any[] = []; + const asking: Asset[] = []; askingAddr.forEach(async (addr, index) => { asking.push(await makeAsset(addr, askingAmountOrId[index])); }); @@ -141,4 +179,6 @@ module.exports = { makeAsset, makeSwap, composeSwap, + encodeConfig, + decodeConfig, }; From f79e1c68c1044b5e2cbbb9c6dfa863d7fd8ecb04 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 17 Apr 2024 15:02:51 +0700 Subject: [PATCH 14/49] feat: ERC1155 can be traded --- contracts/SwapFactory.sol | 26 ++++++++++++++++++++++++++ contracts/Swaplace.sol | 28 +++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/contracts/SwapFactory.sol b/contracts/SwapFactory.sol index 0293c98..d56531e 100644 --- a/contracts/SwapFactory.sol +++ b/contracts/SwapFactory.sol @@ -62,6 +62,32 @@ abstract contract SwapFactory is ISwapFactory, ISwap, IErrors { return Swap(owner, config, biding, asking); } + /** + * @dev See {ISwapFactory-encodeAsset}. + */ + function encodeAsset( + uint120 tokenId, + uint120 tokenAmount + ) public pure returns (uint256 amountOrId) { + return + (uint256(type(uint16).max) << 240) | + (uint256(tokenId) << 120) | + uint256(tokenAmount); + } + + /** + * @dev See {ISwapFactory-decodeAsset}. + */ + function decodeAsset( + uint256 amountOrId + ) public pure returns (uint16, uint256, uint256) { + return ( + uint16(amountOrId >> 240), + uint256(amountOrId >> 120), + uint256(amountOrId) + ); + } + /** * @dev See {ISwapFactory-encodeConfig}. */ diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index 9b4922e..b7128ba 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -125,7 +125,7 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { * @dev Transfer 'assets' from 'from' to 'to'. * The selector of the `transferFrom` function in bytes4: 0x23b872dd */ - function _transferFrom( + function _transferFrom2( address from, address to, Asset[] memory assets @@ -141,6 +141,32 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { } } + function _transferFrom( + address from, + address to, + Asset[] memory assets + ) internal { + for (uint256 i; i < assets.length; ) { + (uint16 assetType, uint256 tokenId, uint256 tokenAmount) = decodeAsset( + assets[i].amountOrId + ); + if (assetType == type(uint16).max) { + (bool success, ) = address(assets[i].addr).call( + abi.encodeWithSelector(0xf242432a, from, to, tokenId, tokenAmount, "") + ); + if (!success) revert InvalidCall(); + } else { + (bool success, ) = address(assets[i].addr).call( + abi.encodeWithSelector(0x23b872dd, from, to, tokenAmount) + ); + if (!success) revert InvalidCall(); + } + assembly { + i := add(i, 1) + } + } + } + /** * @dev See {IERC165-supportsInterface}. */ From 95f72f1576fcabcdca615c8c27603cf53744d2ea Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 17 Apr 2024 21:55:12 +0700 Subject: [PATCH 15/49] feat: encode and decode Asset --- contracts/SwapFactory.sol | 17 ++++++++++- contracts/Swaplace.sol | 22 +++----------- contracts/interfaces/ISwapFactory.sol | 41 +++++++++++++++++++++++++-- 3 files changed, 59 insertions(+), 21 deletions(-) diff --git a/contracts/SwapFactory.sol b/contracts/SwapFactory.sol index d56531e..9024c86 100644 --- a/contracts/SwapFactory.sol +++ b/contracts/SwapFactory.sol @@ -45,6 +45,17 @@ abstract contract SwapFactory is ISwapFactory, ISwap, IErrors { return Asset(addr, amountOrId); } + /** + * @dev See {ISwapFactory-make1155Asset}. + */ + function make1155Asset( + address addr, + uint120 tokenId, + uint120 tokenAmount + ) public pure virtual returns (Asset memory) { + return Asset(addr, encodeAsset(tokenId, tokenAmount)); + } + /** * @dev See {ISwapFactory-makeSwap}. */ @@ -80,7 +91,11 @@ abstract contract SwapFactory is ISwapFactory, ISwap, IErrors { */ function decodeAsset( uint256 amountOrId - ) public pure returns (uint16, uint256, uint256) { + ) + public + pure + returns (uint16 tokenType, uint256 tokenId, uint256 tokenAmount) + { return ( uint16(amountOrId >> 240), uint256(amountOrId >> 120), diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index b7128ba..24fce2f 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -122,25 +122,11 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { } /** - * @dev Transfer 'assets' from 'from' to 'to'. - * The selector of the `transferFrom` function in bytes4: 0x23b872dd + * @dev Transfer multiple 'assets' from 'from' to 'to'. + * + * `0x23b872dd` - Selector of the `transferFrom` function (ERC20, ERC721). + * `0xf242432a` - Selector of the `safeTransferFrom` function (ERC1155). */ - function _transferFrom2( - address from, - address to, - Asset[] memory assets - ) internal { - for (uint256 i; i < assets.length; ) { - (bool success, ) = address(assets[i].addr).call( - abi.encodeWithSelector(0x23b872dd, from, to, assets[i].amountOrId) - ); - if (!success) revert InvalidCall(); - assembly { - i := add(i, 1) - } - } - } - function _transferFrom( address from, address to, diff --git a/contracts/interfaces/ISwapFactory.sol b/contracts/interfaces/ISwapFactory.sol index 18aa5cc..a636c00 100644 --- a/contracts/interfaces/ISwapFactory.sol +++ b/contracts/interfaces/ISwapFactory.sol @@ -8,13 +8,26 @@ import {ISwap} from "./ISwap.sol"; */ interface ISwapFactory { /** - * @dev Constructs an {ISwap-Asset} struct to work with token standards. + * @dev Make an {ISwap-Asset} struct to work with token standards. */ function makeAsset( address addr, uint256 amountOrId ) external pure returns (ISwap.Asset memory); + /** + * @dev Make an {ISwap-Asset} struct to work with token standards. + * + * NOTE: Different from the {makeAsset} function, this function is used to + * encode the token ID and token amount into a single uint256. This is made + * to work with the ERC1155 standard. + */ + function make1155Asset( + address addr, + uint120 tokenId, + uint120 tokenAmount + ) external pure returns (ISwap.Asset memory); + /** * @dev Build a swap struct to use in the {Swaplace-createSwap} function. * @@ -33,7 +46,31 @@ interface ISwapFactory { ) external view returns (ISwap.Swap memory); /** - * @dev This function uses bitwise to return a uint256. + * @dev Encode `tokenId` and `tokenAmount` into a single uint256 while adding a flag + * to indicate that it's an ERC1155 token. + */ + function encodeAsset( + uint120 tokenId, + uint120 tokenAmount + ) external pure returns (uint256 amountOrId); + + /** + * @dev Decode `amountOrId` to check if the first 4 bytes are set to 0xFFFFFFFF. + * If the flag is set to 0xFFFFFFFF, then it's an ERC1155 standard, otherwise it's + * assumed to be an ERC20 or ERC721 standard. + * + * NOTE: If it's an ERC1155 token, then the next 120 bits are the token ID and the next + * 120 bits are the token amount. + */ + function decodeAsset( + uint256 amountOrId + ) + external + pure + returns (uint16 tokenType, uint256 tokenId, uint256 tokenAmount); + + /** + * @dev This function uses bitwise to return an encoded uint256 of the following parameters. */ function encodeConfig( address allowed, From 0f2a9f443ffb46b081e4b9b887d896b1b8c5d209 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 17 Apr 2024 22:29:26 +0700 Subject: [PATCH 16/49] fix: leading zero addresses incorrectly padded --- test/TestSwapFactory.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/TestSwapFactory.test.ts b/test/TestSwapFactory.test.ts index b434fb4..472bdb7 100644 --- a/test/TestSwapFactory.test.ts +++ b/test/TestSwapFactory.test.ts @@ -70,6 +70,7 @@ describe("Swaplace Factory", async function () { configOnChain, ); const decodedConfig = await decodeConfig(configOffChain); + expect(BigInt(allowed)).to.be.equals(decodedConfig.allowed); expect(expiry).to.be.equals(decodedConfig.expiry); expect(recipient).to.be.equals(decodedConfig.recipient); From fd56aa3e94731d830a2f2633b646ddc511dd4501 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 17 Apr 2024 23:17:21 +0700 Subject: [PATCH 17/49] fix: types was not adjusting to uint120 at first --- contracts/SwapFactory.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/SwapFactory.sol b/contracts/SwapFactory.sol index 9024c86..3c6e2e6 100644 --- a/contracts/SwapFactory.sol +++ b/contracts/SwapFactory.sol @@ -98,8 +98,8 @@ abstract contract SwapFactory is ISwapFactory, ISwap, IErrors { { return ( uint16(amountOrId >> 240), - uint256(amountOrId >> 120), - uint256(amountOrId) + uint256(uint120(amountOrId >> 120)), + uint256(uint120(amountOrId)) ); } From dd6f6631a5208fa2e5c69163f44c4f766199d338 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 17 Apr 2024 23:17:51 +0700 Subject: [PATCH 18/49] refactor: removed ITransfer as it's being used func selector --- contracts/Swaplace.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index 24fce2f..cebad6b 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.17; import {IERC165} from "./interfaces/IERC165.sol"; import {ISwaplace} from "./interfaces/ISwaplace.sol"; -import {ITransfer} from "./interfaces/ITransfer.sol"; import {SwapFactory} from "./SwapFactory.sol"; /** @@ -136,6 +135,7 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { (uint16 assetType, uint256 tokenId, uint256 tokenAmount) = decodeAsset( assets[i].amountOrId ); + if (assetType == type(uint16).max) { (bool success, ) = address(assets[i].addr).call( abi.encodeWithSelector(0xf242432a, from, to, tokenId, tokenAmount, "") @@ -147,6 +147,7 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { ); if (!success) revert InvalidCall(); } + assembly { i := add(i, 1) } From 7d5b4ccd53d64a946662f8fab8f4e016da3c099c Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 17 Apr 2024 23:18:04 +0700 Subject: [PATCH 19/49] delete: ITransfer sol file --- contracts/interfaces/ITransfer.sol | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 contracts/interfaces/ITransfer.sol diff --git a/contracts/interfaces/ITransfer.sol b/contracts/interfaces/ITransfer.sol deleted file mode 100644 index cfde089..0000000 --- a/contracts/interfaces/ITransfer.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; - -/** - * @dev Generalized Interface for {IERC20} and {IERC721} `transferFrom` functions. - */ -interface ITransfer { - /** - * @dev See {IERC20-transferFrom} or {IERC721-transferFrom}. - * - * Moves an `amount` for ERC20 or `tokenId` for ERC721 from `from` to `to`. - * - * Emits a {Transfer} event. - */ - function transferFrom(address from, address to, uint256 amountOrId) external; -} From 705084ab011833eaccd0b3d228f6e73614460dc8 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 17 Apr 2024 23:19:14 +0700 Subject: [PATCH 20/49] fix: incorrect padding when address has leading zeroes --- test/utils/SwapFactory.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/utils/SwapFactory.ts b/test/utils/SwapFactory.ts index 1629d87..fd721a2 100644 --- a/test/utils/SwapFactory.ts +++ b/test/utils/SwapFactory.ts @@ -44,12 +44,13 @@ export async function decodeConfig(config: bigint): Promise<{ recipient: bigint | number; value: bigint | number; }> { - // ethers check sum address return { allowed: config >> BigInt(96) == BigInt(0) ? ethers.constants.AddressZero - : ethers.utils.getAddress((config >> BigInt(96)).toString(16)), + : ethers.utils.getAddress( + `0x${(config >> BigInt(96)).toString(16).padStart(40, "0")}`, + ), expiry: (config >> BigInt(64)) & ((BigInt(1) << BigInt(32)) - BigInt(1)), recipient: (config >> BigInt(56)) & ((BigInt(1) << BigInt(8)) - BigInt(1)), value: config & ((BigInt(1) << BigInt(56)) - BigInt(1)), From ef23c09b735cdddcf1fa8925902817e733fc677f Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 17 Apr 2024 23:19:31 +0700 Subject: [PATCH 21/49] feat: tests with ERC1155 --- test/TestSwaplace.test.ts | 67 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index 8feccd1..35e6c5b 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -10,6 +10,7 @@ describe("Swaplace", async function () { let Swaplace: Contract; let MockERC20: Contract; let MockERC721: Contract; + let MockERC1155: Contract; // The signers of the test let deployer: SignerWithAddress; @@ -58,10 +59,76 @@ describe("Swaplace", async function () { Swaplace = await deploy("Swaplace", deployer); MockERC20 = await deploy("MockERC20", deployer); MockERC721 = await deploy("MockERC721", deployer); + MockERC1155 = await deploy("MockERC1155", deployer); }); describe("Creating Swaps", () => { context("Creating different types of Swaps", () => { + it("Should be able to create a 1-1 swap with ERC1155", async function () { + const bidingAddr = [MockERC1155.address]; + const tokenId = 1; + const amount = 3; + const amountAndId = await Swaplace.encodeAsset(tokenId, amount); + const bidingAmountOrId = [amountAndId]; + + const askingAddr = [MockERC721.address]; + const askingAmountOrId = [50]; + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + allowed.address, + currentTimestamp, + 0, + 0, + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await MockERC1155.mint(owner.address, tokenId, amount); + await MockERC1155.connect(owner).setApprovalForAll( + Swaplace.address, + true, + ); + await MockERC721.mint(allowed.address, askingAmountOrId[0]); + await MockERC721.connect(allowed).approve( + Swaplace.address, + askingAmountOrId[0], + ); + + const nextSwapId = Number(await Swaplace.totalSwaps()) + 1; + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs(nextSwapId, owner.address, allowed.address); + + await expect( + await Swaplace.connect(allowed).acceptSwap( + nextSwapId, + receiver.address, + ), + ) + .to.emit(Swaplace, "SwapAccepted") + .withArgs(nextSwapId, owner.address, allowed.address); + + expect( + await MockERC1155.balanceOf(owner.address, tokenId), + ).to.be.equals(0); + expect( + await MockERC1155.balanceOf(receiver.address, tokenId), + ).to.be.equals(amount); + expect(await MockERC721.ownerOf(askingAmountOrId[0])).to.be.equals( + owner.address, + ); + expect(await MockERC721.balanceOf(allowed.address)).to.be.equals(0); + }); + it("Should be able to create a 1-1 swap with ERC20", async function () { const bidingAddr = [MockERC20.address]; const bidingAmountOrId = [50]; From 3ee46605778183b8c01c0e0194ce6a508dbdfd49 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 17 Apr 2024 23:32:51 +0700 Subject: [PATCH 22/49] docs: explaining support for ERC1155 --- contracts/SwapFactory.sol | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/contracts/SwapFactory.sol b/contracts/SwapFactory.sol index 3c6e2e6..9fc5710 100644 --- a/contracts/SwapFactory.sol +++ b/contracts/SwapFactory.sol @@ -26,13 +26,11 @@ import {ISwapFactory} from "./interfaces/ISwapFactory.sol"; * The Swap struct uses an {Asset} struct to represent the asset. This struct is * composed of: * - * - The `address` of the asset. This address can be from an ERC20 or ERC721 contract. + * - The `address` of the token asset. * - The `amount` or `id` of the asset. This amount can be the amount of ERC20 tokens - * or the ID of an ERC721 token. - * - * To use other standards, like ERC1155, you can wrap the ownership of the asset - * in an a trusted contract and Swap as an ERC721. This way, you can tokenize any - * on-chain execution and trade on Swaplace. + * or the NFT ID of an ERC721. + * - The `amount` and `id` can be encoded together in a single uint256, allowing the + * ERC1155 tokens to be swapped. */ abstract contract SwapFactory is ISwapFactory, ISwap, IErrors { /** From e123c977cc27ab8028ab5591e70b6d28969fe610 Mon Sep 17 00:00:00 2001 From: Deepu Date: Wed, 1 May 2024 23:30:00 +0530 Subject: [PATCH 23/49] closes #205 --- test/TestSwapFactory.test.ts | 95 +++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/test/TestSwapFactory.test.ts b/test/TestSwapFactory.test.ts index 472bdb7..b04fcc5 100644 --- a/test/TestSwapFactory.test.ts +++ b/test/TestSwapFactory.test.ts @@ -1,5 +1,5 @@ import { expect } from "chai"; -import { Contract } from "ethers"; +import { BigNumber, Contract } from "ethers"; import { ethers } from "hardhat"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { @@ -156,6 +156,61 @@ describe("Swaplace Factory", async function () { ); }); + it("Should be able to {makeSwap} with native ethers value", async function () { + const currentTimestamp = (await blocktimestamp()) + 2000000; + + const ERC20Asset: Asset = await makeAsset(MockERC20.address, 1000); + const ERC721Asset: Asset = await makeAsset(MockERC721.address, 1); + + const valueToSend: BigNumber = ethers.utils.parseEther("0.2"); + + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 1, + valueToSend.div(1e12), + ); + + const swap = await makeSwap( + owner.address, + config, + [ERC20Asset], + [ERC721Asset], + ); + + const onChainSwap = await Swaplace.makeSwap( + owner.address, + zeroAddress, + currentTimestamp, + 1, + valueToSend.div(1e12), + [ERC20Asset], + [ERC721Asset], + ); + + const [allowed, expiry, recipient, value] = await Swaplace.decodeConfig( + swap.config, + ); + const [onChainAllowed, onChainExpiry, onChainRecipient, onChainValue] = + await Swaplace.decodeConfig(onChainSwap.config); + + expect(swap.owner).to.be.equals(onChainSwap.owner); + expect(expiry).to.be.equals(onChainExpiry); + expect(allowed).to.be.equals(onChainAllowed); + expect(recipient).to.be.equals(onChainRecipient); + expect(value).to.be.equals(onChainValue); + + expect(swap.biding[0].addr).to.be.equals(onChainSwap.biding[0].addr); + expect(swap.biding[0].amountOrId).to.be.equals( + onChainSwap.biding[0].amountOrId, + ); + + expect(swap.asking[0].addr).to.be.equals(onChainSwap.asking[0].addr); + expect(swap.asking[0].amountOrId).to.be.equals( + onChainSwap.asking[0].amountOrId, + ); + }); + it("Should be able to {makeSwap} with multiple assets", async function () { const currentTimestamp = (await blocktimestamp()) + 2000000; @@ -218,6 +273,44 @@ describe("Swaplace Factory", async function () { expect(expiry).to.be.equals(expiry); }); + it("Should be able to {composeSwap} using native ethers value", async function () { + const currentTimestamp = (await blocktimestamp()) + 2000000; + + const bidingAddr = [MockERC20.address, MockERC721.address]; + const bidingAmountOrId = [1000, 1]; + + const askingAddr = [MockERC721.address]; + const askingAmountOrId = [2]; + + const valueToSend: BigNumber = ethers.utils.parseEther("1"); + + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + valueToSend.div(1e12), + ); + + const swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + const [allowed, expiry, recipient, value] = await Swaplace.decodeConfig( + swap.config, + ); + + expect(swap.owner).to.be.equals(owner.address); + expect(allowed).to.be.equals(zeroAddress); + expect(expiry).to.be.equals(expiry); + expect(recipient).to.be.equals(0); + expect(value).to.be.equals(valueToSend.div(1e12)); + }); + it("Should revert using {composeSwap} without minimum expiry", async function () { const expiry = 0; From b46209ec1c92d240074aeda0911bda65a20959e4 Mon Sep 17 00:00:00 2001 From: Deepu Date: Thu, 9 May 2024 14:38:46 +0530 Subject: [PATCH 24/49] closes #214 --- contracts/Swaplace.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index cebad6b..d105556 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -46,7 +46,7 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { uint256 swapId = _totalSwaps; _swaps[swapId] = swap; - (address allowed, , uint8 recipient, uint56 value) = decodeConfig( + (address allowed, , uint8 recipient, uint256 value) = decodeConfig( swap.config ); @@ -72,7 +72,7 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { address allowed, uint32 expiry, uint8 recipient, - uint56 value + uint256 value ) = decodeConfig(swap.config); if (allowed != address(0) && allowed != msg.sender) revert InvalidAddress(); @@ -100,7 +100,7 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { Swap memory swap = _swaps[swapId]; if (swap.owner != msg.sender) revert InvalidAddress(); - (, uint32 expiry, uint8 recipient, uint56 value) = decodeConfig( + (, uint32 expiry, uint8 recipient, uint256 value) = decodeConfig( swap.config ); From 18d04d18a1b701ca88aa1a5efd711a1916f5b603 Mon Sep 17 00:00:00 2001 From: Deepu Date: Fri, 10 May 2024 18:38:45 +0530 Subject: [PATCH 25/49] closes #206 --- test/TestSwaplace.test.ts | 235 +++++++++++++++++++++++++++++++++++++- 1 file changed, 234 insertions(+), 1 deletion(-) diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index 35e6c5b..414307b 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -1,5 +1,5 @@ import { expect } from "chai"; -import { Contract } from "ethers"; +import { BigNumber, Contract } from "ethers"; import { ethers, network } from "hardhat"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { Asset, Swap, composeSwap } from "./utils/SwapFactory"; @@ -326,6 +326,44 @@ describe("Swaplace", async function () { .to.emit(Swaplace, "SwapCreated") .withArgs(await Swaplace.totalSwaps(), owner.address, zeroAddress); }); + + it("Should be able to create a swap with native ethers", async function () { + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [50]; + + const askingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const askingAmountOrId = [50, 100, 150]; + + const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + valueToSend.div(1e12), + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + await expect( + await Swaplace.connect(owner).createSwap(swap, { + value: valueToSend, + }), + ) + .to.emit(Swaplace, "SwapCreated") + .withArgs(await Swaplace.totalSwaps(), owner.address, zeroAddress); + }); }); context("Reverts when creating Swaps", () => { @@ -335,6 +373,40 @@ describe("Swaplace", async function () { Swaplace.connect(allowed).createSwap(swap), ).to.be.revertedWithCustomError(Swaplace, `InvalidAddress`); }); + + it("Should revert when the wrong amount of ethers is sent by the {owner}", async function () { + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [50]; + + const askingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const askingAmountOrId = [50, 100, 150]; + + const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + valueToSend.div(1e12), + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + await expect( + Swaplace.connect(owner).createSwap(swap, { value: 69 }), + ).to.be.revertedWithCustomError(Swaplace, `InvalidValue`); + }); }); }); @@ -466,6 +538,69 @@ describe("Swaplace", async function () { allowed.address, ); }); + + it("Should be able to {acceptSwap} with native ethers", async function () { + await MockERC20.mint(owner.address, 1000); + await MockERC721.mint(allowed.address, 10); + + await MockERC20.connect(owner).approve(Swaplace.address, 1000); + await MockERC721.connect(allowed).approve(Swaplace.address, 10); + + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [50]; + + const askingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const askingAmountOrId = [50, 100, 150]; + + const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); + + const expiry = (await blocktimestamp()) + 1000000; + + const config = await Swaplace.encodeConfig( + allowed.address, + expiry, + 0, + valueToSend.div(1e12), + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await expect( + await Swaplace.connect(owner).createSwap(swap, { + value: valueToSend, + }), + ) + .to.emit(Swaplace, "SwapCreated") + .withArgs( + await Swaplace.totalSwaps(), + owner.address, + allowed.address, + ); + + await expect( + await Swaplace.connect(allowed).acceptSwap( + await Swaplace.totalSwaps(), + receiver.address, + ), + ) + .to.emit(Swaplace, "SwapAccepted") + .withArgs( + await Swaplace.totalSwaps(), + owner.address, + allowed.address, + ); + }); }); context("Reverts when accepting Swaps", () => { @@ -553,6 +688,60 @@ describe("Swaplace", async function () { ), ).to.be.revertedWithCustomError(Swaplace, "InvalidAddress"); }); + + it("Should revert when wrong amount of ethers are sent by the {acceptee}", async function () { + await MockERC20.mint(owner.address, 1000); + await MockERC721.mint(allowed.address, 10); + + await MockERC20.connect(owner).approve(Swaplace.address, 1000); + await MockERC721.connect(allowed).approve(Swaplace.address, 10); + + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [50]; + + const askingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const askingAmountOrId = [50, 100, 150]; + + const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); + + const expiry = (await blocktimestamp()) + 1000000; + + const config = await Swaplace.encodeConfig( + allowed.address, + expiry, + 1, + valueToSend.div(1e12), + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs( + await Swaplace.totalSwaps(), + owner.address, + allowed.address, + ); + + await expect( + Swaplace.connect(allowed).acceptSwap( + await Swaplace.totalSwaps(), + receiver.address, + { value: 69 }, + ), + ).to.be.revertedWithCustomError(Swaplace, "InvalidValue"); + }); }); }); @@ -578,6 +767,50 @@ describe("Swaplace", async function () { Swaplace.connect(owner).acceptSwap(lastSwap, receiver.address), ).to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`); }); + + it("Should be able to {cancelSwap} and return ethers to {owner}", async function () { + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [50]; + + const askingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const askingAmountOrId = [50, 100, 150]; + + const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + valueToSend.div(1e12), + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + const lastSwap = await Swaplace.totalSwaps(); + await expect( + await Swaplace.connect(owner).createSwap(swap, { + value: valueToSend, + }), + ) + .to.emit(Swaplace, "SwapCreated") + .withArgs(await Swaplace.totalSwaps(), owner.address, zeroAddress); + + await expect(Swaplace.connect(owner).cancelSwap(lastSwap)) + .to.emit(Swaplace, "SwapCanceled") + .withArgs(lastSwap, owner.address); + }); }); context("Reverts when canceling Swaps", () => { From 5e91f545c4fb6b8f592aa38ad5580f030327272c Mon Sep 17 00:00:00 2001 From: Deepu Date: Fri, 10 May 2024 22:08:43 +0530 Subject: [PATCH 26/49] closes #207 --- test/TestSwapFactory.test.ts | 113 +++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/test/TestSwapFactory.test.ts b/test/TestSwapFactory.test.ts index b04fcc5..12e88f0 100644 --- a/test/TestSwapFactory.test.ts +++ b/test/TestSwapFactory.test.ts @@ -9,6 +9,7 @@ import { composeSwap, encodeConfig, decodeConfig, + Swap, } from "./utils/SwapFactory"; import { blocktimestamp, deploy } from "./utils/utils"; @@ -17,6 +18,7 @@ describe("Swaplace Factory", async function () { let Swaplace: Contract; let MockERC20: Contract; let MockERC721: Contract; + let MockERC1155: Contract; // The signers of the test let deployer: SignerWithAddress; @@ -30,6 +32,7 @@ describe("Swaplace Factory", async function () { Swaplace = await deploy("Swaplace", deployer); MockERC20 = await deploy("MockERC20", deployer); MockERC721 = await deploy("MockERC721", deployer); + MockERC1155 = await deploy("MockERC1155", deployer); }); it("Should be able to {makeAsset} for ERC20 and ERC721", async function () { @@ -241,6 +244,116 @@ describe("Swaplace Factory", async function () { expect(swap.asking[1]).to.be.equals(ERC721Asset); }); + it("Should be able to {makeSwap} with ERC1155 tokens", async function () { + const bidingAddr = [MockERC1155.address]; + const tokenId = 1; + const amount = 3; + const amountAndId = await Swaplace.encodeAsset(tokenId, amount); + const bidingAmountOrId = [amountAndId]; + + const askingAddr = [MockERC721.address]; + const askingAmountOrId = [50]; + + const ERC1155Asset: Asset = await makeAsset( + bidingAddr[0], + bidingAmountOrId[0], + ); + const ERC721Asset: Asset = await makeAsset( + askingAddr[0], + askingAmountOrId[0], + ); + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); + + const swap = await makeSwap( + owner.address, + config, + [ERC1155Asset], + [ERC721Asset], + ); + + const onChainSwap = await Swaplace.makeSwap( + owner.address, + zeroAddress, + currentTimestamp, + 0, + 0, + [ERC1155Asset], + [ERC721Asset], + ); + + const [allowed, expiry, recipient, value] = await Swaplace.decodeConfig( + swap.config, + ); + + const [onChainAllowed, onChainExpiry, onChainRecipient, onChainValue] = + await Swaplace.decodeConfig(onChainSwap.config); + + expect(swap.owner).to.be.equals(onChainSwap.owner); + expect(expiry).to.be.equals(onChainExpiry); + expect(allowed).to.be.equals(onChainAllowed); + expect(recipient).to.be.equals(onChainRecipient); + expect(value).to.be.equals(onChainValue); + expect(swap.biding[0].addr).to.be.equals(onChainSwap.biding[0].addr); + expect(swap.biding[0].amountOrId).to.be.equals( + onChainSwap.biding[0].amountOrId, + ); + + expect(swap.asking[0].addr).to.be.equals(onChainSwap.asking[0].addr); + expect(swap.asking[0].amountOrId).to.be.equals( + onChainSwap.asking[0].amountOrId, + ); + }); + + it("Should be able to {composeSwap} using ERC1155", async function () { + const bidingAddr = [MockERC1155.address]; + const tokenId = 1; + const amount = 3; + const amountAndId = await Swaplace.encodeAsset(tokenId, amount); + const bidingAmountOrId = [amountAndId]; + + const askingAddr = [MockERC721.address]; + const askingAmountOrId = [50]; + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + const [allowed, expiry, recipient, value] = await Swaplace.decodeConfig( + swap.config, + ); + + expect(swap.owner).to.be.equals(owner.address); + expect(allowed).to.be.equals(zeroAddress); + expect(expiry).to.be.equals(expiry); + expect(recipient).to.be.equals(0); + expect(value).to.be.equals(0); + expect(swap.biding[0].addr).to.be.equals(bidingAddr[0]); + expect(swap.biding[0].amountOrId).to.be.equals(bidingAmountOrId[0]); + + expect(swap.asking[0].addr).to.be.equals(askingAddr[0]); + expect(swap.asking[0].amountOrId).to.be.equals(askingAmountOrId[0]); + }); + it("Should be able to {composeSwap} using both ERC20, ERC721", async function () { const currentTimestamp = (await blocktimestamp()) + 2000000; From 5ba014991fe03605580157a8a57c6cac56a28fa3 Mon Sep 17 00:00:00 2001 From: Deepu Date: Sat, 11 May 2024 00:01:37 +0530 Subject: [PATCH 27/49] closes #208 --- test/TestSwaplace.test.ts | 184 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index 35e6c5b..e12ca1c 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -129,6 +129,190 @@ describe("Swaplace", async function () { expect(await MockERC721.balanceOf(allowed.address)).to.be.equals(0); }); + it("Should be able to create a 1-N swap with ERC1155", async function () { + const bidingAddr = [ + MockERC1155.address, + MockERC1155.address, + MockERC1155.address, + ]; + const tokenId1 = 69; + const amount1 = 3; + const tokenId2 = 2; + const amount2 = 6; + const tokenId3 = 3; + const amount3 = 9; + const amountAndId1 = await Swaplace.encodeAsset(tokenId1, amount1); + const amountAndId2 = await Swaplace.encodeAsset(tokenId2, amount2); + const amountAndId3 = await Swaplace.encodeAsset(tokenId3, amount3); + const bidingAmountOrId = [amountAndId1, amountAndId2, amountAndId3]; + + const askingAddr = [MockERC721.address]; + const askingAmountOrId = [69]; + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + allowed.address, + currentTimestamp, + 0, + 0, + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await MockERC1155.mint(owner.address, tokenId1, amount1); + await MockERC1155.mint(owner.address, tokenId2, amount2); + await MockERC1155.mint(owner.address, tokenId3, amount3); + await MockERC1155.connect(owner).setApprovalForAll( + Swaplace.address, + true, + ); + await MockERC721.mint(allowed.address, askingAmountOrId[0]); + await MockERC721.connect(allowed).approve( + Swaplace.address, + askingAmountOrId[0], + ); + + const nextSwapId = Number(await Swaplace.totalSwaps()) + 1; + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs(nextSwapId, owner.address, allowed.address); + + await expect( + await Swaplace.connect(allowed).acceptSwap( + nextSwapId, + receiver.address, + ), + ) + .to.emit(Swaplace, "SwapAccepted") + .withArgs(nextSwapId, owner.address, allowed.address); + + expect( + await MockERC1155.balanceOf(owner.address, tokenId1), + ).to.be.equals(0); + expect( + await MockERC1155.balanceOf(owner.address, tokenId2), + ).to.be.equals(0); + expect( + await MockERC1155.balanceOf(owner.address, tokenId3), + ).to.be.equals(0); + expect( + await MockERC1155.balanceOf(receiver.address, tokenId1), + ).to.be.equals(amount1); + expect( + await MockERC1155.balanceOf(receiver.address, tokenId2), + ).to.be.equals(amount2); + expect( + await MockERC1155.balanceOf(receiver.address, tokenId3), + ).to.be.equals(amount3); + expect(await MockERC721.ownerOf(askingAmountOrId[0])).to.be.equals( + owner.address, + ); + expect(await MockERC721.balanceOf(allowed.address)).to.be.equals(0); + }); + + it("Should be able to create a N-N swap with ERC1155", async function () { + const bidingAddr = [ + MockERC1155.address, + MockERC1155.address, + MockERC1155.address, + ]; + const tokenId1 = 4; + const amount1 = 69; + const tokenId2 = 5; + const amount2 = 69; + const tokenId3 = 6; + const amount3 = 69; + const amountAndId1 = await Swaplace.encodeAsset(tokenId1, amount1); + const amountAndId2 = await Swaplace.encodeAsset(tokenId2, amount2); + const amountAndId3 = await Swaplace.encodeAsset(tokenId3, amount3); + const bidingAmountOrId = [amountAndId1, amountAndId2, amountAndId3]; + + const askingAddr = [ + MockERC721.address, + MockERC721.address, + MockERC721.address, + ]; + const askingAmountOrId = [59, 79, 89]; + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + allowed.address, + currentTimestamp, + 0, + 0, + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await MockERC1155.mint(owner.address, tokenId1, amount1); + await MockERC1155.mint(owner.address, tokenId2, amount2); + await MockERC1155.mint(owner.address, tokenId3, amount3); + await MockERC1155.connect(owner).setApprovalForAll( + Swaplace.address, + true, + ); + await MockERC721.mint(allowed.address, askingAmountOrId[0]); + await MockERC721.mint(allowed.address, askingAmountOrId[1]); + await MockERC721.mint(allowed.address, askingAmountOrId[2]); + await MockERC721.connect(allowed).setApprovalForAll( + Swaplace.address, + true, + ); + + const nextSwapId = Number(await Swaplace.totalSwaps()) + 1; + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs(nextSwapId, owner.address, allowed.address); + + await expect( + await Swaplace.connect(allowed).acceptSwap( + nextSwapId, + receiver.address, + ), + ) + .to.emit(Swaplace, "SwapAccepted") + .withArgs(nextSwapId, owner.address, allowed.address); + + expect( + await MockERC1155.balanceOf(owner.address, tokenId1), + ).to.be.equals(0); + expect( + await MockERC1155.balanceOf(owner.address, tokenId2), + ).to.be.equals(0); + expect( + await MockERC1155.balanceOf(owner.address, tokenId3), + ).to.be.equals(0); + expect( + await MockERC1155.balanceOf(receiver.address, tokenId1), + ).to.be.equals(amount1); + expect( + await MockERC1155.balanceOf(receiver.address, tokenId2), + ).to.be.equals(amount2); + expect( + await MockERC1155.balanceOf(receiver.address, tokenId3), + ).to.be.equals(amount3); + expect(await MockERC721.ownerOf(askingAmountOrId[0])).to.be.equals( + owner.address, + ); + expect(await MockERC721.balanceOf(allowed.address)).to.be.equals(0); + }); + it("Should be able to create a 1-1 swap with ERC20", async function () { const bidingAddr = [MockERC20.address]; const bidingAmountOrId = [50]; From 1da56f7de2167b2a0da76b17759f4d0bada823d8 Mon Sep 17 00:00:00 2001 From: Rafael Sanches Date: Fri, 10 May 2024 17:57:37 -0300 Subject: [PATCH 28/49] feat: Kakarot rpc and ipfs on mockerc721 --- .env.sample | 1 + contracts/mock/MockERC721.sol | 4 ++-- hardhat.config.ts | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.env.sample b/.env.sample index 9c426f7..18dd70c 100644 --- a/.env.sample +++ b/.env.sample @@ -28,6 +28,7 @@ SEPOLIA_RPC_URL=https://ethereum-sepolia.publicnode.com MUMBAI_RPC_URL=https://polygon-mumbai-bor.publicnode.com FUJI_RPC_URL=https://avalanche-fuji-c-chain.publicnode.com BNB_TESTNET_RPC_URL=https://bsc-testnet.publicnode.com +KAKAROT_SEPOLIA_RPC_URL=https://sepolia-rpc.kakarot.org # MAINNETS ETH_RPC_URL="https://eth.llamarpc.com" diff --git a/contracts/mock/MockERC721.sol b/contracts/mock/MockERC721.sol index f9e63ff..80bbfc8 100644 --- a/contracts/mock/MockERC721.sol +++ b/contracts/mock/MockERC721.sol @@ -16,6 +16,6 @@ contract MockERC721 is ERC721 { function tokenURI( uint256 ) public view virtual override returns (string memory) { - return "ipfs://QmQJnHseE9VPw5qVxuEhxTiZ7avzgkCdFz69rg86UvTZdk/"; + return "ipfs://QmWodCkovJk18U75g8Veg6rCnw7951vQvTjYfS7J3nMFma/"; } -} +} \ No newline at end of file diff --git a/hardhat.config.ts b/hardhat.config.ts index 5932a6c..5e9334d 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -27,7 +27,7 @@ const config: HardhatUserConfig = { * @dev Testnets */ kakarot: { - url: `${process.env.KAKAROT_RPC_URL}`, + url: `${process.env.KAKAROT_SEPOLIA_RPC_URL}`, accounts: [`${DEPLOYER_PRIVATE_KEY}`], }, sepolia: { From 9ffdc10ad3d58223b742da7e60609065313d036a Mon Sep 17 00:00:00 2001 From: Deepu Date: Mon, 13 May 2024 21:10:50 +0530 Subject: [PATCH 29/49] closes #210 --- scripts/deployMock.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/scripts/deployMock.ts b/scripts/deployMock.ts index f8266d3..660c10e 100644 --- a/scripts/deployMock.ts +++ b/scripts/deployMock.ts @@ -11,6 +11,7 @@ async function main() { /// @dev The returned contract instance that will be deployed via the deploy function in utils. let MockERC20: Contract; let MockERC721: Contract; + let MockERC1155: Contract; /// @dev will throw an error if any of the accounts was not set up correctly. try { @@ -27,6 +28,7 @@ async function main() { // We are deploying both contracts to test the user flux with the entire functionality. MockERC20 = await deploy("MockERC20", signers[0]); MockERC721 = await deploy("MockERC721", signers[0]); + MockERC1155 = await deploy("MockERC1155", signers[0]); // @dev Log Contract address and the Tx hash which can be searched on Etherscan (or any other block explorer). console.log( @@ -43,13 +45,22 @@ async function main() { MockERC721.deployTransaction.hash, ); + console.log( + "\nContract %s \nDeployed to %s \nAt Tx %s\n", + "MockERC1155", + MockERC1155.address, + MockERC1155.deployTransaction.hash, + ); + /// @dev Store the contract addresses in the .env file. await storeEnv(MockERC20.address, "ERC20_ADDRESS", true); await storeEnv(MockERC721.address, "ERC721_ADDRESS", true); + await storeEnv(MockERC1155.address, "ERC1155_ADDRESS", true); /// @dev Awaits for the transaction to be mined. await MockERC20.deployed(); await MockERC721.deployed(); + await MockERC1155.deployed(); } main().catch((error) => { From bb4b941898ff6b669140ad8556f8302633a461bf Mon Sep 17 00:00:00 2001 From: Deepu Date: Tue, 14 May 2024 00:19:05 +0530 Subject: [PATCH 30/49] closes #211 --- scripts/approve.ts | 19 +++++++++++++++++-- scripts/mint.ts | 20 ++++++++++++++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/scripts/approve.ts b/scripts/approve.ts index 5137afd..34faa3c 100755 --- a/scripts/approve.ts +++ b/scripts/approve.ts @@ -10,12 +10,18 @@ async function main() { /// @dev This is the list of mock deployments addresses that were stored in the `.env` file. const ERC20_ADDRESS = process.env.ERC20_ADDRESS || 0x0; const ERC721_ADDRESS = process.env.ERC721_ADDRESS || 0x0; + const ERC1155_ADDRESS = process.env.ERC1155_ADDRESS || 0x0; /// @dev The Swaplace address also needs to be instance to receive the approvals. const SWAPLACE_ADDRESS = process.env.SWAPLACE_ADDRESS || 0x0; /// @dev Will throw an error if any of the addresses were not set in the `.env` file. - if (!ERC20_ADDRESS || !ERC721_ADDRESS || !SWAPLACE_ADDRESS) { + if ( + !ERC20_ADDRESS || + !ERC721_ADDRESS || + !SWAPLACE_ADDRESS || + !ERC1155_ADDRESS + ) { throw new Error( - "Invalid ERC20, ERC721 or Swaplace address, please check if the addresse in the `.env` file is set up correctly.", + "Invalid ERC20, ERC721, ERC1155 or Swaplace address, please check if the addresse in the `.env` file is set up correctly.", ); } @@ -39,6 +45,7 @@ async function main() { /// @dev The returned contract instance that will be deployed via the deploy function in utils. let MockERC20: Contract; let MockERC721: Contract; + let MockERC1155: Contract; /// @dev will throw an error if any of the accounts was not set up correctly. try { @@ -63,6 +70,11 @@ async function main() { ERC721_ADDRESS, signers[0], ); + MockERC1155 = await ethers.getContractAt( + "MockERC1155", + ERC1155_ADDRESS, + signers[0], + ); } catch (error) { throw new Error( `Error deploying one of the Mock Contracts. @@ -76,11 +88,13 @@ async function main() { /// @dev Responses from the minting transactions. let txErc20; let txErc721; + let txErc1155; /// @dev We are approving the signer address to spend the amount of tokens. try { txErc20 = await MockERC20.approve(SWAPLACE_ADDRESS, amount); txErc721 = await MockERC721.approve(SWAPLACE_ADDRESS, tokenId); + txErc1155 = await MockERC1155.setApprovalForAll(SWAPLACE_ADDRESS, true); } catch (error) { throw new Error( `Error while approving the tokens. Make sure that the approve function is @@ -97,6 +111,7 @@ async function main() { tokenId, txErc721.hash, ); + console.log("\nERC1155 Approved all tokens \nAt Tx %s", txErc1155.hash); } main().catch((error) => { diff --git a/scripts/mint.ts b/scripts/mint.ts index f96d286..64ee9a0 100644 --- a/scripts/mint.ts +++ b/scripts/mint.ts @@ -11,10 +11,11 @@ async function main() { /// @dev This is the list of mock deployments addresses that were stored in the `.env` file. const ERC20_ADDRESS = process.env.ERC20_ADDRESS || 0x0; const ERC721_ADDRESS = process.env.ERC721_ADDRESS || 0x0; + const ERC1155_ADDRESS = process.env.ERC1155_ADDRESS || 0x0; /// @dev Will throw an error if any of the addresses were not set in the `.env` file. - if (!ERC20_ADDRESS || !ERC721_ADDRESS) { + if (!ERC20_ADDRESS || !ERC721_ADDRESS || !ERC1155_ADDRESS) { throw new Error( - "Invalid ERC20 or ERC721 address, please check if the addresse in the `.env` file is set up correctly.", + "Invalid ERC20 or ERC721 or ERC1155 address, please check if the addresses in the `.env` file are set up correctly.", ); } @@ -25,6 +26,7 @@ async function main() { /// @dev The returned contract instance that will be deployed via the deploy function in utils. let MockERC20: Contract; let MockERC721: Contract; + let MockERC1155: Contract; /// @dev will throw an error if any of the accounts was not set up correctly. try { @@ -49,6 +51,11 @@ async function main() { ERC721_ADDRESS, signers[0], ); + MockERC1155 = await ethers.getContractAt( + "MockERC1155", + ERC1155_ADDRESS, + signers[0], + ); } catch (error) { throw new Error( `Error deploying one of the Mock Contracts. @@ -69,6 +76,7 @@ async function main() { /// @dev Responses from the minting transactions. let txErc20; let txErc721; + let txErc1155; /// @dev Minting function will throw an error if the minting fails. /// We are minting for the first signer of `hardhat.config.ts` 1000 @@ -77,6 +85,7 @@ async function main() { try { txErc20 = await MockERC20.mint(signers[0].address, amount); txErc721 = await MockERC721.mint(signers[0].address, tokenId); + txErc1155 = await MockERC1155.mint(signers[0].address, tokenId, amount); } catch (error) { throw new Error( `Error while minting tokens. Make sure that the minting function is @@ -93,12 +102,19 @@ async function main() { tokenId, txErc721.hash, ); + console.log( + "\nERC1155 Minted %s tokens with ID #%s \nAt Tx %s", + amount, + tokenId, + txErc1155.hash, + ); await storeEnv(tokenId, "TOKEN_ID", false); /// @dev Awaits for the transaction to be mined. await txErc20.wait(); await txErc721.wait(); + await txErc1155.wait(); } main().catch((error) => { From 4a2849fd34063fb8a768763a2fe7c1572a16666f Mon Sep 17 00:00:00 2001 From: Deepu Date: Sat, 18 May 2024 00:09:00 +0530 Subject: [PATCH 31/49] closes #212 --- scripts/createSwap.ts | 32 ++++++++++++++++++++++++-------- test/utils/SwapFactory.ts | 21 +++++++++++++++++++++ 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/scripts/createSwap.ts b/scripts/createSwap.ts index a382b64..28de0a7 100644 --- a/scripts/createSwap.ts +++ b/scripts/createSwap.ts @@ -1,19 +1,30 @@ import { ethers } from "hardhat"; import { Contract } from "ethers"; import { blocktimestamp, storeEnv } from "../test/utils/utils"; -import { Swap, composeSwap } from "../test/utils/SwapFactory"; +import { + Swap, + composeSwap, + encodeAsset, + encodeConfig, +} from "../test/utils/SwapFactory"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; export async function main() { /// @dev This is the list of mock deployments addresses that were stored in the `.env` file. const ERC20_ADDRESS = process.env.ERC20_ADDRESS || 0x0; const ERC721_ADDRESS = process.env.ERC721_ADDRESS || 0x0; + const ERC1155_ADDRESS = process.env.ERC1155_ADDRESS || 0x0; /// @dev The Swaplace address also needs to be instance to receive the approvals. const SWAPLACE_ADDRESS = process.env.SWAPLACE_ADDRESS || 0x0; /// @dev Will throw an error if any of the addresses were not set in the `.env` file. - if (!ERC20_ADDRESS || !ERC721_ADDRESS || !SWAPLACE_ADDRESS) { + if ( + !ERC20_ADDRESS || + !ERC721_ADDRESS || + !SWAPLACE_ADDRESS || + !ERC1155_ADDRESS + ) { throw new Error( - "Invalid ERC20, ERC721 or Swaplace address, please check if the addresse in the `.env` file is set up correctly.", + "Invalid ERC20, ERC721, ERC1155 or Swaplace address, please check if the addresses in the `.env` file are set up correctly.", ); } @@ -65,21 +76,26 @@ export async function main() { ); } - /// @dev Fill the Swap struct + /// @dev Fill the Swap struct and config const owner = signers[0].address; const allowed = ethers.constants.AddressZero; const expiry = (await blocktimestamp()) * 2; + const recipient = 0; + const value = 0; + + /// @dev Encode ERC1155 asset + const amountAndId = await encodeAsset(BigInt(tokenId), BigInt(amount)); /// @dev Build the biding assets - const bidingAddr = [ERC20_ADDRESS]; - const bidingAmountOrId = [amount]; + const bidingAddr = [ERC20_ADDRESS, ERC1155_ADDRESS]; + const bidingAmountOrId = [BigInt(amount), amountAndId]; /// @dev Build the asking assets const askingAddr = [ERC721_ADDRESS]; - const askingAmountOrId = [tokenId]; + const askingAmountOrId = [BigInt(tokenId)]; /// @dev Pack the config together - const config = (BigInt(allowed) << BigInt(96)) | BigInt(expiry); + const config = await encodeConfig(allowed, expiry, recipient, value); /// @dev Compose the above swap into the Swap Struct const swap: Swap = await composeSwap( diff --git a/test/utils/SwapFactory.ts b/test/utils/SwapFactory.ts index fd721a2..e7afabd 100644 --- a/test/utils/SwapFactory.ts +++ b/test/utils/SwapFactory.ts @@ -88,6 +88,27 @@ export async function makeAsset( return asset; } +/** + * @dev See {ISwapFactory-encodeAsset}. + */ +export async function encodeAsset( + tokenId: bigint | number, + tokenAmount: bigint | number, +): Promise { + // if the amount or ID is negative, it will throw an error + if (tokenId < 0 || tokenAmount < 0) { + throw new Error("tokenId or tokenAmount cannot be less than 0"); + } + + const uint16Max = 65535; + + return BigInt( + (BigInt(uint16Max) << BigInt(240)) | + (BigInt(tokenId) << BigInt(120)) | + BigInt(tokenAmount), + ); +} + /** * @dev See {ISwapFactory-makeSwap}. */ From 247965f6df1a06d8b12d0cc588786142df3c1ce0 Mon Sep 17 00:00:00 2001 From: Deepu Date: Sat, 18 May 2024 15:23:16 +0530 Subject: [PATCH 32/49] Requested changes made #217 --- test/TestSwaplace.test.ts | 83 ++++++++++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 10 deletions(-) diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index 414307b..8f99819 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -327,7 +327,7 @@ describe("Swaplace", async function () { .withArgs(await Swaplace.totalSwaps(), owner.address, zeroAddress); }); - it("Should be able to create a swap with native ethers", async function () { + it("Should be able to {createSwap} with native ethers sent by the {owner}", async function () { const bidingAddr = [MockERC20.address]; const bidingAmountOrId = [50]; @@ -339,7 +339,7 @@ describe("Swaplace", async function () { const askingAmountOrId = [50, 100, 150]; const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); - + const currentTimestamp = (await blocktimestamp()) + 1000000; const config = await Swaplace.encodeConfig( zeroAddress, @@ -374,7 +374,7 @@ describe("Swaplace", async function () { ).to.be.revertedWithCustomError(Swaplace, `InvalidAddress`); }); - it("Should revert when the wrong amount of ethers is sent by the {owner}", async function () { + it("Should revert when the wrong amount of ethers are sent by the {owner}", async function () { const bidingAddr = [MockERC20.address]; const bidingAmountOrId = [50]; @@ -539,7 +539,7 @@ describe("Swaplace", async function () { ); }); - it("Should be able to {acceptSwap} with native ethers", async function () { + it("Should be able to {acceptSwap} with native ethers sent by the {owner}", async function () { await MockERC20.mint(owner.address, 1000); await MockERC721.mint(allowed.address, 10); @@ -555,11 +555,8 @@ describe("Swaplace", async function () { MockERC20.address, ]; const askingAmountOrId = [50, 100, 150]; - const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); - const expiry = (await blocktimestamp()) + 1000000; - const config = await Swaplace.encodeConfig( allowed.address, expiry, @@ -576,6 +573,9 @@ describe("Swaplace", async function () { askingAmountOrId, ); + const balanceBefore: BigNumber = await receiver.getBalance(); + const expectedBalance: BigNumber = balanceBefore.add(valueToSend); + await expect( await Swaplace.connect(owner).createSwap(swap, { value: valueToSend, @@ -600,6 +600,72 @@ describe("Swaplace", async function () { owner.address, allowed.address, ); + + const balanceAfter: BigNumber = await receiver.getBalance(); + await expect(balanceAfter).to.be.equals(expectedBalance); + }); + + it("Should be able to {acceptSwap} with native ethers sent by the {acceptee}", async function () { + await MockERC20.mint(owner.address, 1000); + await MockERC721.mint(allowed.address, 10); + + await MockERC20.connect(owner).approve(Swaplace.address, 1000); + await MockERC721.connect(allowed).approve(Swaplace.address, 10); + + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [50]; + + const askingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const askingAmountOrId = [50, 100, 150]; + const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); + const expiry = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + allowed.address, + expiry, + 1, + valueToSend.div(1e12), + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs( + await Swaplace.totalSwaps(), + owner.address, + allowed.address, + ); + + const balanceBefore: BigNumber = await owner.getBalance(); + const expectedBalance: BigNumber = balanceBefore.add(valueToSend); + + await expect( + Swaplace.connect(allowed).acceptSwap( + await Swaplace.totalSwaps(), + receiver.address, + { value: valueToSend }, + ), + ) + .to.emit(Swaplace, "SwapAccepted") + .withArgs( + await Swaplace.totalSwaps(), + owner.address, + allowed.address, + ); + + const balanceAfter: BigNumber = await owner.getBalance(); + await expect(balanceAfter).to.be.equals(expectedBalance); }); }); @@ -705,11 +771,8 @@ describe("Swaplace", async function () { MockERC20.address, ]; const askingAmountOrId = [50, 100, 150]; - const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); - const expiry = (await blocktimestamp()) + 1000000; - const config = await Swaplace.encodeConfig( allowed.address, expiry, From 9da0bd46e943f4ce78ff38cc7ec6163655068f73 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Sun, 19 May 2024 11:15:05 +0700 Subject: [PATCH 33/49] fix: removing length tests from echidna as it no longer exists --- contracts/echidna/TestSwapFactory.sol | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/contracts/echidna/TestSwapFactory.sol b/contracts/echidna/TestSwapFactory.sol index 3ebd53a..931c506 100644 --- a/contracts/echidna/TestSwapFactory.sol +++ b/contracts/echidna/TestSwapFactory.sol @@ -58,16 +58,4 @@ contract TestFactory is SwapFactory { _asset ); } - - function echidna_revert_invalid_length() public view { - makeSwap( - address(0), - address(0), - uint32(block.timestamp + 100), - 0, - 0, - new Asset[](0), - new Asset[](0) - ); - } } From 2bed24fbbae62156be0a051e0a2387f056e77915 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Sun, 19 May 2024 16:34:09 +0700 Subject: [PATCH 34/49] fix: adding ERC1155 on sample --- .env.sample | 1 + 1 file changed, 1 insertion(+) diff --git a/.env.sample b/.env.sample index 9c426f7..8981182 100644 --- a/.env.sample +++ b/.env.sample @@ -3,6 +3,7 @@ SWAPLACE_ADDRESS=0x000000000000000000000000000000000000000000 # Mocks last deployed contracts addresses. ERC20_ADDRESS=0x000000000000000000000000000000000000000000 ERC721_ADDRESS=0x000000000000000000000000000000000000000000 +ERC1155_ADDRESS=0x000000000000000000000000000000000000000000 # Amount of ERC20 tokens to be minted for signer address. AMOUNT=1000 # Token ID of ERC721 to be minted for signer address. From c9f3df0093f36d33b010765e29a0339722447020 Mon Sep 17 00:00:00 2001 From: Deepu Date: Sun, 19 May 2024 15:20:33 +0530 Subject: [PATCH 35/49] removed white space #217 --- test/TestSwaplace.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index 8f99819..0f34d48 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -339,7 +339,6 @@ describe("Swaplace", async function () { const askingAmountOrId = [50, 100, 150]; const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); - const currentTimestamp = (await blocktimestamp()) + 1000000; const config = await Swaplace.encodeConfig( zeroAddress, From b4b46cc71c108ffd4cd8a23c68e8bf130be40856 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Sun, 19 May 2024 18:52:50 +0700 Subject: [PATCH 36/49] fix: encodeAsset was not being exported --- test/utils/SwapFactory.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/utils/SwapFactory.ts b/test/utils/SwapFactory.ts index e7afabd..5f2c277 100644 --- a/test/utils/SwapFactory.ts +++ b/test/utils/SwapFactory.ts @@ -101,6 +101,13 @@ export async function encodeAsset( } const uint16Max = 65535; + const uint120Max = BigInt(2) ** BigInt(120) - BigInt(1); + + if (tokenId > uint120Max || tokenAmount > uint120Max) { + throw new Error( + "Maxium bits exceeded for tokenId or tokenAmount. Max: 120 bits.", + ); + } return BigInt( (BigInt(uint16Max) << BigInt(240)) | @@ -203,4 +210,5 @@ module.exports = { composeSwap, encodeConfig, decodeConfig, + encodeAsset, }; From e84906afa90c59303b57d47656496bbab1ed556b Mon Sep 17 00:00:00 2001 From: Deepu Date: Thu, 6 Jun 2024 00:11:53 +0530 Subject: [PATCH 37/49] closes #227 --- contracts/Swaplace.sol | 7 +++++-- contracts/interfaces/IErrors.sol | 5 +++++ test/TestSwaplace.test.ts | 34 ++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index d105556..365aa6d 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -50,8 +50,11 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { swap.config ); - if (value > 0 && recipient == 0) { - if (value * 1e12 != msg.value) revert InvalidValue(); + if (msg.value > 0) { + if (recipient == 0) { + if (value * 1e12 != msg.value) revert InvalidValue(); + } + else revert InvalidSender(); } emit SwapCreated(swapId, msg.sender, allowed); diff --git a/contracts/interfaces/IErrors.sol b/contracts/interfaces/IErrors.sol index 1395042..2219165 100644 --- a/contracts/interfaces/IErrors.sol +++ b/contracts/interfaces/IErrors.sol @@ -24,4 +24,9 @@ interface IErrors { * @dev Displayed when a low level call failed to execute. */ error InvalidCall(); + + /** + * @dev Displayed when the wrong caller sends ethers. + */ + error InvalidSender(); } diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index 54ed5ac..b608744 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -590,6 +590,40 @@ describe("Swaplace", async function () { Swaplace.connect(owner).createSwap(swap, { value: 69 }), ).to.be.revertedWithCustomError(Swaplace, `InvalidValue`); }); + + it("Should revert when the {owner} sends ethers while not being set as the {recipient}", async function () { + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [50]; + + const askingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const askingAmountOrId = [50, 100, 150]; + + const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 1, + valueToSend.div(1e12), + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + await expect( + Swaplace.connect(owner).createSwap(swap, { value: valueToSend }), + ).to.be.revertedWithCustomError(Swaplace, `InvalidSender`); + }); }); }); From bd5a9020233af71ce4580f598884a86a5a8d66b7 Mon Sep 17 00:00:00 2001 From: Deepu Date: Thu, 6 Jun 2024 15:56:46 +0530 Subject: [PATCH 38/49] Fixed a blunder #227 --- contracts/Swaplace.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index 365aa6d..03ef9aa 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -50,11 +50,11 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { swap.config ); - if (msg.value > 0) { + if (value > 0) { if (recipient == 0) { if (value * 1e12 != msg.value) revert InvalidValue(); } - else revert InvalidSender(); + else if (msg.value > 0) revert InvalidSender(); } emit SwapCreated(swapId, msg.sender, allowed); From bc8d90f694117415e0b8ce1248eda35ebac7734f Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 6 Jun 2024 18:44:33 +0700 Subject: [PATCH 39/49] remove: unused error --- contracts/interfaces/IErrors.sol | 5 ----- 1 file changed, 5 deletions(-) diff --git a/contracts/interfaces/IErrors.sol b/contracts/interfaces/IErrors.sol index 2219165..1395042 100644 --- a/contracts/interfaces/IErrors.sol +++ b/contracts/interfaces/IErrors.sol @@ -24,9 +24,4 @@ interface IErrors { * @dev Displayed when a low level call failed to execute. */ error InvalidCall(); - - /** - * @dev Displayed when the wrong caller sends ethers. - */ - error InvalidSender(); } From 175709b48fbd792a2212bbfb715f4d42247e339f Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 6 Jun 2024 18:44:51 +0700 Subject: [PATCH 40/49] fix: wrong identation and error name --- contracts/Swaplace.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index 03ef9aa..968f5d0 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -53,8 +53,7 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { if (value > 0) { if (recipient == 0) { if (value * 1e12 != msg.value) revert InvalidValue(); - } - else if (msg.value > 0) revert InvalidSender(); + } else if (msg.value > 0) revert InvalidValue(); } emit SwapCreated(swapId, msg.sender, allowed); From c39dfaf3c6aa3af745510b6ee292b487d752ca8a Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 6 Jun 2024 18:46:47 +0700 Subject: [PATCH 41/49] fix: wrong test name --- test/TestSwaplace.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index b608744..c896b43 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -591,7 +591,7 @@ describe("Swaplace", async function () { ).to.be.revertedWithCustomError(Swaplace, `InvalidValue`); }); - it("Should revert when the {owner} sends ethers while not being set as the {recipient}", async function () { + it("Should revert when the {owner} sends ethers while being the {recipient}", async function () { const bidingAddr = [MockERC20.address]; const bidingAmountOrId = [50]; From 694c4a1115cf2baa469ce8a8bc498d355d6c15bb Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 6 Jun 2024 18:48:46 +0700 Subject: [PATCH 42/49] docs: explaining the recipient and value var usage --- contracts/SwapFactory.sol | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/contracts/SwapFactory.sol b/contracts/SwapFactory.sol index 9fc5710..acaa2ad 100644 --- a/contracts/SwapFactory.sol +++ b/contracts/SwapFactory.sol @@ -18,8 +18,11 @@ import {ISwapFactory} from "./interfaces/ISwapFactory.sol"; * - The `allowed` address is the address that can accept the Swap. If the allowed * address is the zero address, then anyone can accept the Swap. * - The `expiry` date is the timestamp that the Swap will be available to accept. - * - The `recipient` is the address that will receive the ETH. - * - The `value` is the amount of ETH that the recipient will receive. + * - The `recipient` is the address that will receive the ETH as type uint8. If the + * recipient is equals to 0, the acceptee will receive the ETH. If the recipient is + * between 1<>255 then the recipient will be the owner of the Swap. + * - The `value` is the amount of ETH that the recipient will receive with a maximum + * of 6 decimals (0.000001 ETH). The contract will fill the value up to 18 decimals. * - The `biding` are the assets that the owner is offering. * - The `asking` are the assets that the owner wants in exchange. * From 490f89d6802e8c8bf536a6e3dc0fd5cb8c195929 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 6 Jun 2024 19:02:17 +0700 Subject: [PATCH 43/49] fix: erc20/erc721 was using wrong amountOrId parameter --- contracts/Swaplace.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index 968f5d0..a5c33e2 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -145,7 +145,7 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { if (!success) revert InvalidCall(); } else { (bool success, ) = address(assets[i].addr).call( - abi.encodeWithSelector(0x23b872dd, from, to, tokenAmount) + abi.encodeWithSelector(0x23b872dd, from, to, assets[i].amountOrId) ); if (!success) revert InvalidCall(); } From 41ef97dd37e6e4cbb992c540870e36e652f54e5f Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 6 Jun 2024 19:12:20 +0700 Subject: [PATCH 44/49] fix: no need to check for recipient since value can only be present when the owner truly sent it --- contracts/Swaplace.sol | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index a5c33e2..7d19180 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -102,14 +102,12 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { Swap memory swap = _swaps[swapId]; if (swap.owner != msg.sender) revert InvalidAddress(); - (, uint32 expiry, uint8 recipient, uint256 value) = decodeConfig( - swap.config - ); + (, uint32 expiry, , uint256 value) = decodeConfig(swap.config); if (expiry < block.timestamp) revert InvalidExpiry(); _swaps[swapId].config = 0; - if (value > 0 && recipient == 0) _payNativeEth(msg.sender, value * 1e12); + if (value > 0) _payNativeEth(msg.sender, value * 1e12); emit SwapCanceled(swapId, msg.sender); } From 091789757b5396c20623a78ef22981b9aaa51e6e Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 6 Jun 2024 19:12:38 +0700 Subject: [PATCH 45/49] fix: wrong return variable name --- contracts/SwapFactory.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/SwapFactory.sol b/contracts/SwapFactory.sol index acaa2ad..4e039fc 100644 --- a/contracts/SwapFactory.sol +++ b/contracts/SwapFactory.sol @@ -80,7 +80,7 @@ abstract contract SwapFactory is ISwapFactory, ISwap, IErrors { function encodeAsset( uint120 tokenId, uint120 tokenAmount - ) public pure returns (uint256 amountOrId) { + ) public pure returns (uint256 amountAndId) { return (uint256(type(uint16).max) << 240) | (uint256(tokenId) << 120) | From 55963c8884eeeaa221deec657b33456450521250 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 6 Jun 2024 19:13:50 +0700 Subject: [PATCH 46/49] docs: added param types and stricly rules of variables --- contracts/interfaces/ISwapFactory.sol | 51 ++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/contracts/interfaces/ISwapFactory.sol b/contracts/interfaces/ISwapFactory.sol index a636c00..a975ce6 100644 --- a/contracts/interfaces/ISwapFactory.sol +++ b/contracts/interfaces/ISwapFactory.sol @@ -9,6 +9,9 @@ import {ISwap} from "./ISwap.sol"; interface ISwapFactory { /** * @dev Make an {ISwap-Asset} struct to work with token standards. + * + * @param addr is the address of the token asset. + * @param amountOrId is the amount of tokens or the ID of the NFT. */ function makeAsset( address addr, @@ -21,6 +24,10 @@ interface ISwapFactory { * NOTE: Different from the {makeAsset} function, this function is used to * encode the token ID and token amount into a single uint256. This is made * to work with the ERC1155 standard. + * + * @param addr is the address of the token asset. + * @param tokenId is the ID of the ERC1155 token. + * @param tokenAmount is the amount of the ERC1155 token. */ function make1155Asset( address addr, @@ -34,6 +41,15 @@ interface ISwapFactory { * Requirements: * * - `expiry` cannot be in the past. + * + * @param owner is the address that created the Swap. + * @param allowed is the address that can accept the Swap. If the allowed + * address is the zero address, then anyone can accept the Swap. + * @param expiry is the timestamp that the Swap will be available to accept. + * @param recipient is the address that will receive the ETH. `0` for the acceptee + * and `1<>255` for the owner. + * @param value is the amount of ETH that the recipient will receive. Maximum of + * 6 decimals (0.000001 ETH). The contract will fill the value up to 18 decimals. */ function makeSwap( address owner, @@ -48,22 +64,36 @@ interface ISwapFactory { /** * @dev Encode `tokenId` and `tokenAmount` into a single uint256 while adding a flag * to indicate that it's an ERC1155 token. + * + * NOTE: The flag is set to 0xFFFFFFFF. + * + * @param tokenId is the ID of the ERC1155 token. + * @param tokenAmount is the amount of the ERC1155 token. */ function encodeAsset( uint120 tokenId, uint120 tokenAmount - ) external pure returns (uint256 amountOrId); + ) external pure returns (uint256 amountAndId); /** - * @dev Decode `amountOrId` to check if the first 4 bytes are set to 0xFFFFFFFF. + * @dev Decode `amountOrId` returning the first 4 bytes to try match with 0xFFFFFFFF. * If the flag is set to 0xFFFFFFFF, then it's an ERC1155 standard, otherwise it's - * assumed to be an ERC20 or ERC721 standard. + * assumed to be an ERC20 or ERC721. * * NOTE: If it's an ERC1155 token, then the next 120 bits are the token ID and the next * 120 bits are the token amount. + * + * WARNING: Swaplace cannot handle ERC1155 tokens where the ID or the amount is greater + * than 120 bits. + * + * @param amountAndId is the amount of tokens and the ID of the ERC1155 token. + * @return tokenType is the flag to indicate the token standard. + * @return tokenId is the ID of the ERC1155 token. + * @return tokenAmount is the amount of the ERC1155 token. + * */ function decodeAsset( - uint256 amountOrId + uint256 amountAndId ) external pure @@ -71,16 +101,27 @@ interface ISwapFactory { /** * @dev This function uses bitwise to return an encoded uint256 of the following parameters. + * + * @param allowed address is the address that can accept the Swap. If the allowed + * address is the zero address, then anyone can accept the Swap. + * @param expiry date is the timestamp that the Swap will be available to accept. + * @param recipient is the address that will receive the ETH as type uint8. If the + * recipient is equals to 0, the acceptee will receive the ETH. If the recipient is + * between 1<>255 then the recipient will be the owner of the Swap. + * @param value is the amount of ETH that the recipient will receive with a maximum + * of 6 decimals (0.000001 ETH). The contract will fill the value up to 18 decimals. */ function encodeConfig( address allowed, uint32 expiry, uint8 recipient, uint56 value - ) external pure returns (uint256); + ) external pure returns (uint256 config); /** * @dev Decode `config` into their respective variables. + * + * @param config is the encoded uint256 configuration of the Swap. */ function decodeConfig( uint256 config From ce31d03af3fe3563b558cf06ddd851e3c608aa49 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 6 Jun 2024 19:14:01 +0700 Subject: [PATCH 47/49] fix: wrong error name --- test/TestSwaplace.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index c896b43..f499e53 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -622,7 +622,7 @@ describe("Swaplace", async function () { ); await expect( Swaplace.connect(owner).createSwap(swap, { value: valueToSend }), - ).to.be.revertedWithCustomError(Swaplace, `InvalidSender`); + ).to.be.revertedWithCustomError(Swaplace, `InvalidValue`); }); }); }); From d452e4fa03c33f3ce5590459da96ced91f747a65 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 6 Jun 2024 19:41:06 +0700 Subject: [PATCH 48/49] fix: cancel swap test wasn't being validated --- test/TestSwaplace.test.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index f499e53..9b86dfb 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -1078,7 +1078,6 @@ describe("Swaplace", async function () { askingAmountOrId, ); - const lastSwap = await Swaplace.totalSwaps(); await expect( await Swaplace.connect(owner).createSwap(swap, { value: valueToSend, @@ -1087,9 +1086,18 @@ describe("Swaplace", async function () { .to.emit(Swaplace, "SwapCreated") .withArgs(await Swaplace.totalSwaps(), owner.address, zeroAddress); - await expect(Swaplace.connect(owner).cancelSwap(lastSwap)) - .to.emit(Swaplace, "SwapCanceled") - .withArgs(lastSwap, owner.address); + const balanceBefore = await owner.getBalance(); + + const lastSwap = await Swaplace.totalSwaps(); + const tx = await Swaplace.connect(owner).cancelSwap(lastSwap); + const receipt = await tx.wait(); + const gasUsed = receipt.gasUsed; + const gasPrice = receipt.effectiveGasPrice; + + const balanceAfter = await owner.getBalance(); + expect(balanceBefore.add(valueToSend)).to.be.equals( + balanceAfter.add(gasPrice.mul(gasUsed)), + ); }); }); @@ -1102,8 +1110,6 @@ describe("Swaplace", async function () { }); it("Should revert when {expiry} is smaller than {block.timestamp}", async function () { - const [, expiry, ,] = await Swaplace.decodeConfig(swap.config); - await network.provider.send("evm_increaseTime", [2000000]); const lastSwap = await Swaplace.totalSwaps(); From a773428f6c3d4be7eac3a13dd52823cab6ba1a0a Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 6 Jun 2024 20:56:45 +0700 Subject: [PATCH 49/49] fix: expired swaps couldnt withdraw their ether --- contracts/Swaplace.sol | 12 +++- test/TestSwaplace.test.ts | 140 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+), 3 deletions(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index 7d19180..444858e 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -102,12 +102,18 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { Swap memory swap = _swaps[swapId]; if (swap.owner != msg.sender) revert InvalidAddress(); - (, uint32 expiry, , uint256 value) = decodeConfig(swap.config); + (, uint32 expiry, uint8 recipient, uint256 value) = decodeConfig( + swap.config + ); - if (expiry < block.timestamp) revert InvalidExpiry(); + if (expiry < block.timestamp && (value == 0 || recipient > 0)) { + revert InvalidExpiry(); + } _swaps[swapId].config = 0; - if (value > 0) _payNativeEth(msg.sender, value * 1e12); + if (value > 0 && recipient == 0) { + _payNativeEth(msg.sender, value * 1e12); + } emit SwapCanceled(swapId, msg.sender); } diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index 9b86dfb..13be114 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -1099,6 +1099,146 @@ describe("Swaplace", async function () { balanceAfter.add(gasPrice.mul(gasUsed)), ); }); + + it("Should be able to {cancelSwap} and return ethers to {owner} even after expiration", async function () { + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [50]; + + const askingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const askingAmountOrId = [50, 100, 150]; + + const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + valueToSend.div(1e12), + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await expect( + await Swaplace.connect(owner).createSwap(swap, { + value: valueToSend, + }), + ) + .to.emit(Swaplace, "SwapCreated") + .withArgs(await Swaplace.totalSwaps(), owner.address, zeroAddress); + + await network.provider.send("evm_increaseTime", [1000000]); + + const balanceBefore = await owner.getBalance(); + + const lastSwap = await Swaplace.totalSwaps(); + const tx = await Swaplace.connect(owner).cancelSwap(lastSwap); + const receipt = await tx.wait(); + const gasUsed = receipt.gasUsed; + const gasPrice = receipt.effectiveGasPrice; + + const balanceAfter = await owner.getBalance(); + expect(balanceBefore.add(valueToSend)).to.be.equals( + balanceAfter.add(gasPrice.mul(gasUsed)), + ); + }); + + it("Should be able to {cancelSwap} before expiration if the recipient is the {owner}", async function () { + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [50]; + + const askingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const askingAmountOrId = [50, 100, 150]; + + const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 1, + valueToSend.div(1e12), + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs(await Swaplace.totalSwaps(), owner.address, zeroAddress); + + const lastSwap = await Swaplace.totalSwaps(); + await expect(await Swaplace.connect(owner).cancelSwap(lastSwap)) + .to.emit(Swaplace, "SwapCanceled") + .withArgs(lastSwap, owner.address); + + await expect( + Swaplace.connect(owner).cancelSwap(lastSwap), + ).to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`); + }); + + it("Should not be able to {cancelSwap} after expiration if the recipient is the {owner}", async function () { + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [50]; + + const askingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const askingAmountOrId = [50, 100, 150]; + + const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 1, + valueToSend.div(1e12), + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs(await Swaplace.totalSwaps(), owner.address, zeroAddress); + + await network.provider.send("evm_increaseTime", [1000000]); + + const lastSwap = await Swaplace.totalSwaps(); + await expect( + Swaplace.connect(owner).cancelSwap(lastSwap), + ).to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`); + }); }); context("Reverts when canceling Swaps", () => {