diff --git a/.env.sample b/.env.sample index d272e26..69a4b0d 100644 --- a/.env.sample +++ b/.env.sample @@ -1,11 +1,9 @@ # Swaplace deployed contracts addresses -SWAPLACE_ADDRESS=0x5FbDB2315678afecb367f032d93F642f64180aa3 +SWAPLACE_ADDRESS=0xD8E3580C1b6f117c5b35DdD01dd9e50d9487501D # These are public known private keys and are here as an example. # Funds in these accounts are at risk of being stolen. DEPLOYER_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 -SWAP_CREATOR_PRIVATE_KEY=0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d -SWAP_ACCEPTEE_PRIVATE_KEY=0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a # Etherscan and similar services API keys ETHERSCAN_API_KEY=YourApiKeyToken diff --git a/.prettierrc.yml b/.prettierrc.yml index c8d93ae..98e57f6 100644 --- a/.prettierrc.yml +++ b/.prettierrc.yml @@ -1,4 +1,5 @@ -trailingComma: "es5" -tabWidth: 4 -semi: false +trailingComma: "all" singleQuote: false +printWidth: 80 +tabWidth: 2 +semi: true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..30a5b28 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,76 @@ +# Contribution Guidelines + +Thank you for your interest in contributing to Swaplace! The project is an open source build by the community. We are welcome any type of contribution, suggestion or improvement, no matter how small. + +### Contents + +- [Open Source Guideline](#how-to-contribute) +- [Opening an Issue](#opening-issue) +- [Writing Commit Messages](#writing-commit-messages) +- [Submitting Pull Requests](#submiting-pull-requests) +- [Code Review](#code-review) +- [Coding Style](#coding-style) +- [Get in touch](#get-in-touch) + +## How to Contribute + +There are many ways to contribute, but here are a few steps to start: + +1. Fork the repository in GitHub +2. Check if you have any issue, suggestion or improvement to work in. + + - We manage all the issues using [GitHub Projects](https://github.com/orgs/blockful-io/projects/3) + +3. Send a message in the issue like + `I'm interested in this issue` or ask for more information like `Hey, can you provide more information about this ... ?`and the mantainers will assigned or provide more context about your issue. **After that, you are asigned to the issue.** + +4. If a task is already assigned but you believe you can expedite the process, feel free to comment, requesting to assist or speed up the task. Additionally, you may open a pull request directly because we prioritize both code efficiency and delivery speed. Thus, we encourage multiple individuals to review the same issues and debate together to enhance the code's security. + +### Congratulations ! Now you can start work in the issue and everyone knows you are assigned. + +## Opening Issue + +Before opening an issue, please check if there is no issue open. If there is, feel free to comment or suggest more details. We are always open to feedback and receptive to suggestions! + +- Before [creating an issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/creating-an-issue#creating-an-issue-from-a-project), check if your fork is updated. + +## Writing Commit Messages + +We are using [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) + +Some examples below: + +- feat: adds or remove a new feature +- fix: fixes a bug +- refactor: rewrite/restructure your code +- chore: changes to the build process or auxiliary tools and libraries such as documentation generation +- style: changes do not affect the meaning (white-space, formatting, missing semi-colons, etc) +- test: add missing tests or correcting existing tests +- docs: affect documentation only + +## Submiting Pull Requests + +You can resolve an issue and after that submit a pull request fixing an issue and remember to [reference that issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword#linking-a-pull-request-to-an-issue-using-a-keyword). When submit a PR, put the code below with the number of the issue you are closing +`Example: closes #1` + +## Code Review + +The maintainers will review your PR. If something is missing or a fix needs to be made, the maintainers will let you know in the PR comment. + +## Coding Style + +In Swaplace please use this .prettierrc.yml configuration to make the code standardized. + +``` +{ + trailingComma: "all" + singleQuote: false + printWidth: 80 + tabWidth: 2 + semi: true +} +``` + +## Get in Touch + +If you need to get in contact with the repository maintainers, please reach out in our [Discord](https://discord.gg/B6uDmm7hvC). diff --git a/README.md b/README.md index 1259451..3127f60 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ This repository is subjected to incentives for the community to contribute to th ## Features -- **Create Swaps**: A Swap has an `owner` and an `allowed` address. The `owner` is the one that can cancel the swap while the `allowed` address is the one that can execute the swap but anyone can accept if it's set as the Zero Address. A Swap also has an `expiry` period in seconds. The Swap can only be executed before the expiry period is reached. The `Asset` type represents on one hand the bidding assets and on the other hand the asking assets. +- **Create Swaps**: A Swap has an `owner` and an `allowed` address. The `owner` is the one that can cancel the swap while the `allowed` address is the one that can execute the swap but anyone can accept if it's set as the Zero Address. A Swap also has an `expiry` period in seconds. The Swap can only be executed before the expiry period is reached. The `Asset` type represents on one hand the bidding assets and on the other hand the asking assets. ``` struct Swap { @@ -31,11 +31,11 @@ This repository is subjected to incentives for the community to contribute to th } ``` -- **Accept Swaps**: You can accept swaps that have an `allowed` address equal to your or the `Zero Address`. As long as you provide the asked assets. +- **Accept Swaps**: You can accept swaps that have an `allowed` address equal to your or the `Zero Address`. As long as you provide the asked assets. -- **Cancel Swaps**: You can cancel swaps that haven't been `expired`. +- **Cancel Swaps**: You can cancel swaps that haven't been `expired`. -- **Swap Factory**: Aids new swap creations to be used on Swaplace. Check `contracts/SwapFactory.sol`. +- **Swap Factory**: Aids new swap creations to be used on Swaplace. Check `contracts/SwapFactory.sol`. ## Setup @@ -138,4 +138,4 @@ yarn deploy ## Contributing -- To know more about how you can contribute [see our notion page](https://blockful.notion.site/Swaplace-Call-for-Contributors-6e4895d2a7264f679439ab2c124603fe). +- To know more about how you can contribute [see our notion page](https://blockful.notion.site/Swaplace-Call-for-Contributors-6e4895d2a7264f679439ab2c124603fe). \ No newline at end of file diff --git a/contracts/SwapFactory.sol b/contracts/SwapFactory.sol index e577888..4061c1d 100644 --- a/contracts/SwapFactory.sol +++ b/contracts/SwapFactory.sol @@ -26,41 +26,37 @@ import {ISwapFactory} from "./interfaces/ISwapFactory.sol"; * * - The `address` of the asset. This address can be from an ERC20 or ERC721 contract. * - The `amount` or `id` of the asset. This amount can be the amount of ERC20 tokens - * or the id of an ERC721 token. + * 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. */ abstract contract SwapFactory is ISwapFactory, ISwap, IErrors { - /** - * @dev See {ISwapFactory-makeAsset}. - */ - function makeAsset( - address addr, - uint256 amountOrId - ) public pure virtual returns (Asset memory) { - return Asset(addr, amountOrId); - } + /** + * @dev See {ISwapFactory-makeAsset}. + */ + function makeAsset( + address addr, + uint256 amountOrId + ) public pure virtual returns (Asset memory) { + return Asset(addr, amountOrId); + } - /** - * @dev See {ISwapFactory-makeSwap}. - */ - function makeSwap( - address owner, - address allowed, - uint256 expiry, - Asset[] memory biding, - Asset[] memory asking - ) public view virtual returns (Swap memory) { - if (expiry < block.timestamp) { - revert InvalidExpiry(expiry); - } + /** + * @dev See {ISwapFactory-makeSwap}. + */ + function makeSwap( + address owner, + address allowed, + uint256 expiry, + 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(); - } + if (biding.length == 0 || asking.length == 0) revert InvalidAssetsLength(); - return Swap(owner, allowed, expiry, biding, asking); - } + return Swap(owner, allowed, expiry, biding, asking); + } } diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index 0ce484c..525d9a6 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -14,131 +14,119 @@ import {SwapFactory} from "./SwapFactory.sol"; * `approve` or `permit` function. */ contract Swaplace is SwapFactory, ISwaplace, IERC165 { - /// @dev Swap Identifier counter. - uint256 private _totalSwaps; + /// @dev Swap Identifier counter. + uint256 private _totalSwaps; - /// @dev Mapping of Swap ID to Swap struct. See {ISwap-Swap}. - mapping(uint256 => Swap) private _swaps; + /// @dev Mapping of Swap ID to Swap struct. See {ISwap-Swap}. + mapping(uint256 => Swap) private _swaps; - /** - * @dev See {ISwaplace-createSwap}. - */ - function createSwap(Swap calldata swap) public returns (uint256) { - if (swap.owner != msg.sender) { - revert InvalidAddress(msg.sender); - } + /** + * @dev See {ISwaplace-createSwap}. + */ + function createSwap(Swap calldata swap) public returns (uint256) { + if (swap.owner != msg.sender) revert InvalidAddress(msg.sender); - if (swap.expiry < block.timestamp) { - revert InvalidExpiry(swap.expiry); - } + if (swap.expiry < block.timestamp) revert InvalidExpiry(swap.expiry); - if (swap.biding.length == 0 || swap.asking.length == 0) { - revert InvalidAssetsLength(); - } + if (swap.biding.length == 0 || swap.asking.length == 0) + revert InvalidAssetsLength(); - unchecked { - assembly { - sstore(_totalSwaps.slot, add(sload(_totalSwaps.slot), 1)) - } - } - - uint256 swapId = _totalSwaps; + unchecked { + assembly { + sstore(_totalSwaps.slot, add(sload(_totalSwaps.slot), 1)) + } + } - _swaps[swapId] = swap; + uint256 swapId = _totalSwaps; - emit SwapCreated(swapId, msg.sender, swap.expiry); + _swaps[swapId] = swap; - return swapId; - } + emit SwapCreated(swapId, msg.sender, swap.allowed, swap.expiry); - /** - * @dev See {ISwaplace-acceptSwap}. - */ - function acceptSwap(uint256 id) public returns (bool) { - Swap memory swap = _swaps[id]; - - if (swap.allowed != address(0) && swap.allowed != msg.sender) { - revert InvalidAddress(msg.sender); - } - - if (swap.expiry < block.timestamp) { - revert InvalidExpiry(swap.expiry); - } - - _swaps[id].expiry = 0; - - Asset[] memory assets = swap.asking; - - for (uint256 i = 0; i < assets.length; ) { - ITransfer(assets[i].addr).transferFrom( - msg.sender, - swap.owner, - assets[i].amountOrId - ); - unchecked { - i++; - } - } - - assets = swap.biding; - - for (uint256 i = 0; i < assets.length; ) { - ITransfer(assets[i].addr).transferFrom( - swap.owner, - msg.sender, - assets[i].amountOrId - ); - unchecked { - i++; - } - } - - emit SwapAccepted(id, msg.sender); - - return true; - } + return swapId; + } - /** - * @dev See {ISwaplace-cancelSwap}. - */ - function cancelSwap(uint256 id) public { - Swap memory swap = _swaps[id]; + /** + * @dev See {ISwaplace-acceptSwap}. + */ + function acceptSwap(uint256 swapId) public returns (bool) { + Swap memory swap = _swaps[swapId]; - if (swap.owner != msg.sender) { - revert InvalidAddress(msg.sender); - } + if (swap.allowed != address(0) && swap.allowed != msg.sender) + revert InvalidAddress(msg.sender); - if (swap.expiry < block.timestamp) { - revert InvalidExpiry(swap.expiry); - } + if (swap.expiry < block.timestamp) revert InvalidExpiry(swap.expiry); - _swaps[id].expiry = 0; + _swaps[swapId].expiry = 0; - emit SwapCanceled(id, msg.sender); - } + Asset[] memory assets = swap.asking; - /** - * @dev See {ISwaplace-getSwap}. - */ - function getSwap(uint256 id) public view returns (Swap memory) { - return _swaps[id]; + for (uint256 i = 0; i < assets.length; ) { + ITransfer(assets[i].addr).transferFrom( + msg.sender, + swap.owner, + assets[i].amountOrId + ); + unchecked { + i++; + } } - /** - * @dev See {IERC165-supportsInterface}. - */ - function supportsInterface( - bytes4 interfaceID - ) external pure override(IERC165) returns (bool) { - return - interfaceID == type(IERC165).interfaceId || - interfaceID == type(ISwaplace).interfaceId; + assets = swap.biding; + + for (uint256 i = 0; i < assets.length; ) { + ITransfer(assets[i].addr).transferFrom( + swap.owner, + msg.sender, + assets[i].amountOrId + ); + unchecked { + i++; + } } - /** - * @dev Getter function for _totalSwaps. - */ - function totalSwaps() public view returns (uint256) { - return _totalSwaps; - } + emit SwapAccepted(swapId, msg.sender); + + return true; + } + + /** + * @dev See {ISwaplace-cancelSwap}. + */ + function cancelSwap(uint256 swapId) public { + Swap memory swap = _swaps[swapId]; + + if (swap.owner != msg.sender) revert InvalidAddress(msg.sender); + + if (swap.expiry < block.timestamp) revert InvalidExpiry(swap.expiry); + + _swaps[swapId].expiry = 0; + + emit SwapCanceled(swapId, msg.sender); + } + + /** + * @dev See {ISwaplace-getSwap}. + */ + function getSwap(uint256 swapId) public view returns (Swap memory) { + return _swaps[swapId]; + } + + /** + * @dev See {IERC165-supportsInterface}. + */ + function supportsInterface( + bytes4 interfaceID + ) external pure override(IERC165) returns (bool) { + return + 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 ad48115..5847825 100644 --- a/contracts/echidna/TestSwapFactory.sol +++ b/contracts/echidna/TestSwapFactory.sol @@ -4,56 +4,56 @@ pragma solidity ^0.8.17; import "../SwapFactory.sol"; contract TestFactory is SwapFactory { - Asset[] private _asset; - - function has_values() public { - _asset[0] = makeAsset(address(0), 0); - assert(_asset[0].addr == address(0)); - assert(_asset[0].amountOrId == 0); - } - - function make_asset_array( - address addr, - uint256 amountOrId - ) public pure returns (Asset[] memory) { - Asset[] memory asset = new Asset[](1); - asset[0] = makeAsset(addr, amountOrId); - - assert(asset[0].addr == addr); - assert(asset[0].amountOrId == amountOrId); - return asset; - } - - function make_valid_swap( - address owner, - address addr, - uint256 amountOrId - ) public view returns (Swap memory) { - Swap memory swap = makeSwap( - owner, - address(0), - block.timestamp + 1000, - make_asset_array(addr, amountOrId), - make_asset_array(addr, amountOrId) - ); - - assert(swap.expiry > block.timestamp); - assert(swap.biding.length > 0); - assert(swap.asking.length > 0); - return swap; - } - - function echidna_revert_invalid_expiry() public view { - makeSwap(address(0), address(0), block.timestamp - 100, _asset, _asset); - } - - function echidna_revert_invalid_length() public view { - makeSwap( - address(0), - address(0), - block.timestamp + 100, - new Asset[](0), - new Asset[](0) - ); - } + Asset[] private _asset; + + function has_values() public { + _asset[0] = makeAsset(address(0), 0); + assert(_asset[0].addr == address(0)); + assert(_asset[0].amountOrId == 0); + } + + function make_asset_array( + address addr, + uint256 amountOrId + ) public pure returns (Asset[] memory) { + Asset[] memory asset = new Asset[](1); + asset[0] = makeAsset(addr, amountOrId); + + assert(asset[0].addr == addr); + assert(asset[0].amountOrId == amountOrId); + return asset; + } + + function make_valid_swap( + address owner, + address addr, + uint256 amountOrId + ) public view returns (Swap memory) { + Swap memory swap = makeSwap( + owner, + address(0), + block.timestamp + 1000, + make_asset_array(addr, amountOrId), + make_asset_array(addr, amountOrId) + ); + + assert(swap.expiry > block.timestamp); + assert(swap.biding.length > 0); + assert(swap.asking.length > 0); + return swap; + } + + function echidna_revert_invalid_expiry() public view { + makeSwap(address(0), address(0), block.timestamp - 100, _asset, _asset); + } + + function echidna_revert_invalid_length() public view { + makeSwap( + address(0), + address(0), + block.timestamp + 100, + new Asset[](0), + new Asset[](0) + ); + } } diff --git a/contracts/echidna/TestSwaplace.sol b/contracts/echidna/TestSwaplace.sol index 003a89d..8e76b3e 100644 --- a/contracts/echidna/TestSwaplace.sol +++ b/contracts/echidna/TestSwaplace.sol @@ -6,44 +6,44 @@ import "../mock/MockERC20.sol"; import "../Swaplace.sol"; contract TestSwaplace is TestFactory { - MockERC20 private _token; - Swaplace private _swaplace; - - constructor() { - _token = new MockERC20(); - _swaplace = new Swaplace(); - _token.mintTo(address(this), 100); - } - - function echidna_create_swap() public returns (bool) { - uint256 initId = _swaplace.totalSwaps(); - - uint256 id = _swaplace.createSwap( - make_valid_swap(address(this), address(_token), 100) - ); - - require(_swaplace.getSwap(id).owner == address(this)); - require(id > 0); - return (initId + 1 == id); - } - - function echidna_accept_swap() public returns (bool) { - echidna_create_swap(); - _token.approve(address(_swaplace), type(uint256).max); - - uint256 lastId = _swaplace.totalSwaps(); - return (_swaplace.acceptSwap(lastId)); - } - - function echidna_id_overflow() public view returns (bool) { - return _swaplace.totalSwaps() < type(uint256).max; - } - - function echidna_id_never_zero_after_init() public view returns (bool) { - Swaplace.Swap memory swap = _swaplace.getSwap(0); - return - swap.owner == address(0) - ? _swaplace.totalSwaps() == 0 - : _swaplace.totalSwaps() != 0; - } + MockERC20 private _token; + Swaplace private _swaplace; + + constructor() { + _token = new MockERC20(); + _swaplace = new Swaplace(); + _token.mintTo(address(this), 100); + } + + function echidna_create_swap() public returns (bool) { + uint256 initId = _swaplace.totalSwaps(); + + uint256 id = _swaplace.createSwap( + make_valid_swap(address(this), address(_token), 100) + ); + + require(_swaplace.getSwap(id).owner == address(this)); + require(id > 0); + return (initId + 1 == id); + } + + function echidna_accept_swap() public returns (bool) { + echidna_create_swap(); + _token.approve(address(_swaplace), type(uint256).max); + + uint256 lastId = _swaplace.totalSwaps(); + return (_swaplace.acceptSwap(lastId)); + } + + function echidna_id_overflow() public view returns (bool) { + return _swaplace.totalSwaps() < type(uint256).max; + } + + function echidna_id_never_zero_after_init() public view returns (bool) { + Swaplace.Swap memory swap = _swaplace.getSwap(0); + return + swap.owner == address(0) + ? _swaplace.totalSwaps() == 0 + : _swaplace.totalSwaps() != 0; + } } diff --git a/contracts/interfaces/IERC165.sol b/contracts/interfaces/IERC165.sol index 9d901a5..0dd0eba 100644 --- a/contracts/interfaces/IERC165.sol +++ b/contracts/interfaces/IERC165.sol @@ -13,13 +13,13 @@ pragma solidity ^0.8.17; * For an implementation, see {ERC165}. */ interface IERC165 { - /** - * @dev Returns true if this contract implements the interface defined by - * `interfaceId`. See the corresponding - * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] - * to learn more about how these ids are created. - * - * This function call must use less than 30 000 gas. - */ - function supportsInterface(bytes4 interfaceId) external view returns (bool); -} \ No newline at end of file + /** + * @dev Returns true if this contract implements the interface defined by + * `interfaceId`. See the corresponding + * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] + * to learn more about how these ids are created. + * + * This function call must use less than 30 000 gas. + */ + function supportsInterface(bytes4 interfaceId) external view returns (bool); +} diff --git a/contracts/interfaces/IErrors.sol b/contracts/interfaces/IErrors.sol index 76e86f9..25e3f99 100644 --- a/contracts/interfaces/IErrors.sol +++ b/contracts/interfaces/IErrors.sol @@ -5,23 +5,23 @@ pragma solidity ^0.8.17; * @dev Errors only interface for the {Swaplace} implementations. */ interface IErrors { - /** - * @dev Displayed when the caller is not the owner of the swap. - */ - error InvalidAddress(address caller); + /** + * @dev Displayed when the caller is not the owner of the swap. + */ + error InvalidAddress(address caller); - /** - * @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. - */ - error InvalidAssetsLength(); + /** + * @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. + */ + error InvalidAssetsLength(); - /** - * @dev Displayed when the `expiry` date is in the past. - */ - error InvalidExpiry(uint256 timestamp); + /** + * @dev Displayed when the `expiry` date is in the past. + */ + error InvalidExpiry(uint256 timestamp); } diff --git a/contracts/interfaces/ISwap.sol b/contracts/interfaces/ISwap.sol index ada6c6c..8267d3d 100644 --- a/contracts/interfaces/ISwap.sol +++ b/contracts/interfaces/ISwap.sol @@ -5,37 +5,37 @@ pragma solidity ^0.8.17; * @dev Interface for the Swap Struct, used in the {Swaplace} implementation. */ interface ISwap { - /** - * @dev Assets can be ERC20 or ERC721. - * - * It is composed of: - * - `addr` of the asset. - * - `amountOrId` of the asset based on the standard. - * - * NOTE: `amountOrId` is the `amount` of ERC20 or the `tokenId` of ERC721. - */ - struct Asset { - address addr; - uint256 amountOrId; - } + /** + * @dev Assets can be ERC20 or ERC721. + * + * It is composed of: + * - `addr` of the asset. + * - `amountOrId` of the asset based on the standard. + * + * NOTE: `amountOrId` is the `amount` of ERC20 or the `tokenId` of ERC721. + */ + struct Asset { + address addr; + uint256 amountOrId; + } - /** - * @dev The Swap struct is the heart of Swaplace. - * - * It is composed of: - * - `owner` of the Swap. - * - `allowed` address to accept the Swap. - * - `expiry` date of the Swap. - * - `biding` assets that are being bided by the owner. - * - `asking` assets that are being asked by the owner. - * - * NOTE: When `allowed` address is the zero address, anyone can accept the Swap. - */ - struct Swap { - address owner; - address allowed; - uint256 expiry; - Asset[] biding; - Asset[] asking; - } + /** + * @dev The Swap struct is the heart of Swaplace. + * + * It is composed of: + * - `owner` of the Swap. + * - `allowed` address to accept the Swap. + * - `expiry` date of the Swap. + * - `biding` assets that are being bided by the owner. + * - `asking` assets that are being asked by the owner. + * + * NOTE: When `allowed` address is the zero address, anyone can accept the Swap. + */ + struct Swap { + address owner; + address allowed; + uint256 expiry; + Asset[] biding; + Asset[] asking; + } } diff --git a/contracts/interfaces/ISwapFactory.sol b/contracts/interfaces/ISwapFactory.sol index 51d1cf7..4abac76 100644 --- a/contracts/interfaces/ISwapFactory.sol +++ b/contracts/interfaces/ISwapFactory.sol @@ -7,28 +7,28 @@ import {ISwap} from "./ISwap.sol"; * @dev Interface of the {SwapFactory} implementation. */ 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. - */ - function makeAsset( - address addr, - uint256 amountOrId - ) external pure returns (ISwap.Asset memory); + /** + * @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. + */ + function makeAsset( + address addr, + uint256 amountOrId + ) external pure returns (ISwap.Asset memory); - /** - * @dev Build a swap struct to use in the {Swaplace-createSwap} function. - * - * Requirements: - * - * - `expiry` cannot be in the past timestamp. - * - `biding` and `asking` cannot be empty. - */ - function makeSwap( - address owner, - address allowed, - uint256 expiry, - ISwap.Asset[] memory assets, - ISwap.Asset[] memory asking - ) external view returns (ISwap.Swap memory); + /** + * @dev Build a swap struct to use in the {Swaplace-createSwap} function. + * + * Requirements: + * + * - `expiry` cannot be in the past timestamp. + * - `biding` and `asking` cannot be empty. + */ + function makeSwap( + address owner, + address allowed, + uint256 expiry, + ISwap.Asset[] memory assets, + ISwap.Asset[] memory asking + ) external view returns (ISwap.Swap memory); } diff --git a/contracts/interfaces/ISwaplace.sol b/contracts/interfaces/ISwaplace.sol index 62c6271..af45e53 100644 --- a/contracts/interfaces/ISwaplace.sol +++ b/contracts/interfaces/ISwaplace.sol @@ -7,77 +7,78 @@ import {ISwap} from "./ISwap.sol"; * @dev Interface of the {Swaplace} implementation. */ interface ISwaplace { - /** - * @dev Emitted when a new Swap is created. - */ - event SwapCreated( - uint256 indexed id, - address indexed owner, - uint256 indexed expiry - ); + /** + * @dev Emitted when a new Swap is created. + */ + event SwapCreated( + uint256 indexed swapId, + address indexed owner, + address indexed allowed, + uint256 expiry + ); - /** - * @dev Emitted when a Swap is accepted. - */ - event SwapAccepted(uint256 indexed id, address indexed acceptee); + /** + * @dev Emitted when a Swap is accepted. + */ + event SwapAccepted(uint256 indexed swapId, address indexed acceptee); - /** - * @dev Emitted when a Swap is canceled. - */ - event SwapCanceled(uint256 indexed id, address indexed owner); + /** + * @dev Emitted when a Swap is canceled. + */ + event SwapCanceled(uint256 indexed swapId, address indexed owner); - /** - * @dev Allow users to create a Swap. Each new Swap self-increments its id by one. - * - * Requirements: - * - * - `owner` must be the caller address. - * - `expiry` should be bigger than timestamp. - * - `biding` and `asking` must not be empty. - * - * Emits a {SwapCreated} event. - */ - function createSwap(ISwap.Swap calldata Swap) external returns (uint256); + /** + * @dev Allow users to create a Swap. Each new Swap self-increments its ID by one. + * + * Requirements: + * + * - `owner` must be the caller address. + * - `expiry` should be bigger than timestamp. + * - `biding` and `asking` must not be empty. + * + * Emits a {SwapCreated} event. + */ + function createSwap(ISwap.Swap calldata Swap) external returns (uint256); - /** - * @dev Accepts a Swap. Once the Swap is accepted, the expiry is set - * to zero to avoid reutilization. - * - * Requirements: - * - * - `allowed` must be the zero address or match the caller address. - * - `expiry` must be bigger than timestamp. - * - `biding` assets must be allowed to transfer. - * - `asking` assets must be allowed to transfer. - * - * Emits a {SwapAccepted} event. - * - * NOTE: The expiry is set to 0, because if the Swap is expired it - * will revert, preventing reentrancy attacks. - */ - function acceptSwap(uint256 id) external returns (bool); + /** + * @dev Accepts a Swap. Once the Swap is accepted, the expiry is set + * to zero to avoid reutilization. + * + * Requirements: + * + * - `allowed` must be the zero address or match the caller address. + * - `expiry` must be bigger than timestamp. + * - `biding` assets must be allowed to transfer. + * - `asking` assets must be allowed to transfer. + * + * Emits a {SwapAccepted} event. + * + * NOTE: The expiry is set to 0, because if the Swap is expired it + * will revert, preventing reentrancy attacks. + */ + function acceptSwap(uint256 swapId) external returns (bool); - /** - * @dev Cancels an active Swap by setting the expiry to zero. - * - * Expiry with 0 seconds means that the Swap doesn't exist - * or is already canceled. - * - * Requirements: - * - * - `owner` must be the caller adress. - * - `expiry` must be bigger than timestamp. - * - * Emits a {SwapCanceled} event. - */ - function cancelSwap(uint256 id) external; + /** + * @dev Cancels an active Swap by setting the expiry to zero. + * + * Expiry with 0 seconds means that the Swap doesn't exist + * or is already canceled. + * + * Requirements: + * + * - `owner` must be the caller adress. + * - `expiry` must be bigger than timestamp. + * + * Emits a {SwapCanceled} event. + */ + function cancelSwap(uint256 swapId) external; - /** - * @dev Retrieves the details of a Swap based on the `swapId` provided. - * - * NOTE: If the Swaps doesn't exist, the values will be defaulted to 0. - * You can check if a Swap exists by checking if the `owner` is the zero address. - * If the `owner` is the zero address, then the Swap doesn't exist. - */ - function getSwap(uint256 id) external view returns (ISwap.Swap memory); -} + /** + * @dev Retrieves the details of a Swap based on the `swapId` provided. + * + * NOTE: If the Swaps doesn't exist, the values will be defaulted to 0. + * You can check if a Swap exists by checking if the `owner` is the zero address. + * If the `owner` is the zero address, then the Swap doesn't exist. + */ + function getSwap(uint256 swapId) external view returns (ISwap.Swap memory); +} \ No newline at end of file diff --git a/contracts/interfaces/ITransfer.sol b/contracts/interfaces/ITransfer.sol index 727d5b0..cfde089 100644 --- a/contracts/interfaces/ITransfer.sol +++ b/contracts/interfaces/ITransfer.sol @@ -5,16 +5,12 @@ 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; + /** + * @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; } diff --git a/contracts/mock/ERC20/ERC20.sol b/contracts/mock/ERC20/ERC20.sol index 0ca5b2e..b683636 100644 --- a/contracts/mock/ERC20/ERC20.sol +++ b/contracts/mock/ERC20/ERC20.sol @@ -10,439 +10,418 @@ import {IERC20Errors} from "./interfaces/IERC20Errors.sol"; * @dev Lightweight ERC20 with Permit extension. */ abstract contract ERC20 is IERC20, IERC20Permit, IERC20Errors { - /** - * @dev The bytes32 signature of the permit function and args name and type. - */ - bytes32 private constant _permitTypehash = + /** + * @dev The bytes32 signature of the permit function and args name and type. + */ + bytes32 private constant _permitTypehash = + keccak256( + "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" + ); + + /** + * @dev See {IERC20Permit-DOMAIN_SEPARATOR}. + */ + bytes32 private immutable _domainSeparator; + + /** + * @dev See {IERC20-name}. + */ + string private _name; + + /** + * @dev See {IERC20-symbol}. + */ + string private _symbol; + + /** + * @dev See {IERC20-totalSupply}. + */ + uint256 private _totalSupply; + + /** + * @dev Map accounts to spender to the allowed transfereable value. + */ + mapping(address => mapping(address => uint256)) private _allowance; + + /** + * @dev Map accounts to balance of Tokens. + */ + mapping(address => uint256) private _balances; + + /** + * @dev Map accounts to its current nonce. + */ + mapping(address => uint256) private _nonces; + + /** + * @dev Sets the values for {name} and {symbol}. + */ + constructor(string memory name_, string memory symbol_) { + _name = name_; + _symbol = symbol_; + _domainSeparator = keccak256( + abi.encode( keccak256( - "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" - ); - - /** - * @dev See {IERC20Permit-DOMAIN_SEPARATOR}. - */ - bytes32 private immutable _domainSeparator; - - /** - * @dev See {IERC20-name}. - */ - string private _name; - - /** - * @dev See {IERC20-symbol}. - */ - string private _symbol; - - /** - * @dev See {IERC20-totalSupply}. - */ - uint256 private _totalSupply; - - /** - * @dev Map accounts to spender to the allowed transfereable value. - */ - mapping(address => mapping(address => uint256)) private _allowance; - - /** - * @dev Map accounts to balance of Tokens. - */ - mapping(address => uint256) private _balances; - - /** - * @dev Map accounts to its current nonce. - */ - mapping(address => uint256) private _nonces; - - /** - * @dev Sets the values for {name} and {symbol}. - */ - constructor(string memory name_, string memory symbol_) { - _name = name_; - _symbol = symbol_; - _domainSeparator = keccak256( - abi.encode( - keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" - ), - keccak256(bytes(name_)), - keccak256(bytes("1")), - block.chainid, - address(this) - ) - ); + "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)" + ), + keccak256(bytes(name_)), + keccak256(bytes("1")), + block.chainid, + address(this) + ) + ); + } + + /** + * @dev Returns the name of the token. + */ + function name() public view virtual returns (string memory) { + return _name; + } + + /** + * @dev Returns the symbol of the token, usually a shorter version of the + * name. + */ + function symbol() public view virtual returns (string memory) { + return _symbol; + } + + /** + * @dev Returns the number of decimals used to get its user representation. + * For example, if `decimals` equals `2`, a balance of `505` tokens should + * be displayed to a user as `5.05` (`505 / 10 ** 2`). + * + * Tokens usually opt for a value of 18, imitating the relationship between + * Ether and Wei. This is the default value returned by this function, unless + * it's overridden. + * + * NOTE: This information is only used for _display_ purposes: it in + * no way affects any of the arithmetic of the contract, including + * {IERC20-balanceOf} and {IERC20-transfer}. + */ + function decimals() public view virtual returns (uint8) { + return 18; + } + + /** + * @dev See {IERC20-totalSupply}. + */ + function totalSupply() public view virtual returns (uint256) { + return _totalSupply; + } + + /** + * @dev See {IERC20-balanceOf}. + */ + function balanceOf(address account) public view virtual returns (uint256) { + return _balances[account]; + } + + /** + * @dev See {IERC20-allowance}. + */ + function allowance( + address owner, + address spender + ) public view virtual returns (uint256) { + return _allowance[owner][spender]; + } + + /** + * @dev Returns the current nonce of an address. + */ + function nonces(address owner) public view virtual returns (uint256) { + return _nonces[owner]; + } + + /** + * @dev See {IERC20Permit-DOMAIN_SEPARATOR}. + */ + function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { + return _domainSeparator; + } + + /** + * @dev See {IERC20-approve}. + * + * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on + * `transferFrom`. This is semantically equivalent to an infinite approval. + */ + function approve( + address spender, + uint256 value + ) public virtual returns (bool) { + _allowance[msg.sender][spender] = value; + + emit Approval(msg.sender, spender, value); + + return true; + } + + /** + * @dev See {IERC20-increaseAllowance}. + */ + function increaseAllowance( + address spender, + uint256 addedValue + ) public virtual returns (bool) { + // Overflow check required: allowance should never overflow + uint256 updatedAllowance = _allowance[msg.sender][spender] + addedValue; + + _allowance[msg.sender][spender] = updatedAllowance; + + emit Approval(msg.sender, spender, updatedAllowance); + + return true; + } + + /** + * @dev See {IERC20-decreaseAllowance}. + * + * Requirements: + * + * - `spender` must have allowance for the caller of at least + * `requestedDecrease`. + */ + function decreaseAllowance( + address spender, + uint256 requestedDecrease + ) public virtual returns (bool) { + uint256 currentAllowance = _allowance[msg.sender][spender]; + if (currentAllowance < requestedDecrease) { + revert ERC20FailedDecreaseAllowance( + spender, + currentAllowance, + requestedDecrease + ); } - /** - * @dev Returns the name of the token. - */ - function name() public view virtual returns (string memory) { - return _name; - } + unchecked { + // Underflow not possible: requestedDecrease <= currentAllowance. + _allowance[msg.sender][spender] = currentAllowance - requestedDecrease; - /** - * @dev Returns the symbol of the token, usually a shorter version of the - * name. - */ - function symbol() public view virtual returns (string memory) { - return _symbol; + emit Approval(msg.sender, spender, currentAllowance - requestedDecrease); } - /** - * @dev Returns the number of decimals used to get its user representation. - * For example, if `decimals` equals `2`, a balance of `505` tokens should - * be displayed to a user as `5.05` (`505 / 10 ** 2`). - * - * Tokens usually opt for a value of 18, imitating the relationship between - * Ether and Wei. This is the default value returned by this function, unless - * it's overridden. - * - * NOTE: This information is only used for _display_ purposes: it in - * no way affects any of the arithmetic of the contract, including - * {IERC20-balanceOf} and {IERC20-transfer}. - */ - function decimals() public view virtual returns (uint8) { - return 18; + return true; + } + + /** + * @dev See {IERC20Permit-permit}. + * + * Requirements: + * + * - `spender` cannot be the zero address. + * - `deadline` must be a timestamp in the future. + * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` + * over the EIP712-formatted function arguments. + * - the signature must use ``owner``'s current nonce (see {IERC20Permit-nonces}). + */ + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual returns (bool) { + if (block.timestamp > deadline) { + revert ERC2612ExpiredSignature(deadline); } - /** - * @dev See {IERC20-totalSupply}. - */ - function totalSupply() public view virtual returns (uint256) { - return _totalSupply; + // Overflow not possible: nonces will never reach max uint256. + unchecked { + bytes32 structHash = keccak256( + abi.encode( + _permitTypehash, + owner, + spender, + value, + _nonces[owner]++, + deadline + ) + ); + address recoveredAddress = ecrecover( + keccak256(abi.encodePacked(hex"19_01", _domainSeparator, structHash)), + v, + r, + s + ); + if (recoveredAddress != owner) { + revert ERC2612InvalidSigner(recoveredAddress, owner); + } } - /** - * @dev See {IERC20-balanceOf}. - */ - function balanceOf(address account) public view virtual returns (uint256) { - return _balances[account]; - } + _allowance[owner][spender] = value; - /** - * @dev See {IERC20-allowance}. - */ - function allowance( - address owner, - address spender - ) public view virtual returns (uint256) { - return _allowance[owner][spender]; - } + emit Approval(owner, spender, value); - /** - * @dev Returns the current nonce of an address. - */ - function nonces(address owner) public view virtual returns (uint256) { - return _nonces[owner]; - } + return true; + } - /** - * @dev See {IERC20Permit-DOMAIN_SEPARATOR}. - */ - function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { - return _domainSeparator; - } + /** + * @dev Creates an `value` of tokens and assigns them to `to` by creating supply. + * + * Emits a {Transfer} event with `from` set to the zero address. + */ + function _mint(address to, uint256 value) internal { + // Overflow check required: _totalSupply should never overflow + _totalSupply += value; - /** - * @dev See {IERC20-approve}. - * - * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on - * `transferFrom`. This is semantically equivalent to an infinite approval. - */ - function approve( - address spender, - uint256 value - ) public virtual returns (bool) { - _allowance[msg.sender][spender] = value; - - emit Approval(msg.sender, spender, value); - - return true; + unchecked { + // Overflow not possible: value <= _totalSupply. + _balances[to] += value; } - /** - * @dev See {IERC20-increaseAllowance}. - */ - function increaseAllowance( - address spender, - uint256 addedValue - ) public virtual returns (bool) { - // Overflow check required: allowance should never overflow - uint256 updatedAllowance = _allowance[msg.sender][spender] + addedValue; + emit Transfer(address(0), to, value); + } + + /** + * @dev Destroys an `value` of tokens from `from` by lowering the total supply. + * + * Requirements: + * + * - `from` must have a balance of at least `value`. + * + * Emits a {Transfer} event with `to` set to the zero address. + */ + function _burn(address from, uint256 value) internal { + uint256 fromBalance = _balances[from]; + if (fromBalance < value) { + revert ERC20InsufficientBalance(from, fromBalance, value); + } - _allowance[msg.sender][spender] = updatedAllowance; + unchecked { + // Underflow not possible: value <= _totalSupply or value <= fromBalance <= _totalSupply. + _totalSupply -= value; + _balances[from] -= value; + } - emit Approval(msg.sender, spender, updatedAllowance); + emit Transfer(from, address(0), value); + } + + /** + * @dev See {IERC20-transfer}. + * + * Requirements: + * + * - the caller must have a balance of at least `value`. + */ + function transfer(address to, uint256 value) public virtual returns (bool) { + uint256 fromBalance = _balances[msg.sender]; + if (fromBalance < value) { + revert ERC20InsufficientBalance(msg.sender, fromBalance, value); + } - return true; + // Underflow and overflow not possible: value <= _totalSupply or value <= fromBalance <= _totalSupply. + unchecked { + _balances[msg.sender] = fromBalance - value; + _balances[to] += value; } - /** - * @dev See {IERC20-decreaseAllowance}. - * - * Requirements: - * - * - `spender` must have allowance for the caller of at least - * `requestedDecrease`. - */ - function decreaseAllowance( - address spender, - uint256 requestedDecrease - ) public virtual returns (bool) { - uint256 currentAllowance = _allowance[msg.sender][spender]; - if (currentAllowance < requestedDecrease) { - revert ERC20FailedDecreaseAllowance( - spender, - currentAllowance, - requestedDecrease - ); - } - - unchecked { - // Underflow not possible: requestedDecrease <= currentAllowance. - _allowance[msg.sender][spender] = - currentAllowance - - requestedDecrease; - - emit Approval( - msg.sender, - spender, - currentAllowance - requestedDecrease - ); - } - - return true; + emit Transfer(msg.sender, to, value); + + return true; + } + + /** + * @dev See {IERC20-transferFrom}. + * + * Requirements: + * + * - `from` must have a balance of at least `value`. + * - the caller must have allowance for `from`'s tokens of at least + * `value`. + * + * NOTE: Does not update the allowance if the current allowance + * is the maximum `uint256`. + */ + function transferFrom( + address from, + address to, + uint256 value + ) public virtual returns (bool) { + uint256 currentAllowance = _allowance[from][msg.sender]; + if (currentAllowance != type(uint256).max) { + if (currentAllowance < value) { + revert ERC20InsufficientAllowance(msg.sender, currentAllowance, value); + } + // Underflow not possible: value <= currentAllowance + unchecked { + _allowance[from][msg.sender] = currentAllowance - value; + } } - /** - * @dev See {IERC20Permit-permit}. - * - * Requirements: - * - * - `spender` cannot be the zero address. - * - `deadline` must be a timestamp in the future. - * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` - * over the EIP712-formatted function arguments. - * - the signature must use ``owner``'s current nonce (see {IERC20Permit-nonces}). - */ - function permit( - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) public virtual returns (bool) { - if (block.timestamp > deadline) { - revert ERC2612ExpiredSignature(deadline); - } - - // Overflow not possible: nonces will never reach max uint256. - unchecked { - bytes32 structHash = keccak256( - abi.encode( - _permitTypehash, - owner, - spender, - value, - _nonces[owner]++, - deadline - ) - ); - address recoveredAddress = ecrecover( - keccak256( - abi.encodePacked(hex"19_01", _domainSeparator, structHash) - ), - v, - r, - s - ); - if (recoveredAddress != owner) { - revert ERC2612InvalidSigner(recoveredAddress, owner); - } - } - - _allowance[owner][spender] = value; - - emit Approval(owner, spender, value); - - return true; + uint256 fromBalance = _balances[from]; + if (fromBalance < value) { + revert ERC20InsufficientBalance(from, fromBalance, value); } - /** - * @dev Creates an `value` of tokens and assigns them to `to` by creating supply. - * - * Emits a {Transfer} event with `from` set to the zero address. - */ - function _mint(address to, uint256 value) internal { - // Overflow check required: _totalSupply should never overflow - _totalSupply += value; - - unchecked { - // Overflow not possible: value <= _totalSupply. - _balances[to] += value; - } - - emit Transfer(address(0), to, value); + // Underflow and overflow not possible: value <= fromBalance and value <= _totalSupply. + unchecked { + _balances[from] = fromBalance - value; + _balances[to] += value; } - /** - * @dev Destroys an `value` of tokens from `from` by lowering the total supply. - * - * Requirements: - * - * - `from` must have a balance of at least `value`. - * - * Emits a {Transfer} event with `to` set to the zero address. - */ - function _burn(address from, uint256 value) internal { - uint256 fromBalance = _balances[from]; - if (fromBalance < value) { - revert ERC20InsufficientBalance(from, fromBalance, value); - } - - unchecked { - // Underflow not possible: value <= _totalSupply or value <= fromBalance <= _totalSupply. - _totalSupply -= value; - _balances[from] -= value; - } - - emit Transfer(from, address(0), value); + emit Transfer(from, to, value); + + return true; + } + + /** + * @dev See {IERC20Permit-permitTransfer}. + * + * Requirements: + * + * - `deadline` must be a timestamp in the future. + * - `v`, `r` and `s` must be a valid `secp256k1` signature from `from` + * over the EIP712-formatted function arguments. + * - the signature must use `from`'s current nonce (see {IERC20Permit-nonces}). + */ + function permitTransfer( + address from, + address to, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) public virtual returns (bool) { + if (block.timestamp > deadline) { + revert ERC2612ExpiredSignature(deadline); } - /** - * @dev See {IERC20-transfer}. - * - * Requirements: - * - * - the caller must have a balance of at least `value`. - */ - function transfer(address to, uint256 value) public virtual returns (bool) { - uint256 fromBalance = _balances[msg.sender]; - if (fromBalance < value) { - revert ERC20InsufficientBalance(msg.sender, fromBalance, value); - } - - // Underflow and overflow not possible: value <= _totalSupply or value <= fromBalance <= _totalSupply. - unchecked { - _balances[msg.sender] = fromBalance - value; - _balances[to] += value; - } - - emit Transfer(msg.sender, to, value); - - return true; + // Overflow not possible: nonces will never reach max uint256. + unchecked { + bytes32 structHash = keccak256( + abi.encode(_permitTypehash, from, to, value, _nonces[from]++, deadline) + ); + address recoveredAddress = ecrecover( + keccak256(abi.encodePacked(hex"19_01", _domainSeparator, structHash)), + v, + r, + s + ); + if (recoveredAddress != from) { + revert ERC2612InvalidSigner(recoveredAddress, from); + } } - /** - * @dev See {IERC20-transferFrom}. - * - * Requirements: - * - * - `from` must have a balance of at least `value`. - * - the caller must have allowance for `from`'s tokens of at least - * `value`. - * - * NOTE: Does not update the allowance if the current allowance - * is the maximum `uint256`. - */ - function transferFrom( - address from, - address to, - uint256 value - ) public virtual returns (bool) { - uint256 currentAllowance = _allowance[from][msg.sender]; - if (currentAllowance != type(uint256).max) { - if (currentAllowance < value) { - revert ERC20InsufficientAllowance( - msg.sender, - currentAllowance, - value - ); - } - // Underflow not possible: value <= currentAllowance - unchecked { - _allowance[from][msg.sender] = currentAllowance - value; - } - } - - uint256 fromBalance = _balances[from]; - if (fromBalance < value) { - revert ERC20InsufficientBalance(from, fromBalance, value); - } - - // Underflow and overflow not possible: value <= fromBalance and value <= _totalSupply. - unchecked { - _balances[from] = fromBalance - value; - _balances[to] += value; - } - - emit Transfer(from, to, value); - - return true; + uint256 fromBalance = _balances[from]; + if (fromBalance < value) { + revert ERC20InsufficientBalance(from, fromBalance, value); } - /** - * @dev See {IERC20Permit-permitTransfer}. - * - * Requirements: - * - * - `deadline` must be a timestamp in the future. - * - `v`, `r` and `s` must be a valid `secp256k1` signature from `from` - * over the EIP712-formatted function arguments. - * - the signature must use `from`'s current nonce (see {IERC20Permit-nonces}). - */ - function permitTransfer( - address from, - address to, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) public virtual returns (bool) { - if (block.timestamp > deadline) { - revert ERC2612ExpiredSignature(deadline); - } - - // Overflow not possible: nonces will never reach max uint256. - unchecked { - bytes32 structHash = keccak256( - abi.encode( - _permitTypehash, - from, - to, - value, - _nonces[from]++, - deadline - ) - ); - address recoveredAddress = ecrecover( - keccak256( - abi.encodePacked(hex"19_01", _domainSeparator, structHash) - ), - v, - r, - s - ); - if (recoveredAddress != from) { - revert ERC2612InvalidSigner(recoveredAddress, from); - } - } - - uint256 fromBalance = _balances[from]; - if (fromBalance < value) { - revert ERC20InsufficientBalance(from, fromBalance, value); - } - - // Underflow and overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply. - unchecked { - _balances[from] = fromBalance - value; - _balances[to] += value; - } - - emit Transfer(from, to, value); - - return true; + // Underflow and overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply. + unchecked { + _balances[from] = fromBalance - value; + _balances[to] += value; } + + emit Transfer(from, to, value); + + return true; + } } diff --git a/contracts/mock/ERC20/interfaces/IERC20.sol b/contracts/mock/ERC20/interfaces/IERC20.sol index af0c3cb..e49ae33 100644 --- a/contracts/mock/ERC20/interfaces/IERC20.sol +++ b/contracts/mock/ERC20/interfaces/IERC20.sol @@ -6,129 +6,125 @@ pragma solidity ^0.8.17; * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { - /** - * @dev Emitted when the allowance of a `spender` for an `owner` is set by - * a call to {approve}. `value` is the new allowance. - */ - event Approval( - address indexed owner, - address indexed spender, - uint256 value - ); + /** + * @dev Emitted when the allowance of a `spender` for an `owner` is set by + * a call to {approve}. `value` is the new allowance. + */ + event Approval(address indexed owner, address indexed spender, uint256 value); - /** - * @dev Emitted when `value` tokens are moved from `from` to `to`. - * - * NOTE: `value` can be zero. - */ - event Transfer(address indexed from, address indexed to, uint256 value); + /** + * @dev Emitted when `value` tokens are moved from `from` to `to`. + * + * NOTE: `value` can be zero. + */ + event Transfer(address indexed from, address indexed to, uint256 value); - /** - * @dev Returns the name of the token. - */ - function name() external view returns (string memory); + /** + * @dev Returns the name of the token. + */ + function name() external view returns (string memory); - /** - * @dev Returns the symbol of the token. - */ - function symbol() external view returns (string memory); + /** + * @dev Returns the symbol of the token. + */ + function symbol() external view returns (string memory); - /** - * @dev Returns the decimals places of the token. - */ - function decimals() external view returns (uint8); + /** + * @dev Returns the decimals places of the token. + */ + function decimals() external view returns (uint8); - /** - * @dev Returns the value of tokens in existence. - */ - function totalSupply() external view returns (uint256); + /** + * @dev Returns the value of tokens in existence. + */ + function totalSupply() external view returns (uint256); - /** - * @dev Returns the value of tokens owned by `account`. - */ - function balanceOf(address account) external view returns (uint256); + /** + * @dev Returns the value of tokens owned by `account`. + */ + function balanceOf(address account) external view returns (uint256); - /** - * @dev Returns the remaining number of tokens that `spender` will be - * allowed to spend on behalf of `owner` through {transferFrom}. This is - * zero by default. - * - * This value changes when {approve} or {transferFrom} are called. - * - * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on - * `transferFrom`. This is semantically equivalent to an infinite approval. - */ - function allowance( - address owner, - address spender - ) external view returns (uint256); + /** + * @dev Returns the remaining number of tokens that `spender` will be + * allowed to spend on behalf of `owner` through {transferFrom}. This is + * zero by default. + * + * This value changes when {approve} or {transferFrom} are called. + * + * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on + * `transferFrom`. This is semantically equivalent to an infinite approval. + */ + function allowance( + address owner, + address spender + ) external view returns (uint256); - /** - * @dev Sets a `value` amount of tokens as the allowance of `spender` over the - * caller's tokens. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * IMPORTANT: Beware that changing an allowance with this method brings the risk - * that someone may use both the old and the new allowance by unfortunate - * transaction ordering. One possible solution to mitigate this race - * condition is to first reduce the spender's allowance to 0 and set the - * desired value afterwards: - * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 - * - * Emits an {Approval} event. - */ - function approve(address spender, uint256 value) external returns (bool); + /** + * @dev Sets a `value` amount of tokens as the allowance of `spender` over the + * caller's tokens. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * IMPORTANT: Beware that changing an allowance with this method brings the risk + * that someone may use both the old and the new allowance by unfortunate + * transaction ordering. One possible solution to mitigate this race + * condition is to first reduce the spender's allowance to 0 and set the + * desired value afterwards: + * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 + * + * Emits an {Approval} event. + */ + function approve(address spender, uint256 value) external returns (bool); - /** - * @dev Atomically increases the allowance granted to `spender` by the caller. - * - * This is an alternative to {approve} that can be used as a mitigation for - * problems described in {IERC20-approve}. - * - * Emits an {IERC20-Approval} event indicating the updated allowance. - */ - function increaseAllowance( - address spender, - uint256 addedValue - ) external returns (bool); + /** + * @dev Atomically increases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {IERC20-Approval} event indicating the updated allowance. + */ + function increaseAllowance( + address spender, + uint256 addedValue + ) external returns (bool); - /** - * @dev Atomically decreases the allowance granted to `spender` by the caller. - * - * This is an alternative to {approve} that can be used as a mitigation for - * problems described in {IERC20-approve}. - * - * Emits an {Approval} event indicating the updated allowance. - * - * NOTE: Although this function is designed to avoid double spending with {approval}, - * it can still be frontrunned, preventing any attempt of allowance reduction. - */ - function decreaseAllowance( - address spender, - uint256 requestedDecrease - ) external returns (bool); + /** + * @dev Atomically decreases the allowance granted to `spender` by the caller. + * + * This is an alternative to {approve} that can be used as a mitigation for + * problems described in {IERC20-approve}. + * + * Emits an {Approval} event indicating the updated allowance. + * + * NOTE: Although this function is designed to avoid double spending with {approval}, + * it can still be frontrunned, preventing any attempt of allowance reduction. + */ + function decreaseAllowance( + address spender, + uint256 requestedDecrease + ) external returns (bool); - /** - * @dev Moves a `value` amount of tokens from the caller's account to `to`. - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transfer(address to, uint256 value) external returns (bool); + /** + * @dev Moves a `value` amount of tokens from the caller's account to `to`. + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transfer(address to, uint256 value) external returns (bool); - /** - * @dev Moves a `value` amount of tokens from `from` to `to` using the - * allowance mechanism. `value` is then deducted from the caller's - * allowance. - * - * Returns a boolean value indicating whether the operation succeeded. - * - * Emits a {Transfer} event. - */ - function transferFrom( - address from, - address to, - uint256 value - ) external returns (bool); + /** + * @dev Moves a `value` amount of tokens from `from` to `to` using the + * allowance mechanism. `value` is then deducted from the caller's + * allowance. + * + * Returns a boolean value indicating whether the operation succeeded. + * + * Emits a {Transfer} event. + */ + function transferFrom( + address from, + address to, + uint256 value + ) external returns (bool); } diff --git a/contracts/mock/ERC20/interfaces/IERC20Errors.sol b/contracts/mock/ERC20/interfaces/IERC20Errors.sol index 8751fe8..99d0672 100644 --- a/contracts/mock/ERC20/interfaces/IERC20Errors.sol +++ b/contracts/mock/ERC20/interfaces/IERC20Errors.sol @@ -5,60 +5,60 @@ pragma solidity ^0.8.17; * @dev Standard ERC20 Errors */ interface IERC20Errors { - /** - * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. - * @param sender Address whose tokens are being transferred. - * @param balance Current balance for the interacting account. - * @param needed Minimum amount required to perform a transfer. - */ - error ERC20InsufficientBalance( - address sender, - uint256 balance, - uint256 needed - ); + /** + * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. + * @param sender Address whose tokens are being transferred. + * @param balance Current balance for the interacting account. + * @param needed Minimum amount required to perform a transfer. + */ + error ERC20InsufficientBalance( + address sender, + uint256 balance, + uint256 needed + ); - /** - * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers. - * @param spender Address that may be allowed to operate on tokens without being their owner. - * @param allowance Amount of tokens a `spender` is allowed to operate with. - * @param needed Minimum amount required to perform a transfer. - */ + /** + * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers. + * @param spender Address that may be allowed to operate on tokens without being their owner. + * @param allowance Amount of tokens a `spender` is allowed to operate with. + * @param needed Minimum amount required to perform a transfer. + */ - error ERC20InsufficientAllowance( - address spender, - uint256 allowance, - uint256 needed - ); + error ERC20InsufficientAllowance( + address spender, + uint256 allowance, + uint256 needed + ); - /** - * @dev Indicates a failed `decreaseAllowance` request. - * @param spender Address that may be allowed to operate on tokens without being their owner. - * @param allowance Amount of tokens a `spender` want to operate with. - * @param needed Amount required to decrease the allowance. - */ - error ERC20FailedDecreaseAllowance( - address spender, - uint256 allowance, - uint256 needed - ); + /** + * @dev Indicates a failed `decreaseAllowance` request. + * @param spender Address that may be allowed to operate on tokens without being their owner. + * @param allowance Amount of tokens a `spender` want to operate with. + * @param needed Amount required to decrease the allowance. + */ + error ERC20FailedDecreaseAllowance( + address spender, + uint256 allowance, + uint256 needed + ); - /** - * @dev Indicates the nonce used for an `account` is not the expected current nonce. - * @param account Address whose nonce is being checked. - * @param nonce Expected nonce for the given `account`. - */ - error ERC20PermitInvalidNonce(address account, uint256 nonce); + /** + * @dev Indicates the nonce used for an `account` is not the expected current nonce. + * @param account Address whose nonce is being checked. + * @param nonce Expected nonce for the given `account`. + */ + error ERC20PermitInvalidNonce(address account, uint256 nonce); - /** - * @dev Indicates the expiration of a permit to be used. - * @param deadline Expiration time limit in seconds. - */ - error ERC2612ExpiredSignature(uint256 deadline); + /** + * @dev Indicates the expiration of a permit to be used. + * @param deadline Expiration time limit in seconds. + */ + error ERC2612ExpiredSignature(uint256 deadline); - /** - * @dev Indicates the mismatched owner when validating the signature. - * @param signer Address of the signer recovered. - * @param owner Address of the owner expected to match `signer`. - */ - error ERC2612InvalidSigner(address signer, address owner); + /** + * @dev Indicates the mismatched owner when validating the signature. + * @param signer Address of the signer recovered. + * @param owner Address of the owner expected to match `signer`. + */ + error ERC2612InvalidSigner(address signer, address owner); } diff --git a/contracts/mock/ERC20/interfaces/IERC20Permit.sol b/contracts/mock/ERC20/interfaces/IERC20Permit.sol index f861fab..fee25ca 100644 --- a/contracts/mock/ERC20/interfaces/IERC20Permit.sol +++ b/contracts/mock/ERC20/interfaces/IERC20Permit.sol @@ -11,66 +11,66 @@ pragma solidity ^0.8.17; * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { - /** - * @dev Returns the current nonce for `owner`. This value must be - * included whenever a signature is generated for {permit}. - * - * Every successful call to {permit} increases `owner`'s nonce by one. - * This prevents a signature from being used multiple times. - */ - function nonces(address owner) external view returns (uint256); + /** + * @dev Returns the current nonce for `owner`. This value must be + * included whenever a signature is generated for {permit}. + * + * Every successful call to {permit} increases `owner`'s nonce by one. + * This prevents a signature from being used multiple times. + */ + function nonces(address owner) external view returns (uint256); - /** - * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. - */ - // solhint-disable-next-line func-name-mixedcase - function DOMAIN_SEPARATOR() external view returns (bytes32); + /** + * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. + */ + // solhint-disable-next-line func-name-mixedcase + function DOMAIN_SEPARATOR() external view returns (bytes32); - /** - * @dev Sets `value` as the allowance of `spender` over `owner`'s tokens, - * given `owner`'s signed approval. - * - * IMPORTANT: The same issues {IERC20-approve} has related to transaction - * ordering also apply here. - * - * Emits an {IERC20-Approval} event. - * - * NOTE: `spender` can be the zero address. Checking this on-chain is a bad - * usage of gas. For more information on the signature format, see the - * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIPsection]. - */ - function permit( - address owner, - address spender, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) external returns (bool); + /** + * @dev Sets `value` as the allowance of `spender` over `owner`'s tokens, + * given `owner`'s signed approval. + * + * IMPORTANT: The same issues {IERC20-approve} has related to transaction + * ordering also apply here. + * + * Emits an {IERC20-Approval} event. + * + * NOTE: `spender` can be the zero address. Checking this on-chain is a bad + * usage of gas. For more information on the signature format, see the + * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIPsection]. + */ + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external returns (bool); - /** - * @dev Allows {IERC20-transferFrom} to be used with the `owner`'s signature. - * Similar to permit but changing the scope to handle the balance instead of - * allowance. - * - * Requires less gas than regular {permit} and {IERC20-transferFrom}. - * - * IMPORTANT: `owner` works as `from` and `spender` as `to` (see {IERC20Permit-permit}). - * - * Emits an {IERC20-Transfer} event. - * - * NOTE: Realize that {PERMIT_TYPEHASH} is different from the one in {permit}. - * This is because the arguments name differ. But won't result in a different - * output as long as it is encoded following the EIP712 and ERC20Permit specs. - */ - function permitTransfer( - address from, - address to, - uint256 value, - uint256 deadline, - uint8 v, - bytes32 r, - bytes32 s - ) external returns (bool); + /** + * @dev Allows {IERC20-transferFrom} to be used with the `owner`'s signature. + * Similar to permit but changing the scope to handle the balance instead of + * allowance. + * + * Requires less gas than regular {permit} and {IERC20-transferFrom}. + * + * IMPORTANT: `owner` works as `from` and `spender` as `to` (see {IERC20Permit-permit}). + * + * Emits an {IERC20-Transfer} event. + * + * NOTE: Realize that {PERMIT_TYPEHASH} is different from the one in {permit}. + * This is because the arguments name differ. But won't result in a different + * output as long as it is encoded following the EIP712 and ERC20Permit specs. + */ + function permitTransfer( + address from, + address to, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external returns (bool); } diff --git a/contracts/mock/MockERC20.sol b/contracts/mock/MockERC20.sol index 96198f7..149f73e 100644 --- a/contracts/mock/MockERC20.sol +++ b/contracts/mock/MockERC20.sol @@ -4,9 +4,9 @@ pragma solidity ^0.8.9; import {ERC20} from "./ERC20/ERC20.sol"; contract MockERC20 is ERC20 { - constructor() ERC20("MockERC20", "ERC20") {} + constructor() ERC20("MockERC20", "ERC20") {} - function mintTo(address to, uint256 amount) public { - _mint(to, amount); - } + function mintTo(address to, uint256 amount) public { + _mint(to, amount); + } } diff --git a/contracts/mock/MockERC721.sol b/contracts/mock/MockERC721.sol index 80b35d6..eee2835 100644 --- a/contracts/mock/MockERC721.sol +++ b/contracts/mock/MockERC721.sol @@ -4,12 +4,12 @@ pragma solidity ^0.8.9; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; contract MockERC721 is ERC721 { - uint256 public totalSupply; + uint256 public totalSupply; - constructor() ERC721("MockERC721", "ERC721") {} + constructor() ERC721("MockERC721", "ERC721") {} - function mintTo(address to, uint256 id) public { - totalSupply++; - _mint(to, id); - } + function mintTo(address to, uint256 id) public { + totalSupply++; + _mint(to, id); + } } diff --git a/docs/SwapFactory.md b/docs/SwapFactory.md index b693a4c..9326a4e 100644 --- a/docs/SwapFactory.md +++ b/docs/SwapFactory.md @@ -10,19 +10,19 @@ used in the {Swaplace-createSwap} function. Swaplace uses a {ISwap-Swap} struct to represent a Swap. This struct is composed of: -- The `owner` of the Swap is the address that created the Swap. -- 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 `biding` are the assets that the owner is offering. -- The `asking` are the assets that the owner wants in exchange. +- The `owner` of the Swap is the address that created the Swap. +- 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 `biding` are the assets that the owner is offering. +- The `asking` are the assets that the owner wants in exchange. 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 `amount` or `id` of the asset. This amount can be the amount of ERC20 tokens - or the id of an ERC721 token. +- The `address` of the asset. This address can be from an ERC20 or ERC721 contract. +- 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 diff --git a/docs/Swaplace.md b/docs/Swaplace.md index eae345e..799136d 100644 --- a/docs/Swaplace.md +++ b/docs/Swaplace.md @@ -18,7 +18,7 @@ _See {ISwaplace-createSwap}._ ### acceptSwap ```solidity -function acceptSwap(uint256 id) public returns (bool) +function acceptSwap(uint256 swapId) public returns (bool) ``` _See {ISwaplace-acceptSwap}._ @@ -26,7 +26,7 @@ _See {ISwaplace-acceptSwap}._ ### cancelSwap ```solidity -function cancelSwap(uint256 id) public +function cancelSwap(uint256 swapId) public ``` _See {ISwaplace-cancelSwap}._ @@ -34,7 +34,7 @@ _See {ISwaplace-cancelSwap}._ ### getSwap ```solidity -function getSwap(uint256 id) public view returns (struct ISwap.Swap) +function getSwap(uint256 swapId) public view returns (struct ISwap.Swap) ``` _See {ISwaplace-getSwap}._ diff --git a/docs/interfaces/ISwaplace.md b/docs/interfaces/ISwaplace.md index c9fc51f..b3e08cc 100644 --- a/docs/interfaces/ISwaplace.md +++ b/docs/interfaces/ISwaplace.md @@ -7,7 +7,7 @@ Interface of the {Swaplace} implementation. ### SwapCreated ```solidity -event SwapCreated(uint256 id, address owner, uint256 expiry) +event SwapCreated(uint256 swapId, address owner, address allowed, uint256 expiry) ``` Emitted when a new Swap is created. @@ -15,7 +15,7 @@ Emitted when a new Swap is created. ### SwapAccepted ```solidity -event SwapAccepted(uint256 id, address acceptee) +event SwapAccepted(uint256 swapId, address acceptee) ``` Emitted when a Swap is accepted. @@ -23,7 +23,7 @@ Emitted when a Swap is accepted. ### SwapCanceled ```solidity -event SwapCanceled(uint256 id, address owner) +event SwapCanceled(uint256 swapId, address owner) ``` Emitted when a Swap is canceled. @@ -34,20 +34,20 @@ Emitted when a Swap is canceled. function createSwap(struct ISwap.Swap Swap) external returns (uint256) ``` -Allow users to create a Swap. Each new Swap self-increments its id by one. +Allow users to create a Swap. Each new Swap self-increments its ID by one. Requirements: -- `owner` must be the caller address. -- `expiry` should be bigger than timestamp. -- `biding` and `asking` must not be empty. +- `owner` must be the caller address. +- `expiry` should be bigger than timestamp. +- `biding` and `asking` must not be empty. Emits a {SwapCreated} event. ### acceptSwap ```solidity -function acceptSwap(uint256 id) external returns (bool) +function acceptSwap(uint256 swapId) external returns (bool) ``` Accepts a Swap. Once the Swap is accepted, the expiry is set @@ -55,10 +55,10 @@ to zero to avoid reutilization. Requirements: -- `allowed` must be the zero address or match the caller address. -- `expiry` must be bigger than timestamp. -- `biding` assets must be allowed to transfer. -- `asking` assets must be allowed to transfer. +- `allowed` must be the zero address or match the caller address. +- `expiry` must be bigger than timestamp. +- `biding` assets must be allowed to transfer. +- `asking` assets must be allowed to transfer. Emits a {SwapAccepted} event. @@ -68,7 +68,7 @@ will revert, preventing reentrancy attacks. ### cancelSwap ```solidity -function cancelSwap(uint256 id) external +function cancelSwap(uint256 swapId) external ``` Cancels an active Swap by setting the expiry to zero. @@ -78,15 +78,15 @@ or is already canceled. Requirements: -- `owner` must be the caller adress. -- `expiry` must be bigger than timestamp. +- `owner` must be the caller adress. +- `expiry` must be bigger than timestamp. Emits a {SwapCanceled} event. ### getSwap ```solidity -function getSwap(uint256 id) external view returns (struct ISwap.Swap) +function getSwap(uint256 swapId) external view returns (struct ISwap.Swap) ``` Retrieves the details of a Swap based on the `swapId` provided. diff --git a/hardhat.config.ts b/hardhat.config.ts index 4fbd1d2..5c4efed 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -1,75 +1,79 @@ -import { HardhatUserConfig } from "hardhat/config" -import "@nomicfoundation/hardhat-toolbox" -import "solidity-docgen" -import dotenv from "dotenv" -dotenv.config() - -const { SWAP_CREATOR_PRIVATE_KEY, SWAP_ACCEPTEE_PRIVATE_KEY } = process.env +import { HardhatUserConfig } from "hardhat/config"; +import "@nomicfoundation/hardhat-toolbox"; +import "solidity-docgen"; +import dotenv from "dotenv"; +dotenv.config(); const DEPLOYER_PRIVATE_KEY = - "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; const config: HardhatUserConfig = { - solidity: "0.8.17", - etherscan: { - apiKey: `${process.env.ETHERSCAN_API_KEY}`, + solidity: "0.8.17", + etherscan: { + apiKey: `${process.env.ETHERSCAN_API_KEY}`, + }, + networks: { + /** + * @dev Testnets + */ + sepolia: { + url: `${process.env.SEPOLIA_RPC_URL}`, + accounts: [ + `${ + process.env.DEPLOYER_PRIVATE_KEY + ? process.env.DEPLOYER_PRIVATE_KEY + : DEPLOYER_PRIVATE_KEY + }`, + ], + }, + goerli: { + url: `${process.env.SEPOLIA_RPC_URL}`, + accounts: [`${DEPLOYER_PRIVATE_KEY}`], + }, + mumbai: { + url: `${process.env.MUMBAI_RPC_URL}`, + accounts: [`${DEPLOYER_PRIVATE_KEY}`], + }, + fuji: { + url: `${process.env.FUJI_RPC_URL}`, + accounts: [`${DEPLOYER_PRIVATE_KEY}`], + }, + bnbtest: { + url: `${process.env.BNB_TESTNET_RPC_URL}`, + accounts: [`${DEPLOYER_PRIVATE_KEY}`], + }, + /** + * @dev Mainnets + */ + ethereum: { + url: `${process.env.ETH_RPC_URL}`, + accounts: [`${DEPLOYER_PRIVATE_KEY}`], + }, + polygon: { + url: `${process.env.MATIC_RPC_URL}`, + accounts: [`${DEPLOYER_PRIVATE_KEY}`], }, - networks: { - /** - * @dev Testnets - */ - sepolia: { - url: `${process.env.SEPOLIA_RPC_URL}`, - accounts: [`${DEPLOYER_PRIVATE_KEY}`], - }, - goerli: { - url: `${process.env.SEPOLIA_RPC_URL}`, - accounts: [`${DEPLOYER_PRIVATE_KEY}`], - }, - mumbai: { - url: `${process.env.MUMBAI_RPC_URL}`, - accounts: [`${DEPLOYER_PRIVATE_KEY}`], - }, - fuji: { - url: `${process.env.FUJI_RPC_URL}`, - accounts: [`${DEPLOYER_PRIVATE_KEY}`], - }, - bnbtest: { - url: `${process.env.BNB_TESTNET_RPC_URL}`, - accounts: [`${DEPLOYER_PRIVATE_KEY}`], - }, - /** - * @dev Mainnets - */ - ethereum: { - url: `${process.env.ETH_RPC_URL}`, - accounts: [`${DEPLOYER_PRIVATE_KEY}`], - }, - polygon: { - url: `${process.env.MATIC_RPC_URL}`, - accounts: [`${DEPLOYER_PRIVATE_KEY}`], - }, - avalanche: { - url: `${process.env.AVAX_RPC_URL}`, - accounts: [`${DEPLOYER_PRIVATE_KEY}`], - }, - binance: { - url: `${process.env.BNB_RPC_URL}`, - accounts: [`${DEPLOYER_PRIVATE_KEY}`], - }, - fantom: { - url: `${process.env.FTM_RPC_URL}`, - accounts: [`${DEPLOYER_PRIVATE_KEY}`], - }, + avalanche: { + url: `${process.env.AVAX_RPC_URL}`, + accounts: [`${DEPLOYER_PRIVATE_KEY}`], }, - defaultNetwork: "hardhat", - docgen: { - outputDir: "docs", - pages: "files", + binance: { + url: `${process.env.BNB_RPC_URL}`, + accounts: [`${DEPLOYER_PRIVATE_KEY}`], }, - gasReporter: { - enabled: true, + fantom: { + url: `${process.env.FTM_RPC_URL}`, + accounts: [`${DEPLOYER_PRIVATE_KEY}`], }, -} + }, + defaultNetwork: "hardhat", + docgen: { + outputDir: "docs", + pages: "files", + }, + gasReporter: { + enabled: true, + }, +}; -export default config +export default config; diff --git a/package.json b/package.json index acbed4e..00ad580 100644 --- a/package.json +++ b/package.json @@ -1,39 +1,39 @@ { - "name": "swaplace", - "dependencies": { - "hardhat": "^2.12.7" - }, - "devDependencies": { - "@nomicfoundation/hardhat-chai-matchers": "^1.0.0", - "@nomicfoundation/hardhat-network-helpers": "^1.0.8", - "@nomicfoundation/hardhat-toolbox": "^2.0.1", - "@nomiclabs/hardhat-etherscan": "^3.1.5", - "@nomiclabs/hardhat-ethers": "^2.2.2", - "@nomiclabs/hardhat-solhint": "^3.0.0", - "@openzeppelin/contracts": "^4.8.1", - "@typechain/ethers-v5": "^10.2.0", - "@typechain/hardhat": "^6.1.5", - "@types/chai": "^4.3.4", - "@types/mocha": "^10.0.1", - "chai": "^4.3.4", - "dotenv": "^16.0.3", - "ethers": "^5.6.1", - "hardhat-gas-reporter": "^1.0.9", - "solidity-coverage": "^0.8.2", - "solidity-docgen": "^0.6.0-beta.36", - "ts-node": "^10.9.1", - "typechain": "^8.1.1", - "typescript": "^4.9.5" - }, - "scripts": { - "clean": "npx hardhat clean", - "compile": "npx hardhat compile", - "test": "npx hardhat test", - "docs": "npx hardhat docgen", - "testnet": "npx hardhat test --network $1", - "deploy": "npx hardhat run scripts/deploy.ts --network $1", - "compile-echidna": "crytic-compile . && slither . --print echidna", - "fuzz-p": "echidna . --contract TestSwaplace --test-mode property --config echidna.config.yml", - "fuzz-a": "echidna . --contract TestSwaplace --test-mode assertion --config echidna.config.yml" - } + "name": "swaplace", + "dependencies": { + "hardhat": "^2.12.7" + }, + "devDependencies": { + "@nomicfoundation/hardhat-chai-matchers": "^1.0.0", + "@nomicfoundation/hardhat-network-helpers": "^1.0.8", + "@nomicfoundation/hardhat-toolbox": "^2.0.1", + "@nomiclabs/hardhat-etherscan": "^3.1.5", + "@nomiclabs/hardhat-ethers": "^2.2.2", + "@nomiclabs/hardhat-solhint": "^3.0.0", + "@openzeppelin/contracts": "^4.8.1", + "@typechain/ethers-v5": "^10.2.0", + "@typechain/hardhat": "^6.1.5", + "@types/chai": "^4.3.4", + "@types/mocha": "^10.0.1", + "chai": "^4.3.4", + "dotenv": "^16.0.3", + "ethers": "^5.6.1", + "hardhat-gas-reporter": "^1.0.9", + "solidity-coverage": "^0.8.2", + "solidity-docgen": "^0.6.0-beta.36", + "ts-node": "^10.9.1", + "typechain": "^8.1.1", + "typescript": "^4.9.5" + }, + "scripts": { + "clean": "npx hardhat clean", + "compile": "npx hardhat compile", + "test": "npx hardhat test", + "docs": "npx hardhat docgen", + "testnet": "npx hardhat test --network $1", + "deploy": "npx hardhat run scripts/deploy.ts --network $1", + "compile-echidna": "crytic-compile . && slither . --print echidna", + "fuzz-p": "echidna . --contract TestSwaplace --test-mode property --config echidna.config.yml", + "fuzz-a": "echidna . --contract TestSwaplace --test-mode assertion --config echidna.config.yml" + } } diff --git a/scripts/acceptSwap.ts b/scripts/acceptSwap.ts index 447046c..8ea1032 100644 --- a/scripts/acceptSwap.ts +++ b/scripts/acceptSwap.ts @@ -2,29 +2,29 @@ import { ethers } from "hardhat"; import abi from "../artifacts/contracts/Swaplace.sol/Swaplace.json"; export async function main() { - // Get the first account from the list of accounts - const [signer] = await ethers.getSigners(); + // Get the first account from the list of accounts + const [signer] = await ethers.getSigners(); - // Get the Swaplace address from .env file - const swaplaceAddress: string = process.env.SWAPLACE_ADDRESS || ""; + // Get the Swaplace address from .env file + const swaplaceAddress: string = process.env.SWAPLACE_ADDRESS || ""; - // Get the Swaplace instance - const Swaplace = new ethers.Contract(swaplaceAddress, abi.abi, signer); + // Get the Swaplace instance + const Swaplace = new ethers.Contract(swaplaceAddress, abi.abi, signer); - // Get the swap id to be accepted - const swapId = 1; + // Get the swap ID to be accepted + const swapId = 1; - // Accept the swap - const tx = await Swaplace.acceptSwap(swapId); + // Accept the swap + const tx = await Swaplace.acceptSwap(swapId); - // Wait for the transaction to be mined - await tx.wait(); + // Wait for the transaction to be mined + await tx.wait(); - // Log the transaction hash - console.log("\nTransaction Hash: ", tx); + // Log the transaction hash + console.log("\nTransaction Hash: ", tx); } main().catch((error) => { - console.error(error); - process.exitCode = 1; + console.error(error); + process.exitCode = 1; }); diff --git a/scripts/approve.ts b/scripts/approve.ts new file mode 100755 index 0000000..4870c8d --- /dev/null +++ b/scripts/approve.ts @@ -0,0 +1,25 @@ +import { ethers } from "ethers"; + +export async function approve( + contract: ethers.Contract, + spender: string, + amountOrId: bigint, +) { + try { + if (ethers.utils.isAddress(spender)) { + // Approve tokens + const tx = await contract.approve(spender, amountOrId); + + // Wait for the transaction to be mined + await tx.wait(); + + // Return the transaction response + return tx; + } else { + throw new Error("Invalid Ethereum address"); + } + } catch (error) { + console.error(error); + process.exitCode = 1; + } +} diff --git a/scripts/cancelSwap.ts b/scripts/cancelSwap.ts index 21b6b95..e316b44 100644 --- a/scripts/cancelSwap.ts +++ b/scripts/cancelSwap.ts @@ -2,29 +2,29 @@ import { ethers } from "hardhat"; import abi from "../artifacts/contracts/Swaplace.sol/Swaplace.json"; export async function main() { - // Get the first account from the list of accounts - const [signer] = await ethers.getSigners(); + // Get the first account from the list of accounts + const [signer] = await ethers.getSigners(); - // Get the Swaplace address from .env file - const swaplaceAddress: string = process.env.SWAPLACE_ADDRESS || ""; + // Get the Swaplace address from .env file + const swaplaceAddress: string = process.env.SWAPLACE_ADDRESS || ""; - // Get the Swaplace instance - const Swaplace = new ethers.Contract(swaplaceAddress, abi.abi, signer); + // Get the Swaplace instance + const Swaplace = new ethers.Contract(swaplaceAddress, abi.abi, signer); - // Get the swap id to be canceled - const swapId = 1; + // Get the swap ID to be canceled + const swapId = 1; - // Cancel the swap - const tx = await Swaplace.cancelSwap(swapId); + // Cancel the swap + const tx = await Swaplace.cancelSwap(swapId); - // Wait for the transaction to be mined - await tx.wait(); + // Wait for the transaction to be mined + await tx.wait(); - // Log the transaction hash - console.log("\nTransaction Hash: ", tx); + // Log the transaction hash + console.log("\nTransaction Hash: ", tx); } main().catch((error) => { - console.error(error); - process.exitCode = 1; + console.error(error); + process.exitCode = 1; }); diff --git a/scripts/createSwap.ts b/scripts/createSwap.ts index a3305a0..b7d06ad 100644 --- a/scripts/createSwap.ts +++ b/scripts/createSwap.ts @@ -4,50 +4,50 @@ import { Swap, composeSwap } from "../test/utils/SwapFactory"; import abi from "../artifacts/contracts/Swaplace.sol/Swaplace.json"; export async function main() { - // Get the first account from the list of accounts - const [signer] = await ethers.getSigners(); - - // Get the Swaplace address from .env file - const swaplaceAddress: string = process.env.SWAPLACE_ADDRESS || ""; - - // Get the Swaplace instance - const Swaplace = new ethers.Contract(swaplaceAddress, abi.abi, signer); - - // Fill the Swap struct - const owner = signer.address; - const allowed = ethers.constants.AddressZero; - const expiry = (await blocktimestamp()* 2); - - // Build the biding assets - const bidingAddr = ["0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"]; // USDC - const bidingAmountOrId = [1000]; - - // Build the asking assets - const askingAddr = ["0x2260fac5e5542a773aa44fbcfedf7c193bc2c599"]; // WBTC - const askingAmountOrId = [1]; - - // Compose the swap - const swap: Swap = await composeSwap( - owner, - allowed, - expiry, - bidingAddr, - bidingAmountOrId, - askingAddr, - askingAmountOrId - ); - - // Create the swap - const tx = await Swaplace.createSwap(swap); - - // Wait for the transaction to be mined - await tx.wait(); - - // Log the transaction hash - console.log("\nTransaction Hash: ", tx); + // Get the first account from the list of accounts + const [signer] = await ethers.getSigners(); + + // Get the Swaplace address from .env file + const swaplaceAddress: string = process.env.SWAPLACE_ADDRESS || ""; + + // Get the Swaplace instance + const Swaplace = new ethers.Contract(swaplaceAddress, abi.abi, signer); + + // Fill the Swap struct + const owner = signer.address; + const allowed = ethers.constants.AddressZero; + const expiry = (await blocktimestamp()) * 2; + + // Build the biding assets + const bidingAddr = ["0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"]; // USDC + const bidingAmountOrId = [1000]; + + // Build the asking assets + const askingAddr = ["0x2260fac5e5542a773aa44fbcfedf7c193bc2c599"]; // WBTC + const askingAmountOrId = [1]; + + // Compose the swap + const swap: Swap = await composeSwap( + owner, + allowed, + expiry, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + // Create the swap + const tx = await Swaplace.createSwap(swap); + + // Wait for the transaction to be mined + await tx.wait(); + + // Log the transaction hash + console.log("\nTransaction Hash: ", tx); } main().catch((error) => { - console.error(error); - process.exitCode = 1; + console.error(error); + process.exitCode = 1; }); diff --git a/scripts/deploy.ts b/scripts/deploy.ts index b969c91..9108fb9 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -2,22 +2,22 @@ import { ethers } from "hardhat"; import { deploy } from "../test/utils/utils"; async function main() { - // Get the first account from the list of accounts - const [signer] = await ethers.getSigners(); + // Get the first account from the list of accounts + const [signer] = await ethers.getSigners(); - // Deploy in the currrent network and return the Swaplace instance - const Swaplace = await deploy("Swaplace", signer); + // Deploy in the currrent network and return the Swaplace instance + const Swaplace = await deploy("Swaplace", signer); - // Log Contract address, the Tx then return the Contract instance - console.log( - "\nContract %s \nDeployed to %s \nAt Tx %s", - "Swaplace", - Swaplace.address, - Swaplace.deployTransaction.hash - ); + // Log Contract address, the Tx then return the Contract instance + console.log( + "\nContract %s \nDeployed to %s \nAt Tx %s", + "Swaplace", + Swaplace.address, + Swaplace.deployTransaction.hash, + ); } main().catch((error) => { - console.error(error); - process.exitCode = 1; + console.error(error); + process.exitCode = 1; }); diff --git a/scripts/deployMock.ts b/scripts/deployMock.ts new file mode 100644 index 0000000..52c07bd --- /dev/null +++ b/scripts/deployMock.ts @@ -0,0 +1,34 @@ +import { ethers } from "hardhat"; +import { deploy } from "../test/utils/utils"; + +export async function deployMock(signer: any) { + // Deploy in the currrent network and return the contract instance + const MockERC20 = await deploy("MockERC20", signer); + const MockERC721 = await deploy("MockERC721", signer); + + // Log Contract address, the Tx then return the Contract instance of MockERC20 + console.log( + "\nDEPLOY:\nContract %s \nDeployed to %s \nAt Tx %s", + "MockERC20", + MockERC20.address, + MockERC20.deployTransaction.hash, + ); + + // Log Contract address, the Tx then return the Contract instance of MockERC721 + console.log( + "\nContract %s \nDeployed to %s \nAt Tx %s", + "MockERC721", + MockERC721.address, + MockERC721.deployTransaction.hash, + ); + + // Return the transaction response + return { MockERC20, MockERC721 }; +} + +ethers.getSigners().then((signers) => { + deployMock(signers[0]).catch((error) => { + console.error(error); + process.exitCode = 1; + }); +}); diff --git a/scripts/getSwap.ts b/scripts/getSwap.ts index 6c5991a..dc52eab 100644 --- a/scripts/getSwap.ts +++ b/scripts/getSwap.ts @@ -2,27 +2,27 @@ import { ethers } from "hardhat"; import abi from "../artifacts/contracts/Swaplace.sol/Swaplace.json"; export async function main() { - // Get the first account from the list of accounts - const [signer] = await ethers.getSigners(); + // Get the first account from the list of accounts + const [signer] = await ethers.getSigners(); - // Get the Swaplace address from .env file - const swaplaceAddress: string = process.env.SWAPLACE_ADDRESS || ""; + // Get the Swaplace address from .env file + const swaplaceAddress: string = process.env.SWAPLACE_ADDRESS || ""; - // Get the Swaplace instance - const Swaplace = new ethers.Contract(swaplaceAddress, abi.abi, signer); + // Get the Swaplace instance + const Swaplace = new ethers.Contract(swaplaceAddress, abi.abi, signer); - // Get the swap id - const swapId = 1; + // Get the swap ID + const swapId = 1; - // Get the swap - const swap = await Swaplace.getSwap(swapId); + // Get the swap + const swap = await Swaplace.getSwap(swapId); - // Log the swap - console.log("\nSwap %: ", swapId); - console.log(swap); + // Log the swap + console.log("\nSwap %: ", swapId); + console.log(swap); } main().catch((error) => { - console.error(error); - process.exitCode = 1; + console.error(error); + process.exitCode = 1; }); diff --git a/scripts/mint.ts b/scripts/mint.ts new file mode 100644 index 0000000..8882999 --- /dev/null +++ b/scripts/mint.ts @@ -0,0 +1,25 @@ +import { ethers } from "ethers"; + +export async function mint( + contract: ethers.Contract, + amountOrId: bigint, + receiver: string, +) { + try { + if (ethers.utils.isAddress(receiver)) { + // Mint ERC tokens + const tx = await contract.mintTo(receiver, amountOrId); + + // Wait for the transaction to be mined + await tx.wait(); + + // Return the transaction response + return tx; + } else { + throw new Error("Invalid Ethereum address"); + } + } catch (error) { + console.error(error); + process.exitCode = 1; + } +} diff --git a/test/TestMockContracts.test.ts b/test/TestMockContracts.test.ts index e428933..5db1bf7 100644 --- a/test/TestMockContracts.test.ts +++ b/test/TestMockContracts.test.ts @@ -5,54 +5,54 @@ import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { deploy } from "./utils/utils"; describe("Swaplace", async function () { - // The deployed contracts - let MockERC20: Contract; - let MockERC721: Contract; + // The deployed contracts + let MockERC20: Contract; + let MockERC721: Contract; - // The signers of the test - let deployer: SignerWithAddress; - let owner: SignerWithAddress; - let acceptee: SignerWithAddress; + // The signers of the test + let deployer: SignerWithAddress; + let owner: SignerWithAddress; + let acceptee: SignerWithAddress; - before(async () => { - [deployer, owner, acceptee] = await ethers.getSigners(); - MockERC20 = await deploy("MockERC20", deployer); - MockERC721 = await deploy("MockERC721", deployer); - }); + before(async () => { + [deployer, owner, acceptee] = await ethers.getSigners(); + MockERC20 = await deploy("MockERC20", deployer); + MockERC721 = await deploy("MockERC721", deployer); + }); - it("Should test the {mint} function", async function () { - // Testing the mint of ERC20 - await MockERC20.mintTo(owner.address, 1000); - expect(await MockERC20.balanceOf(owner.address)).to.be.equals(1000); + it("Should test the {mint} function", async function () { + // Testing the mint of ERC20 + await MockERC20.mintTo(owner.address, 1000); + expect(await MockERC20.balanceOf(owner.address)).to.be.equals(1000); - // Testing the mint of ERC721 - await MockERC721.mintTo(owner.address, 1); - expect(await MockERC721.balanceOf(owner.address)).to.be.equals(1); - }); + // Testing the mint of ERC721 + await MockERC721.mintTo(owner.address, 1); + expect(await MockERC721.balanceOf(owner.address)).to.be.equals(1); + }); - 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); - }); + 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); + }); - it("Should test the {transferFrom} function", async function () { - // Testing the transfer of ERC20 - await MockERC20.connect(owner).transfer(acceptee.address, 1000); - expect(await MockERC20.balanceOf(owner.address)).to.be.equals(0); - expect(await MockERC20.balanceOf(acceptee.address)).to.be.equals(1000); - // Testing the transfer of ERC721 - await MockERC721.connect(owner).transferFrom( - owner.address, - acceptee.address, - 1 - ); - expect(await MockERC721.balanceOf(owner.address)).to.be.equals(0); - expect(await MockERC721.balanceOf(acceptee.address)).to.be.equals(1); - }); + it("Should test the {transferFrom} function", async function () { + // Testing the transfer of ERC20 + await MockERC20.connect(owner).transfer(acceptee.address, 1000); + expect(await MockERC20.balanceOf(owner.address)).to.be.equals(0); + expect(await MockERC20.balanceOf(acceptee.address)).to.be.equals(1000); + // Testing the transfer of ERC721 + await MockERC721.connect(owner).transferFrom( + owner.address, + acceptee.address, + 1, + ); + expect(await MockERC721.balanceOf(owner.address)).to.be.equals(0); + expect(await MockERC721.balanceOf(acceptee.address)).to.be.equals(1); + }); }); diff --git a/test/TestSwapFactory.test.ts b/test/TestSwapFactory.test.ts index 4df2574..5ff0915 100644 --- a/test/TestSwapFactory.test.ts +++ b/test/TestSwapFactory.test.ts @@ -6,240 +6,240 @@ import { Asset, makeAsset, makeSwap, composeSwap } from "./utils/SwapFactory"; import { blocktimestamp, deploy } from "./utils/utils"; describe("Swaplace Factory", async function () { - // The deployed contracts - let Swaplace: Contract; - let MockERC20: Contract; - let MockERC721: Contract; - - // The signers of the test - let deployer: SignerWithAddress; - let owner: SignerWithAddress; - let acceptee: SignerWithAddress; - - const zeroAddress = ethers.constants.AddressZero; - - before(async () => { - [deployer, owner, acceptee] = await ethers.getSigners(); - Swaplace = await deploy("Swaplace", deployer); - MockERC20 = await deploy("MockERC20", deployer); - MockERC721 = await deploy("MockERC721", deployer); - }); - - 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"); - - var asset: Asset = await makeAsset(MockERC721.address, 1); - expect(asset.addr).to.be.equals(MockERC721.address); - expect(asset.amountOrId).to.be.equals("1"); - }); - - it("Should be able to {makeAsset} in the off-chain matching on-chain", async function () { - var asset: Asset = await Swaplace.makeAsset(MockERC20.address, 1000); - var asset: Asset = await makeAsset(MockERC20.address, 1000); - - expect(asset.addr).to.be.equals(MockERC20.address); - expect(asset.amountOrId).to.be.equals("1000"); - }); - - it("Should be able to {makeSwap} with ERC20 and ERC721", async function () { - const expiry = (await blocktimestamp()) * 2; - - const ERC20Asset: Asset = await makeAsset(MockERC20.address, 1000); - const ERC721Asset: Asset = await makeAsset(MockERC721.address, 1); - - const swap = await makeSwap( - owner.address, - zeroAddress, - expiry, - [ERC20Asset], - [ERC721Asset] - ); - - expect(swap.owner).to.be.equals(owner.address); - expect(swap.expiry).to.be.equals(expiry); - expect(swap.allowed).to.be.equals(zeroAddress); - expect(swap.biding[0]).to.be.equals(ERC20Asset); - expect(swap.asking[0]).to.be.equals(ERC721Asset); - }); - - it("Should be able to {makeSwap} in the off-chain matching on-chain", async function () { - const expiry = (await blocktimestamp()) * 2; - - const ERC20Asset: Asset = await makeAsset(MockERC20.address, 1000); - const ERC721Asset: Asset = await makeAsset(MockERC721.address, 1); - - const swap = await makeSwap( - owner.address, - zeroAddress, - expiry, - [ERC20Asset], - [ERC721Asset] - ); - - const onchainSwap = await Swaplace.makeSwap( - owner.address, - zeroAddress, - expiry, - [ERC20Asset], - [ERC721Asset] - ); - - expect(swap.owner).to.be.equals(onchainSwap.owner); - expect(swap.expiry).to.be.equals(onchainSwap.expiry); - expect(swap.allowed).to.be.equals(onchainSwap.allowed); - - 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 expiry = (await blocktimestamp()) * 2; - - const ERC20Asset = await makeAsset(MockERC20.address, 1000); - const ERC721Asset = await makeAsset(MockERC721.address, 1); - - const swap = await makeSwap( - owner.address, - zeroAddress, - expiry, - [ERC20Asset, ERC721Asset], - [ERC20Asset, ERC721Asset] - ); - - expect(swap.owner).to.be.equals(owner.address); - expect(swap.expiry).to.be.equals(expiry); - expect(swap.biding[0]).to.be.equals(ERC20Asset); - expect(swap.biding[1]).to.be.equals(ERC721Asset); - expect(swap.asking[0]).to.be.equals(ERC20Asset); - expect(swap.asking[1]).to.be.equals(ERC721Asset); - }); - - it("Should be able to {composeSwap} using both ERC20, ERC721", async function () { - const expiry = (await blocktimestamp()) * 2; - - const bidingAddr = [MockERC20.address, MockERC721.address]; - const bidingAmountOrId = [1000, 1]; - - const askingAddr = [MockERC721.address]; - const askingAmountOrId = [2]; - - const swap = await composeSwap( - owner.address, - zeroAddress, - expiry, - bidingAddr, - bidingAmountOrId, - askingAddr, - askingAmountOrId - ); - - expect(swap.owner).to.be.equals(owner.address); - expect(swap.allowed).to.be.equals(zeroAddress); - expect(swap.expiry).to.be.equals(expiry); - }); - - it("Should revert using {composeSwap} without minimum expiry", async function () { - const expiry = 0; - - const bidingAddr = [MockERC20.address, MockERC721.address]; - const bidingAmountOrId = [1000, 1]; - - const askingAddr = [MockERC721.address]; - const askingAmountOrId = [2]; - - try { - await composeSwap( - owner.address, - zeroAddress, - expiry, - bidingAddr, - bidingAmountOrId, - askingAddr, - askingAmountOrId - ); - } catch (error: any) { - expect(error.message).to.be.equals("InvalidExpiry"); - } - }); - - it("Should revert using {composeSwap} with owner as address zero", async function () { - const expiry = (await blocktimestamp()) * 2; - - const bidingAddr = [MockERC20.address]; - const bidingAmountOrId = [1000]; - - const askingAddr = [MockERC721.address]; - const askingAmountOrId = [2]; - - try { - await composeSwap( - zeroAddress, - zeroAddress, - expiry, - bidingAddr, - bidingAmountOrId, - askingAddr, - askingAmountOrId - ); - } catch (error: any) { - expect(error.message).to.be.equals("InvalidOwnerAddress"); - } - }); - - it("Should revert using {composeSwap} with empty assets", async function () { - const expiry = (await blocktimestamp()) * 2; - - const bidingAddr = [MockERC20.address]; - const bidingAmountOrId = [1000]; - - const askingAddr: any[] = []; - const askingAmountOrId: any[] = []; - - try { - await composeSwap( - owner.address, - zeroAddress, - expiry, - bidingAddr, - bidingAmountOrId, - askingAddr, - askingAmountOrId - ); - } catch (error: any) { - expect(error.message).to.be.equals("InvalidAssetsLength"); - } - }); - - it("Should revert using {composeSwap} with empty assets length", async function () { - const expiry = (await blocktimestamp()) * 2; - - const bidingAddr = [MockERC20.address]; - const bidingAmountOrId = [1000]; - - const askingAddr = [MockERC721.address]; - const askingAmountOrId = [1, 999, 777]; - - try { - await composeSwap( - owner.address, - zeroAddress, - expiry, - bidingAddr, - bidingAmountOrId, - askingAddr, - askingAmountOrId - ); - } catch (error: any) { - expect(error.message).to.be.equals("InvalidAssetsLength"); - } - }); + // The deployed contracts + let Swaplace: Contract; + let MockERC20: Contract; + let MockERC721: Contract; + + // The signers of the test + let deployer: SignerWithAddress; + let owner: SignerWithAddress; + let acceptee: SignerWithAddress; + + const zeroAddress = ethers.constants.AddressZero; + + before(async () => { + [deployer, owner, acceptee] = await ethers.getSigners(); + Swaplace = await deploy("Swaplace", deployer); + MockERC20 = await deploy("MockERC20", deployer); + MockERC721 = await deploy("MockERC721", deployer); + }); + + 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"); + + var asset: Asset = await makeAsset(MockERC721.address, 1); + expect(asset.addr).to.be.equals(MockERC721.address); + expect(asset.amountOrId).to.be.equals("1"); + }); + + it("Should be able to {makeAsset} in the off-chain matching on-chain", async function () { + var asset: Asset = await Swaplace.makeAsset(MockERC20.address, 1000); + var asset: Asset = await makeAsset(MockERC20.address, 1000); + + expect(asset.addr).to.be.equals(MockERC20.address); + expect(asset.amountOrId).to.be.equals("1000"); + }); + + it("Should be able to {makeSwap} with ERC20 and ERC721", async function () { + const expiry = (await blocktimestamp()) * 2; + + const ERC20Asset: Asset = await makeAsset(MockERC20.address, 1000); + const ERC721Asset: Asset = await makeAsset(MockERC721.address, 1); + + const swap = await makeSwap( + owner.address, + zeroAddress, + expiry, + [ERC20Asset], + [ERC721Asset], + ); + + expect(swap.owner).to.be.equals(owner.address); + expect(swap.expiry).to.be.equals(expiry); + expect(swap.allowed).to.be.equals(zeroAddress); + expect(swap.biding[0]).to.be.equals(ERC20Asset); + expect(swap.asking[0]).to.be.equals(ERC721Asset); + }); + + it("Should be able to {makeSwap} in the off-chain matching on-chain", async function () { + const expiry = (await blocktimestamp()) * 2; + + const ERC20Asset: Asset = await makeAsset(MockERC20.address, 1000); + const ERC721Asset: Asset = await makeAsset(MockERC721.address, 1); + + const swap = await makeSwap( + owner.address, + zeroAddress, + expiry, + [ERC20Asset], + [ERC721Asset], + ); + + const onchainSwap = await Swaplace.makeSwap( + owner.address, + zeroAddress, + expiry, + [ERC20Asset], + [ERC721Asset], + ); + + expect(swap.owner).to.be.equals(onchainSwap.owner); + expect(swap.expiry).to.be.equals(onchainSwap.expiry); + expect(swap.allowed).to.be.equals(onchainSwap.allowed); + + 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 expiry = (await blocktimestamp()) * 2; + + const ERC20Asset = await makeAsset(MockERC20.address, 1000); + const ERC721Asset = await makeAsset(MockERC721.address, 1); + + const swap = await makeSwap( + owner.address, + zeroAddress, + expiry, + [ERC20Asset, ERC721Asset], + [ERC20Asset, ERC721Asset], + ); + + expect(swap.owner).to.be.equals(owner.address); + expect(swap.expiry).to.be.equals(expiry); + expect(swap.biding[0]).to.be.equals(ERC20Asset); + expect(swap.biding[1]).to.be.equals(ERC721Asset); + expect(swap.asking[0]).to.be.equals(ERC20Asset); + expect(swap.asking[1]).to.be.equals(ERC721Asset); + }); + + it("Should be able to {composeSwap} using both ERC20, ERC721", async function () { + const expiry = (await blocktimestamp()) * 2; + + const bidingAddr = [MockERC20.address, MockERC721.address]; + const bidingAmountOrId = [1000, 1]; + + const askingAddr = [MockERC721.address]; + const askingAmountOrId = [2]; + + const swap = await composeSwap( + owner.address, + zeroAddress, + expiry, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + expect(swap.owner).to.be.equals(owner.address); + expect(swap.allowed).to.be.equals(zeroAddress); + expect(swap.expiry).to.be.equals(expiry); + }); + + it("Should revert using {composeSwap} without minimum expiry", async function () { + const expiry = 0; + + const bidingAddr = [MockERC20.address, MockERC721.address]; + const bidingAmountOrId = [1000, 1]; + + const askingAddr = [MockERC721.address]; + const askingAmountOrId = [2]; + + try { + await composeSwap( + owner.address, + zeroAddress, + expiry, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + } catch (error: any) { + expect(error.message).to.be.equals("InvalidExpiry"); + } + }); + + it("Should revert using {composeSwap} with owner as address zero", async function () { + const expiry = (await blocktimestamp()) * 2; + + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [1000]; + + const askingAddr = [MockERC721.address]; + const askingAmountOrId = [2]; + + try { + await composeSwap( + zeroAddress, + zeroAddress, + expiry, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + } catch (error: any) { + expect(error.message).to.be.equals("InvalidOwnerAddress"); + } + }); + + it("Should revert using {composeSwap} with empty assets", async function () { + const expiry = (await blocktimestamp()) * 2; + + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [1000]; + + const askingAddr: any[] = []; + const askingAmountOrId: any[] = []; + + try { + await composeSwap( + owner.address, + zeroAddress, + expiry, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + } catch (error: any) { + expect(error.message).to.be.equals("InvalidAssetsLength"); + } + }); + + it("Should revert using {composeSwap} with empty assets length", async function () { + const expiry = (await blocktimestamp()) * 2; + + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [1000]; + + const askingAddr = [MockERC721.address]; + const askingAmountOrId = [1, 999, 777]; + + try { + await composeSwap( + owner.address, + zeroAddress, + expiry, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + } catch (error: any) { + expect(error.message).to.be.equals("InvalidAssetsLength"); + } + }); }); diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index 92c1c5b..4fd455d 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -6,487 +6,495 @@ import { Asset, Swap, composeSwap } from "./utils/SwapFactory"; import { blocktimestamp, deploy } from "./utils/utils"; describe("Swaplace", async function () { - // The deployed contracts - let Swaplace: Contract; - let MockERC20: Contract; - let MockERC721: Contract; - - // The signers of the test - let deployer: SignerWithAddress; - let owner: SignerWithAddress; - let acceptee: SignerWithAddress; - - // Solidity address(0) - const zeroAddress = ethers.constants.AddressZero; - - /** - * @dev The mock is helpful to avoid repetition in the code. - * You can set a `swap` variable with the mock returned value, - * and then edit the values you want to change. - */ - async function mockSwap() { - const bidingAddr = [MockERC721.address]; - const bidingAmountOrId = [1]; - - const askingAddr = [MockERC20.address]; - const askingAmountOrId = [50]; - - const swap: Swap = await composeSwap( - owner.address, - zeroAddress, - (await blocktimestamp()) * 2, - bidingAddr, - bidingAmountOrId, - askingAddr, - askingAmountOrId - ); - - return swap; - } - - before(async () => { - [deployer, owner, acceptee] = await ethers.getSigners(); - Swaplace = await deploy("Swaplace", deployer); - MockERC20 = await deploy("MockERC20", deployer); - MockERC721 = await deploy("MockERC721", deployer); - }); - - describe("Creating Swaps", () => { - context("Creating different types of Swaps", () => { - it("Should be able to create a 1-1 swap with ERC20", async function () { - const bidingAddr = [MockERC20.address]; - const bidingAmountOrId = [50]; - - const askingAddr = [MockERC20.address]; - const askingAmountOrId = [50]; - - const swap: Swap = await composeSwap( - owner.address, - zeroAddress, - (await blocktimestamp()) * 2, - bidingAddr, - bidingAmountOrId, - askingAddr, - askingAmountOrId - ); - - await expect(await Swaplace.connect(owner).createSwap(swap)) - .to.emit(Swaplace, "SwapCreated") - .withArgs(await Swaplace.totalSwaps(), owner.address, swap.expiry); - }); - - it("Should be able to create a 1-N swap with ERC20", async function () { - const bidingAddr = [MockERC20.address]; - const bidingAmountOrId = [50]; - - const askingAddr = [ - MockERC20.address, - MockERC20.address, - MockERC20.address, - ]; - const askingAmountOrId = [50, 100, 150]; - - const swap: Swap = await composeSwap( - owner.address, - zeroAddress, - (await blocktimestamp()) * 2, - bidingAddr, - bidingAmountOrId, - askingAddr, - askingAmountOrId - ); - - await expect(await Swaplace.connect(owner).createSwap(swap)) - .to.emit(Swaplace, "SwapCreated") - .withArgs(await Swaplace.totalSwaps(), owner.address, swap.expiry); - }); - - it("Should be able to create a N-N swap with ERC20", async function () { - const bidingAddr = [ - MockERC20.address, - MockERC20.address, - MockERC20.address, - ]; - const bidingAmountOrId = [50, 100, 150]; - - const askingAddr = [ - MockERC20.address, - MockERC20.address, - MockERC20.address, - ]; - const askingAmountOrId = [50, 100, 150]; - - const swap: Swap = await composeSwap( - owner.address, - zeroAddress, - (await blocktimestamp()) * 2, - bidingAddr, - bidingAmountOrId, - askingAddr, - askingAmountOrId - ); - - await expect(await Swaplace.connect(owner).createSwap(swap)) - .to.emit(Swaplace, "SwapCreated") - .withArgs(await Swaplace.totalSwaps(), owner.address, swap.expiry); - }); - - it("Should be able to create a 1-1 swap with ERC721", async function () { - const bidingAddr = [MockERC721.address]; - const bidingAmountOrId = [1]; - - const askingAddr = [MockERC721.address]; - const askingAmountOrId = [4]; - - const swap: Swap = await composeSwap( - owner.address, - zeroAddress, - (await blocktimestamp()) * 2, - bidingAddr, - bidingAmountOrId, - askingAddr, - askingAmountOrId - ); - - await expect(await Swaplace.connect(owner).createSwap(swap)) - .to.emit(Swaplace, "SwapCreated") - .withArgs(await Swaplace.totalSwaps(), owner.address, swap.expiry); - }); - - it("Should be able to create a 1-N swap with ERC721", async function () { - const bidingAddr = [MockERC721.address]; - const bidingAmountOrId = [1]; - - const askingAddr = [ - MockERC721.address, - MockERC721.address, - MockERC721.address, - ]; - const askingAmountOrId = [4, 5, 6]; - - const swap: Swap = await composeSwap( - owner.address, - zeroAddress, - (await blocktimestamp()) * 2, - bidingAddr, - bidingAmountOrId, - askingAddr, - askingAmountOrId - ); - - await expect(await Swaplace.connect(owner).createSwap(swap)) - .to.emit(Swaplace, "SwapCreated") - .withArgs(await Swaplace.totalSwaps(), owner.address, swap.expiry); - }); - - it("Should be able to create a N-N swap with ERC721", async function () { - const bidingAddr = [ - MockERC721.address, - MockERC721.address, - MockERC721.address, - ]; - const bidingAmountOrId = [1, 2, 3]; - - const askingAddr = [ - MockERC721.address, - MockERC721.address, - MockERC721.address, - ]; - const askingAmountOrId = [4, 5, 6]; - - const swap: Swap = await composeSwap( - owner.address, - zeroAddress, - (await blocktimestamp()) * 2, - bidingAddr, - bidingAmountOrId, - askingAddr, - askingAmountOrId - ); - - await expect(await Swaplace.connect(owner).createSwap(swap)) - .to.emit(Swaplace, "SwapCreated") - .withArgs(await Swaplace.totalSwaps(), owner.address, swap.expiry); - }); - }); - - context("Reverts when creating Swaps", () => { - it("Should revert when {owner} is not {msg.sender}", async function () { - const swap = await mockSwap(); - await expect(Swaplace.connect(acceptee).createSwap(swap)) - .to.be.revertedWithCustomError(Swaplace, `InvalidAddress`) - .withArgs(acceptee.address); - }); - - it("Should revert when {expiry} is smaller than {block.timestamp}", async function () { - const swap = await mockSwap(); - - swap.expiry /= 2; - - await expect(Swaplace.connect(owner).createSwap(swap)) - .to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`) - .withArgs(swap.expiry); - }); - - it("Should revert when {biding} and {asking} lengths are equal 0", async function () { - const swap = await mockSwap(); - swap.biding = []; - swap.asking = []; - - await expect( - Swaplace.connect(owner).createSwap(swap) - ).to.be.revertedWithCustomError(Swaplace, `InvalidAssetsLength`); - }); - }); - }); - - describe("Accepting Swaps", () => { - var swap: Swap; - beforeEach(async () => { - MockERC20 = await deploy("MockERC20", deployer); - MockERC721 = await deploy("MockERC721", deployer); - - await MockERC721.mintTo(owner.address, 1); - await MockERC20.mintTo(acceptee.address, 1000); - - await MockERC721.connect(owner).approve(Swaplace.address, 1); - await MockERC20.connect(acceptee).approve(Swaplace.address, 1000); - - const bidingAddr = [MockERC721.address]; - const bidingAmountOrId = [1]; - - const askingAddr = [MockERC20.address]; - const askingAmountOrId = [1000]; - - swap = await composeSwap( - owner.address, - zeroAddress, - (await blocktimestamp()) * 2, - bidingAddr, - bidingAmountOrId, - askingAddr, - askingAmountOrId - ); - }); - - context("Accepting different types of Swaps", () => { - it("Should be able to {acceptSwap} as 1-1 Swap", async function () { - await Swaplace.connect(owner).createSwap(swap); - await expect( - await Swaplace.connect(acceptee).acceptSwap(await Swaplace.totalSwaps()) - ) - .to.emit(Swaplace, "SwapAccepted") - .withArgs(await Swaplace.totalSwaps(), acceptee.address); - }); - - it("Should be able to {acceptSwap} as N-N Swap", async function () { - await MockERC20.mintTo(owner.address, 500); - await MockERC721.mintTo(acceptee.address, 5); - - await MockERC20.connect(owner).approve(Swaplace.address, 500); - await MockERC721.connect(acceptee).approve(Swaplace.address, 5); - - const bidingAsset: Asset = await Swaplace.makeAsset( - MockERC20.address, - 500 - ); - const askingAsset: Asset = await Swaplace.makeAsset( - MockERC721.address, - 5 - ); - - swap.biding.push(bidingAsset); - swap.asking.push(askingAsset); - - await expect(await Swaplace.connect(owner).createSwap(swap)) - .to.emit(Swaplace, "SwapCreated") - .withArgs(await Swaplace.totalSwaps(), owner.address, swap.expiry); - - await expect( - await Swaplace.connect(acceptee).acceptSwap(await Swaplace.totalSwaps()) - ) - .to.emit(Swaplace, "SwapAccepted") - .withArgs(await Swaplace.totalSwaps(), acceptee.address); - }); - - it("Should be able to {acceptSwap} as P2P Swap", async function () { - await MockERC20.mintTo(owner.address, 1000); - await MockERC721.mintTo(acceptee.address, 10); - - await MockERC20.connect(owner).approve(Swaplace.address, 1000); - await MockERC721.connect(acceptee).approve(Swaplace.address, 10); - - const swap = await mockSwap(); - swap.allowed = acceptee.address; - - await expect(await Swaplace.connect(owner).createSwap(swap)) - .to.emit(Swaplace, "SwapCreated") - .withArgs(await Swaplace.totalSwaps(), owner.address, swap.expiry); - - await expect( - await Swaplace.connect(acceptee).acceptSwap(await Swaplace.totalSwaps()) - ) - .to.emit(Swaplace, "SwapAccepted") - .withArgs(await Swaplace.totalSwaps(), acceptee.address); - }); - }); - - context("Reverts when accepting Swaps", () => { - it("Should revert when calling {acceptSwap} twice", async function () { - await Swaplace.connect(owner).createSwap(swap); - - await expect( - await Swaplace.connect(acceptee).acceptSwap(await Swaplace.totalSwaps()) - ) - .to.emit(Swaplace, "SwapAccepted") - .withArgs(await Swaplace.totalSwaps(), acceptee.address); - - await expect( - Swaplace.connect(acceptee).acceptSwap(await Swaplace.totalSwaps()) - ) - .to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`) - .withArgs(0); - }); - - it("Should revert when {expiry} is smaller than {block.timestamp}", async function () { - await Swaplace.connect(owner).createSwap(swap); - - await network.provider.send("evm_increaseTime", [swap.expiry * 2]); - - await expect( - Swaplace.connect(owner).acceptSwap(await Swaplace.totalSwaps()) - ) - .to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`) - .withArgs(swap.expiry); - }); - - it("Should revert when {allowance} is not provided", async function () { - await MockERC721.connect(owner).approve(zeroAddress, 1); - - await Swaplace.connect(owner).createSwap(swap); - - await expect( - Swaplace.connect(acceptee).acceptSwap(await Swaplace.totalSwaps()) - ).to.be.revertedWith(`ERC721: caller is not token owner or approved`); - }); - - it("Should revert when {acceptSwap} as not allowed to P2P Swap", async function () { - await MockERC20.mintTo(owner.address, 1000); - await MockERC721.mintTo(acceptee.address, 10); - - await MockERC20.connect(owner).approve(Swaplace.address, 1000); - await MockERC721.connect(acceptee).approve(Swaplace.address, 10); - - const swap = await mockSwap(); - swap.allowed = deployer.address; - - await expect(await Swaplace.connect(owner).createSwap(swap)) - .to.emit(Swaplace, "SwapCreated") - .withArgs(await Swaplace.totalSwaps(), owner.address, swap.expiry); - - await expect( - Swaplace.connect(acceptee).acceptSwap(await Swaplace.totalSwaps()) - ) - .to.be.revertedWithCustomError(Swaplace, "InvalidAddress") - .withArgs(acceptee.address); - }); - }); - }); - - describe("Canceling Swaps", () => { - context("Canceling Swaps", () => { - var swap: Swap; - before(async () => { - swap = await mockSwap(); - await Swaplace.connect(owner).createSwap(swap); - }); - - it("Should be able to {cancelSwap} a Swap", async function () { - const lastSwap = await Swaplace.totalSwaps(); - expect(await Swaplace.connect(owner).cancelSwap(lastSwap)) - .to.emit(Swaplace, "SwapCanceled") - .withArgs(lastSwap, swap.owner); - }); - - 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)) - .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(acceptee).cancelSwap(lastSwap)) - .to.be.revertedWithCustomError(Swaplace, `InvalidAddress`) - .withArgs(acceptee.address); - }); - - it("Should revert when {expiry} is smaller than {block.timestamp}", async function () { - await network.provider.send("evm_increaseTime", [swap.expiry * 2]); - - const lastSwap = await Swaplace.totalSwaps(); - await expect(Swaplace.connect(owner).cancelSwap(lastSwap)) - .to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`) - .withArgs(swap.expiry); - }); - }); - }); - - describe("Fetching Swaps", () => { - var swap: Swap; - before(async () => { - MockERC20 = await deploy("MockERC20", deployer); - MockERC721 = await deploy("MockERC721", deployer); - - await MockERC721.mintTo(owner.address, 1); - await MockERC20.mintTo(acceptee.address, 1000); - - const bidingAddr = [MockERC721.address]; - const bidingAmountOrId = [1]; - - const askingAddr = [MockERC20.address]; - const askingAmountOrId = [1000]; - - swap = await composeSwap( - owner.address, - zeroAddress, - (await blocktimestamp()) * 2, - bidingAddr, - bidingAmountOrId, - askingAddr, - askingAmountOrId - ); - - await Swaplace.connect(owner).createSwap(swap); - }); - - it("Should be able to {getSwap}", async function () { - const lastSwap = await Swaplace.totalSwaps(); - 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 - expect(fetchedSwap.expiry).not.to.be.equals(0); - expect(fetchedSwap.biding.length).to.be.greaterThan(0); - expect(fetchedSwap.asking.length).to.be.greaterThan(0); - }); - - it("Should return empty with {getSwap} when Swap is non-existant", async function () { - const imaginarySwapId = 777; - 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); - expect(fetchedSwap.allowed).to.be.deep.equals(zeroAddress); - expect(fetchedSwap.expiry).to.be.deep.equals(0); - }); - }); + // The deployed contracts + let Swaplace: Contract; + let MockERC20: Contract; + let MockERC721: Contract; + + // The signers of the test + let deployer: SignerWithAddress; + let owner: SignerWithAddress; + let acceptee: SignerWithAddress; + + // Solidity address(0) + const zeroAddress = ethers.constants.AddressZero; + + /** + * @dev The mock is helpful to avoid repetition in the code. + * You can set a `swap` variable with the mock returned value, + * and then edit the values you want to change. + */ + async function mockSwap() { + const bidingAddr = [MockERC721.address]; + const bidingAmountOrId = [1]; + + const askingAddr = [MockERC20.address]; + const askingAmountOrId = [50]; + + const swap: Swap = await composeSwap( + owner.address, + zeroAddress, + (await blocktimestamp()) * 2, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + return swap; + } + + before(async () => { + [deployer, owner, acceptee] = await ethers.getSigners(); + Swaplace = await deploy("Swaplace", deployer); + MockERC20 = await deploy("MockERC20", deployer); + MockERC721 = await deploy("MockERC721", deployer); + }); + + describe("Creating Swaps", () => { + context("Creating different types of Swaps", () => { + it("Should be able to create a 1-1 swap with ERC20", async function () { + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [50]; + + const askingAddr = [MockERC20.address]; + const askingAmountOrId = [50]; + + const swap: Swap = await composeSwap( + owner.address, + zeroAddress, + (await blocktimestamp()) * 2, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs(await Swaplace.totalSwaps(), owner.address, swap.allowed, swap.expiry); + }); + + it("Should be able to create a 1-N swap with ERC20", async function () { + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [50]; + + const askingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const askingAmountOrId = [50, 100, 150]; + + const swap: Swap = await composeSwap( + owner.address, + zeroAddress, + (await blocktimestamp()) * 2, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs(await Swaplace.totalSwaps(), owner.address, swap.allowed, swap.expiry); + }); + + it("Should be able to create a N-N swap with ERC20", async function () { + const bidingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const bidingAmountOrId = [50, 100, 150]; + + const askingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const askingAmountOrId = [50, 100, 150]; + + const swap: Swap = await composeSwap( + owner.address, + zeroAddress, + (await blocktimestamp()) * 2, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs(await Swaplace.totalSwaps(), owner.address, swap.allowed, swap.expiry); + }); + + it("Should be able to create a 1-1 swap with ERC721", async function () { + const bidingAddr = [MockERC721.address]; + const bidingAmountOrId = [1]; + + const askingAddr = [MockERC721.address]; + const askingAmountOrId = [4]; + + const swap: Swap = await composeSwap( + owner.address, + zeroAddress, + (await blocktimestamp()) * 2, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs(await Swaplace.totalSwaps(), owner.address, swap.allowed, swap.expiry); + }); + + it("Should be able to create a 1-N swap with ERC721", async function () { + const bidingAddr = [MockERC721.address]; + const bidingAmountOrId = [1]; + + const askingAddr = [ + MockERC721.address, + MockERC721.address, + MockERC721.address, + ]; + const askingAmountOrId = [4, 5, 6]; + + const swap: Swap = await composeSwap( + owner.address, + zeroAddress, + (await blocktimestamp()) * 2, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs(await Swaplace.totalSwaps(), owner.address, swap.allowed, swap.expiry); + }); + + it("Should be able to create a N-N swap with ERC721", async function () { + const bidingAddr = [ + MockERC721.address, + MockERC721.address, + MockERC721.address, + ]; + const bidingAmountOrId = [1, 2, 3]; + + const askingAddr = [ + MockERC721.address, + MockERC721.address, + MockERC721.address, + ]; + const askingAmountOrId = [4, 5, 6]; + + const swap: Swap = await composeSwap( + owner.address, + zeroAddress, + (await blocktimestamp()) * 2, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs(await Swaplace.totalSwaps(), owner.address, swap.allowed, swap.expiry); + }); + }); + + context("Reverts when creating Swaps", () => { + it("Should revert when {owner} is not {msg.sender}", async function () { + const swap = await mockSwap(); + await expect(Swaplace.connect(acceptee).createSwap(swap)) + .to.be.revertedWithCustomError(Swaplace, `InvalidAddress`) + .withArgs(acceptee.address); + }); + + it("Should revert when {expiry} is smaller than {block.timestamp}", async function () { + const swap = await mockSwap(); + + swap.expiry /= 2; + + await expect(Swaplace.connect(owner).createSwap(swap)) + .to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`) + .withArgs(swap.expiry); + }); + + it("Should revert when {biding} and {asking} lengths are equal 0", async function () { + const swap = await mockSwap(); + swap.biding = []; + swap.asking = []; + + await expect( + Swaplace.connect(owner).createSwap(swap), + ).to.be.revertedWithCustomError(Swaplace, `InvalidAssetsLength`); + }); + }); + }); + + describe("Accepting Swaps", () => { + var swap: Swap; + beforeEach(async () => { + MockERC20 = await deploy("MockERC20", deployer); + MockERC721 = await deploy("MockERC721", deployer); + + await MockERC721.mintTo(owner.address, 1); + await MockERC20.mintTo(acceptee.address, 1000); + + await MockERC721.connect(owner).approve(Swaplace.address, 1); + await MockERC20.connect(acceptee).approve(Swaplace.address, 1000); + + const bidingAddr = [MockERC721.address]; + const bidingAmountOrId = [1]; + + const askingAddr = [MockERC20.address]; + const askingAmountOrId = [1000]; + + swap = await composeSwap( + owner.address, + zeroAddress, + (await blocktimestamp()) * 2, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + }); + + context("Accepting different types of Swaps", () => { + it("Should be able to {acceptSwap} as 1-1 Swap", async function () { + await Swaplace.connect(owner).createSwap(swap); + await expect( + await Swaplace.connect(acceptee).acceptSwap( + await Swaplace.totalSwaps(), + ), + ) + .to.emit(Swaplace, "SwapAccepted") + .withArgs(await Swaplace.totalSwaps(), acceptee.address); + }); + + it("Should be able to {acceptSwap} as N-N Swap", async function () { + await MockERC20.mintTo(owner.address, 500); + await MockERC721.mintTo(acceptee.address, 5); + + await MockERC20.connect(owner).approve(Swaplace.address, 500); + await MockERC721.connect(acceptee).approve(Swaplace.address, 5); + + const bidingAsset: Asset = await Swaplace.makeAsset( + MockERC20.address, + 500, + ); + const askingAsset: Asset = await Swaplace.makeAsset( + MockERC721.address, + 5, + ); + + swap.biding.push(bidingAsset); + swap.asking.push(askingAsset); + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs(await Swaplace.totalSwaps(), owner.address, swap.allowed, swap.expiry); + + await expect( + await Swaplace.connect(acceptee).acceptSwap( + await Swaplace.totalSwaps(), + ), + ) + .to.emit(Swaplace, "SwapAccepted") + .withArgs(await Swaplace.totalSwaps(), acceptee.address); + }); + + it("Should be able to {acceptSwap} as P2P Swap", async function () { + await MockERC20.mintTo(owner.address, 1000); + await MockERC721.mintTo(acceptee.address, 10); + + await MockERC20.connect(owner).approve(Swaplace.address, 1000); + await MockERC721.connect(acceptee).approve(Swaplace.address, 10); + + const swap = await mockSwap(); + swap.allowed = acceptee.address; + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs(await Swaplace.totalSwaps(), owner.address, swap.allowed, swap.expiry); + + await expect( + await Swaplace.connect(acceptee).acceptSwap( + await Swaplace.totalSwaps(), + ), + ) + .to.emit(Swaplace, "SwapAccepted") + .withArgs(await Swaplace.totalSwaps(), acceptee.address); + }); + }); + + context("Reverts when accepting Swaps", () => { + it("Should revert when calling {acceptSwap} twice", async function () { + await Swaplace.connect(owner).createSwap(swap); + + await expect( + await Swaplace.connect(acceptee).acceptSwap( + await Swaplace.totalSwaps(), + ), + ) + .to.emit(Swaplace, "SwapAccepted") + .withArgs(await Swaplace.totalSwaps(), acceptee.address); + + await expect( + Swaplace.connect(acceptee).acceptSwap(await Swaplace.totalSwaps()), + ) + .to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`) + .withArgs(0); + }); + + it("Should revert when {expiry} is smaller than {block.timestamp}", async function () { + await Swaplace.connect(owner).createSwap(swap); + + await network.provider.send("evm_increaseTime", [swap.expiry * 2]); + + await expect( + Swaplace.connect(owner).acceptSwap(await Swaplace.totalSwaps()), + ) + .to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`) + .withArgs(swap.expiry); + }); + + it("Should revert when {allowance} is not provided", async function () { + await MockERC721.connect(owner).approve(zeroAddress, 1); + + await Swaplace.connect(owner).createSwap(swap); + + await expect( + Swaplace.connect(acceptee).acceptSwap(await Swaplace.totalSwaps()), + ).to.be.revertedWith(`ERC721: caller is not token owner or approved`); + }); + + it("Should revert when {acceptSwap} as not allowed to P2P Swap", async function () { + await MockERC20.mintTo(owner.address, 1000); + await MockERC721.mintTo(acceptee.address, 10); + + await MockERC20.connect(owner).approve(Swaplace.address, 1000); + await MockERC721.connect(acceptee).approve(Swaplace.address, 10); + + const swap = await mockSwap(); + swap.allowed = deployer.address; + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs(await Swaplace.totalSwaps(), owner.address, swap.allowed, swap.expiry); + + await expect( + Swaplace.connect(acceptee).acceptSwap(await Swaplace.totalSwaps()), + ) + .to.be.revertedWithCustomError(Swaplace, "InvalidAddress") + .withArgs(acceptee.address); + }); + }); + }); + + describe("Canceling Swaps", () => { + context("Canceling Swaps", () => { + var swap: Swap; + before(async () => { + swap = await mockSwap(); + await Swaplace.connect(owner).createSwap(swap); + }); + + it("Should be able to {cancelSwap} a Swap", async function () { + const lastSwap = await Swaplace.totalSwaps(); + expect(await Swaplace.connect(owner).cancelSwap(lastSwap)) + .to.emit(Swaplace, "SwapCanceled") + .withArgs(lastSwap, swap.owner); + }); + + 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)) + .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(acceptee).cancelSwap(lastSwap)) + .to.be.revertedWithCustomError(Swaplace, `InvalidAddress`) + .withArgs(acceptee.address); + }); + + it("Should revert when {expiry} is smaller than {block.timestamp}", async function () { + await network.provider.send("evm_increaseTime", [swap.expiry * 2]); + + const lastSwap = await Swaplace.totalSwaps(); + await expect(Swaplace.connect(owner).cancelSwap(lastSwap)) + .to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`) + .withArgs(swap.expiry); + }); + }); + }); + + describe("Fetching Swaps", () => { + var swap: Swap; + before(async () => { + MockERC20 = await deploy("MockERC20", deployer); + MockERC721 = await deploy("MockERC721", deployer); + + await MockERC721.mintTo(owner.address, 1); + await MockERC20.mintTo(acceptee.address, 1000); + + const bidingAddr = [MockERC721.address]; + const bidingAmountOrId = [1]; + + const askingAddr = [MockERC20.address]; + const askingAmountOrId = [1000]; + + swap = await composeSwap( + owner.address, + zeroAddress, + (await blocktimestamp()) * 2, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await Swaplace.connect(owner).createSwap(swap); + }); + + it("Should be able to {getSwap}", async function () { + const lastSwap = await Swaplace.totalSwaps(); + 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 + expect(fetchedSwap.expiry).not.to.be.equals(0); + expect(fetchedSwap.biding.length).to.be.greaterThan(0); + expect(fetchedSwap.asking.length).to.be.greaterThan(0); + }); + + it("Should return empty with {getSwap} when Swap is non-existant", async function () { + const imaginarySwapId = 777; + 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); + expect(fetchedSwap.allowed).to.be.deep.equals(zeroAddress); + expect(fetchedSwap.expiry).to.be.deep.equals(0); + }); + }); }); diff --git a/test/utils/SwapFactory.ts b/test/utils/SwapFactory.ts index 4fbc931..d4a3419 100644 --- a/test/utils/SwapFactory.ts +++ b/test/utils/SwapFactory.ts @@ -4,88 +4,88 @@ import { ethers } from "hardhat"; * @dev See {ISwapFactory-Asset}. */ export interface Asset { - addr: string; - amountOrId: bigint; + addr: string; + amountOrId: bigint; } /** * @dev See {ISwap-Swap}. */ export interface Swap { - owner: string; - allowed: string; - expiry: number; - biding: Asset[]; - asking: Asset[]; + owner: string; + allowed: string; + expiry: number; + biding: Asset[]; + asking: Asset[]; } /** * @dev See {ISwapFactory-makeAsset}. */ export async function makeAsset( - addr: string, - amountOrId: number | bigint + addr: string, + amountOrId: number | bigint, ): Promise { - // validate if its an ethereum address - if (!ethers.utils.isAddress(addr)) { - throw new Error("InvalidAddressFormat"); - } + // validate if its an ethereum address + if (!ethers.utils.isAddress(addr)) { + throw new Error("InvalidAddressFormat"); + } - // if the amount is negative, it will throw an error - if (amountOrId < 0) { - throw new Error("AmountOrIdCannotBeNegative"); - } + // if the amount is negative, it will throw an error + if (amountOrId < 0) { + throw new Error("AmountOrIdCannotBeNegative"); + } - /** - * @dev Create a new Asset type described by the contract interface. - * - * NOTE: If the amount is in number format, it will be converted to bigint. - * EVM works with a lot of decimals and might overload using number type. - */ - const asset: Asset = { - addr: addr, - amountOrId: typeof amountOrId == "number" ? BigInt(amountOrId) : amountOrId, - }; + /** + * @dev Create a new Asset type described by the contract interface. + * + * NOTE: If the amount is in number format, it will be converted to bigint. + * EVM works with a lot of decimals and might overload using number type. + */ + const asset: Asset = { + addr: addr, + amountOrId: typeof amountOrId == "number" ? BigInt(amountOrId) : amountOrId, + }; - return asset; + return asset; } /** * @dev See {ISwapFactory-makeSwap}. */ export async function makeSwap( - owner: any, - allowed: any, - expiry: any, - biding: Asset[], - asking: Asset[] + owner: any, + allowed: any, + expiry: any, + biding: Asset[], + asking: Asset[], ) { - // check for the current `block.timestamp` because `expiry` cannot be in the past - const timestamp = (await ethers.provider.getBlock("latest")).timestamp; - if (expiry < timestamp) { - throw new Error("InvalidExpiry"); - } + // check for the current `block.timestamp` because `expiry` cannot be in the past + const timestamp = (await ethers.provider.getBlock("latest")).timestamp; + if (expiry < timestamp) { + throw new Error("InvalidExpiry"); + } - /** - * @dev one of the swapped assets should never be empty or it should be directly - * transfered using {ERC20-transferFrom} or {ERC721-safeTransferFrom} - * - * NOTE: if the purpose of the swap is to transfer the asset directly using Swaplace, - * then any small token quantity should be used as the swap asset. - */ - if (biding.length == 0 || asking.length == 0) { - throw new Error("InvalidAssetsLength"); - } + /** + * @dev one of the swapped assets should never be empty or it should be directly + * transfered using {ERC20-transferFrom} or {ERC721-safeTransferFrom} + * + * NOTE: if the purpose of the swap is to transfer the asset directly using Swaplace, + * then any small token quantity should be used as the swap asset. + */ + if (biding.length == 0 || asking.length == 0) { + throw new Error("InvalidAssetsLength"); + } - const swap: Swap = { - owner: owner, - allowed: allowed, - expiry: expiry, - biding: biding, - asking: asking, - }; + const swap: Swap = { + owner: owner, + allowed: allowed, + expiry: expiry, + biding: biding, + asking: asking, + }; - return swap; + return swap; } /** @@ -108,38 +108,38 @@ export async function makeSwap( * - `askingAddr` and `askingAmountOrId` must have the same length. */ export async function composeSwap( - owner: any, - allowed: any, - expiry: any, - bidingAddr: any[], - bidingAmountOrId: any[], - askingAddr: any[], - askingAmountOrId: any[] + owner: any, + allowed: any, + expiry: any, + bidingAddr: any[], + bidingAmountOrId: any[], + askingAddr: any[], + askingAmountOrId: any[], ) { - // lenght of addresses and their respective amounts must be equal - if ( - bidingAddr.length != bidingAmountOrId.length || - askingAddr.length != askingAmountOrId.length - ) { - throw new Error("InvalidAssetsLength"); - } + // lenght of addresses and their respective amounts must be equal + if ( + bidingAddr.length != bidingAmountOrId.length || + askingAddr.length != askingAmountOrId.length + ) { + throw new Error("InvalidAssetsLength"); + } - // push new assets to the array of bids and asks - const biding: any[] = []; - bidingAddr.forEach(async (addr, index) => { - biding.push(await makeAsset(addr, bidingAmountOrId[index])); - }); + // push new assets to the array of bids and asks + const biding: any[] = []; + bidingAddr.forEach(async (addr, index) => { + biding.push(await makeAsset(addr, bidingAmountOrId[index])); + }); - const asking: any[] = []; - askingAddr.forEach(async (addr, index) => { - asking.push(await makeAsset(addr, askingAmountOrId[index])); - }); + const asking: any[] = []; + askingAddr.forEach(async (addr, index) => { + asking.push(await makeAsset(addr, askingAmountOrId[index])); + }); - return await makeSwap(owner, allowed, expiry, biding, asking); + return await makeSwap(owner, allowed, expiry, biding, asking); } module.exports = { - makeAsset, - makeSwap, - composeSwap, + makeAsset, + makeSwap, + composeSwap, }; diff --git a/test/utils/utils.ts b/test/utils/utils.ts index 79d1408..88e3b5f 100644 --- a/test/utils/utils.ts +++ b/test/utils/utils.ts @@ -8,7 +8,7 @@ import { ethers } from "hardhat"; * Networks should be in `hardhat.config.ts` file or via command line. */ export async function blocktimestamp(): Promise { - return (await ethers.provider.getBlock("latest")).timestamp; + return (await ethers.provider.getBlock("latest")).timestamp; } /** @@ -18,19 +18,19 @@ export async function blocktimestamp(): Promise { * @param signer The signer to use. */ export async function deploy(contractName: any, signer: any) { - // Get Contract Factory for contractName - const ContractFactory = await ethers.getContractFactory(contractName, signer); + // Get Contract Factory for contractName + const ContractFactory = await ethers.getContractFactory(contractName, signer); - // Deploy the Contract - const Contract = await ContractFactory.deploy(); + // Deploy the Contract + const Contract = await ContractFactory.deploy(); - // Wait for Contract to be deployed - await Contract.deployed(); + // Wait for Contract to be deployed + await Contract.deployed(); - return Contract; + return Contract; } module.exports = { - blocktimestamp, - deploy, + blocktimestamp, + deploy, }; diff --git a/tsconfig.json b/tsconfig.json index b1e5526..7e42aef 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,4 +14,4 @@ "exclude": ["dist", "node_modules"], "include": ["./test", "./src", "./scripts"], "files": ["./hardhat.config.ts"] -} \ No newline at end of file +}