From d5ccb02410f21ea16282a604613d4e187e1eeb5c Mon Sep 17 00:00:00 2001 From: 0xneves Date: Tue, 20 Feb 2024 23:25:53 +0700 Subject: [PATCH 01/91] test 1 --- hardhat.config.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index 2c5ee05..d2f56b7 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -4,8 +4,10 @@ import "solidity-docgen"; import dotenv from "dotenv"; dotenv.config(); -const DEPLOYER_PRIVATE_KEY = - "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; +// const DEPLOYER_PRIVATE_KEY = +// "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; + +const DEPLOYER_PRIVATE_KEY = process.env.DEPLOYER_PRIVATE_KEY; const config: HardhatUserConfig = { solidity: "0.8.17", @@ -21,8 +23,8 @@ const config: HardhatUserConfig = { accounts: [ `${ process.env.DEPLOYER_PRIVATE_KEY - ? process.env.DEPLOYER_PRIVATE_KEY - : DEPLOYER_PRIVATE_KEY + // ? process.env.DEPLOYER_PRIVATE_KEY + // : DEPLOYER_PRIVATE_KEY }`, ], }, From 7f49c0249bb45077ea1f609c7a80c9d55a4ace9b Mon Sep 17 00:00:00 2001 From: 0xneves Date: Tue, 20 Feb 2024 23:27:45 +0700 Subject: [PATCH 02/91] test 2 --- hardhat.config.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index d2f56b7..41a2a5b 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -20,13 +20,7 @@ const config: HardhatUserConfig = { */ sepolia: { url: `${process.env.SEPOLIA_RPC_URL}`, - accounts: [ - `${ - process.env.DEPLOYER_PRIVATE_KEY - // ? process.env.DEPLOYER_PRIVATE_KEY - // : DEPLOYER_PRIVATE_KEY - }`, - ], + accounts: [`${process.env.DEPLOYER_PRIVATE_KEY}`], }, goerli: { url: `${process.env.SEPOLIA_RPC_URL}`, From 70bc6387d10284c0417fa6392849a4c92c80fd39 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Tue, 20 Feb 2024 23:31:38 +0700 Subject: [PATCH 03/91] fix: workflows --- hardhat.config.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index 41a2a5b..206f8b2 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -4,9 +4,6 @@ import "solidity-docgen"; import dotenv from "dotenv"; dotenv.config(); -// const DEPLOYER_PRIVATE_KEY = -// "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; - const DEPLOYER_PRIVATE_KEY = process.env.DEPLOYER_PRIVATE_KEY; const config: HardhatUserConfig = { From b2193fb264fbc7bd3fb58af2046131527922d33d Mon Sep 17 00:00:00 2001 From: 0xneves Date: Tue, 20 Feb 2024 23:31:49 +0700 Subject: [PATCH 04/91] fix: worklow --- hardhat.config.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index 206f8b2..ac4911a 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -16,10 +16,6 @@ const config: HardhatUserConfig = { * @dev Testnets */ sepolia: { - url: `${process.env.SEPOLIA_RPC_URL}`, - accounts: [`${process.env.DEPLOYER_PRIVATE_KEY}`], - }, - goerli: { url: `${process.env.SEPOLIA_RPC_URL}`, accounts: [`${DEPLOYER_PRIVATE_KEY}`], }, @@ -54,10 +50,6 @@ const config: HardhatUserConfig = { url: `${process.env.BNB_RPC_URL}`, accounts: [`${DEPLOYER_PRIVATE_KEY}`], }, - fantom: { - url: `${process.env.FTM_RPC_URL}`, - accounts: [`${DEPLOYER_PRIVATE_KEY}`], - }, }, defaultNetwork: "hardhat", docgen: { From 2b7add887b9fd64c3dc6946ef7850f1fee4e3360 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Tue, 20 Feb 2024 23:41:33 +0700 Subject: [PATCH 05/91] feat: hardhat fork and subgraph pre upgrades --- hardhat.config.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index ac4911a..fab2e4d 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -7,7 +7,15 @@ dotenv.config(); const DEPLOYER_PRIVATE_KEY = process.env.DEPLOYER_PRIVATE_KEY; const config: HardhatUserConfig = { - solidity: "0.8.17", + solidity: { + version: "0.8.17", + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, etherscan: { apiKey: `${process.env.ETHERSCAN_API_KEY}`, }, @@ -50,6 +58,13 @@ const config: HardhatUserConfig = { url: `${process.env.BNB_RPC_URL}`, accounts: [`${DEPLOYER_PRIVATE_KEY}`], }, + hardhat: { + chainId: 31337, + forking: { + url: `${process.env.SEPOLIA_RPC_URL}`, + blockNumber: 5328000, + }, + }, }, defaultNetwork: "hardhat", docgen: { @@ -59,6 +74,7 @@ const config: HardhatUserConfig = { gasReporter: { enabled: true, }, + allowUnlimitedContractSize: true, }; export default config; From e94605d08e18a7dc061f871c44a75cd5a775c39c Mon Sep 17 00:00:00 2001 From: 0xneves Date: Tue, 20 Feb 2024 23:47:29 +0700 Subject: [PATCH 06/91] refactor: removed avax and bnb nets --- hardhat.config.ts | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index fab2e4d..f567db1 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -31,14 +31,6 @@ const config: HardhatUserConfig = { 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 */ @@ -50,14 +42,9 @@ const config: HardhatUserConfig = { 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}`], - }, + /** + * @dev Localnet (Hardhat) + */ hardhat: { chainId: 31337, forking: { From 2e7b5063f2ad430a8bef1179dee748932a83d565 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Tue, 20 Feb 2024 23:50:02 +0700 Subject: [PATCH 07/91] refactor: removed fork to avoid usage in workflow --- hardhat.config.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index f567db1..0a5374f 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -47,10 +47,6 @@ const config: HardhatUserConfig = { */ hardhat: { chainId: 31337, - forking: { - url: `${process.env.SEPOLIA_RPC_URL}`, - blockNumber: 5328000, - }, }, }, defaultNetwork: "hardhat", From 368a39300b4d23f4e6797f1fcb315981db70b7d9 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 21 Feb 2024 00:01:37 +0700 Subject: [PATCH 08/91] refactor: using hardcoded key as temporary solution --- hardhat.config.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index 0a5374f..513be85 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -4,7 +4,10 @@ import "solidity-docgen"; import dotenv from "dotenv"; dotenv.config(); -const DEPLOYER_PRIVATE_KEY = process.env.DEPLOYER_PRIVATE_KEY; +// Using a hardcoded solution to avoid GitHub actions issues +const DEPLOYER_PRIVATE_KEY = + process.env.DEPLOYER_PRIVATE_KEY || + "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; const config: HardhatUserConfig = { solidity: { From 927905eb6fe6d7b744a5fad54db103f9e7336381 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Sat, 9 Mar 2024 06:22:20 +0700 Subject: [PATCH 09/91] feat: adding a save method for last contract deployed - it will sabe directly in the .env --- test/utils/utils.ts | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/utils/utils.ts b/test/utils/utils.ts index 679ccf8..835be32 100644 --- a/test/utils/utils.ts +++ b/test/utils/utils.ts @@ -1,4 +1,6 @@ import { ethers } from "hardhat"; +import fs from "fs"; +import path from "path"; /** * @dev Get the current `block.timestamp` in seconds from the current @@ -30,7 +32,35 @@ export async function deploy(contractName: any, signer: any) { return Contract; } +export function storeAddress(contractAddress: string, envVarName: string) { + const filePath = path.join(__dirname, "../../.env"); + + fs.readFile(filePath, "utf8", (readErr: any, data: string) => { + if (readErr) { + throw new Error("Error reading .env file:"); + } + + const updatedContent = data.replace( + new RegExp(`${envVarName}=.*`), + `${envVarName}=${contractAddress}`, + ); + + fs.writeFile(filePath, updatedContent, "utf8", (writeErr: any) => { + if (writeErr) { + console.error("Error writing to .env file:", writeErr); + } else { + console.log( + "Stored contract address %s in .env file under the %s variable name", + contractAddress, + envVarName, + ); + } + }); + }); +} + module.exports = { blocktimestamp, + storeAddress, deploy, }; From e33c57214c035d055964a30484d583253c1154e7 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Sat, 9 Mar 2024 06:24:22 +0700 Subject: [PATCH 10/91] refactor: env sample with better docs and with last contract deployed --- .env.sample | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/.env.sample b/.env.sample index 69a4b0d..2841421 100644 --- a/.env.sample +++ b/.env.sample @@ -1,22 +1,28 @@ -# Swaplace deployed contracts addresses -SWAPLACE_ADDRESS=0xD8E3580C1b6f117c5b35DdD01dd9e50d9487501D +# Swaplace last deployed contracts addresses. +SWAPLACE_ADDRESS=0x000000000000000000000000000000000000000000 +# Mock last deployed contracts addresses. +MOCK_ADDRESS=0x000000000000000000000000000000000000000000 # These are public known private keys and are here as an example. -# Funds in these accounts are at risk of being stolen. +# You should change the private keys to your own private keys. +# WARNING: Blockful advises to not, ever, use your wallet helding funds as the private keys in any kind of development. +# WARNING: funds in these account are at risk of being stolen. DEPLOYER_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 -# Etherscan and similar services API keys +# Etherscan and similar services API keys used to verify contract addresses in block explorers. ETHERSCAN_API_KEY=YourApiKeyToken -# EVM compatible chains and their RPC URLs -# TESTNET +# EVM compatible chains and their public RPC's +# They are not implemented in the code, but feel free to use and explore them. + +# TESTNETS GOERLI_RPC_URL=https://ethereum-goerli.publicnode.com SEPOLIA_RPC_URL=https://ethereum-sepolia.publicnode.com MUMBAI_RPC_URL=https://polygon-mumbai-bor.publicnode.com FUJI_RPC_URL=https://avalanche-fuji-c-chain.publicnode.com BNB_TESTNET_RPC_URL=https://bsc-testnet.publicnode.com -# MAINNET +# MAINNETS ETH_RPC_URL="https://eth.llamarpc.com" BNB_RPC_URL=https://binance.llamarpc.com AVAX_RPC_URL=https://avalanche-mainnet.infura.io From e39010c614978b1595125d89113b6b61fe658003 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Sat, 9 Mar 2024 06:26:41 +0700 Subject: [PATCH 11/91] refactor: better docs, more informative and saving deployed address --- scripts/deploy.ts | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/scripts/deploy.ts b/scripts/deploy.ts index 9108fb9..0ae1ac3 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -1,20 +1,38 @@ import { ethers } from "hardhat"; -import { deploy } from "../test/utils/utils"; +import { Contract } from "ethers"; +import { deploy, storeAddress } from "../test/utils/utils"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; async function main() { - // Get the first account from the list of accounts - const [signer] = await ethers.getSigners(); + /// @dev This is the list of accounts that were set in the hardhat config file. + /// The first account will be performing the signing of the transactions, hence becoming the contract deployer. + let signers: SignerWithAddress[]; - // Deploy in the currrent network and return the Swaplace instance - const Swaplace = await deploy("Swaplace", signer); + /// @dev The returned contract instance that will be deployed via the deploy function in utils. + let Swaplace: Contract; - // Log Contract address, the Tx then return the Contract instance + /// @dev will throw an error if any of the accounts was not set up correctly. + try { + signers = await ethers.getSigners(); + } catch (error) { + throw new Error( + "Error getting the first account from the list of accounts. Make sure it is set up in correctly in hardhat.config.ts.", + ); + } + + // @dev Deploy in the currrent network in which the script was called and return the Swaplace instance. + Swaplace = await deploy("Swaplace", signers[0]); + + // @dev Log Contract address and the Tx hash which can be searched on Etherscan (or any other block explorer). console.log( "\nContract %s \nDeployed to %s \nAt Tx %s", "Swaplace", Swaplace.address, Swaplace.deployTransaction.hash, ); + + // @dev Store the contract address in the .env file. + storeAddress(Swaplace.address, "SWAPLACE_ADDRESS"); } main().catch((error) => { From 3027310a5046f62207fb6efd96c13b49e85dc42f Mon Sep 17 00:00:00 2001 From: 0xneves Date: Sat, 9 Mar 2024 06:35:25 +0700 Subject: [PATCH 12/91] docs: even better documentation of the code --- test/utils/utils.ts | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/test/utils/utils.ts b/test/utils/utils.ts index 835be32..e3ba5f9 100644 --- a/test/utils/utils.ts +++ b/test/utils/utils.ts @@ -1,6 +1,7 @@ import { ethers } from "hardhat"; import fs from "fs"; import path from "path"; +import { Contract, ContractFactory } from "ethers"; /** * @dev Get the current `block.timestamp` in seconds from the current @@ -20,13 +21,34 @@ 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); + // @dev Setting variables to be used in the function. + let ContractFactory: ContractFactory; + let Contract: Contract; - // Deploy the Contract - const Contract = await ContractFactory.deploy(); + // @dev Get Contract Factory for contractName and reverts if not found. + try { + ContractFactory = await ethers.getContractFactory(contractName, signer); + } catch (error) { + throw new Error( + `Error getting the Contract Factory for ${contractName}. + Make sure the contract is compiled, the type-chain generated + and a valid Ethereum Address for signer set in hardhat.config.ts.`, + ); + } + + // @dev Deploy the Contract and reverts if the transaction fails. + try { + Contract = await ContractFactory.deploy(); + } catch (error) { + throw new Error( + `Error deploying the Contract ${contractName}. + Make sure the network is correct, that you have a valid Ethereum Address + for signer with enough funds for the transaction. The gas settings might + as well be lower than the amount required by the network at the moment.`, + ); + } - // Wait for Contract to be deployed + // @dev Wait for the deployment transaction to be mined in the blockchain. await Contract.deployed(); return Contract; From 5f1e797b9b24408d4270816cb6c7f363984a8c02 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Sat, 9 Mar 2024 08:55:09 +0700 Subject: [PATCH 13/91] feat: better docs for mocks and removed export behavior --- scripts/deployMock.ts | 50 +++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/scripts/deployMock.ts b/scripts/deployMock.ts index 2dfdcf3..09db613 100644 --- a/scripts/deployMock.ts +++ b/scripts/deployMock.ts @@ -1,34 +1,52 @@ import { ethers } from "hardhat"; -import { deploy } from "../test/utils/utils"; +import { Contract } from "ethers"; +import { deploy, storeAddress } from "../test/utils/utils"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; -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); +async function main() { + /// @dev This is the list of accounts that were set in the hardhat config file. + /// The first account will be performing the signing of the transactions, hence becoming the contract deployer. + let signers: SignerWithAddress[]; - // Log Contract address, the Tx then return the Contract instance of MockERC20 + /// @dev The returned contract instance that will be deployed via the deploy function in utils. + let MockERC20: Contract; + let MockERC721: Contract; + + /// @dev will throw an error if any of the accounts was not set up correctly. + try { + signers = await ethers.getSigners(); + } catch (error) { + throw new Error( + "Error getting the first account from the list of accounts. Make sure it is set up in correctly in hardhat.config.ts.", + ); + } + + // @dev Deploy in the currrent network in which the script was called and return the Swaplace instance. + // We are deploying both contracts to test the user flux with the entire functionality. + MockERC20 = await deploy("MockERC20", signers[0]); + MockERC721 = await deploy("MockERC721", signers[0]); + + // @dev Log Contract address and the Tx hash which can be searched on Etherscan (or any other block explorer). console.log( - "\nDEPLOY:\nContract %s \nDeployed to %s \nAt Tx %s", + "\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", + "\nContract %s \nDeployed to %s \nAt Tx %s\n", "MockERC721", MockERC721.address, MockERC721.deployTransaction.hash, ); - // Return the transaction response - return { MockERC20, MockERC721 }; + // @dev Store the contract addresses in the .env file. + storeAddress(MockERC20.address, "ERC20_ADDRESS"); + storeAddress(MockERC721.address, "ERC721_ADDRESS"); } -ethers.getSigners().then((signers) => { - deployMock(signers[0]).catch((error) => { - console.error(error); - process.exitCode = 1; - }); +main().catch((error) => { + console.error(error); + process.exitCode = 1; }); From 335999c224ad7a17f8e989076629b63964ab6e49 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Sat, 9 Mar 2024 08:55:24 +0700 Subject: [PATCH 14/91] docs: store addr func is commented --- test/utils/utils.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test/utils/utils.ts b/test/utils/utils.ts index e3ba5f9..ccd33fd 100644 --- a/test/utils/utils.ts +++ b/test/utils/utils.ts @@ -55,18 +55,21 @@ export async function deploy(contractName: any, signer: any) { } export function storeAddress(contractAddress: string, envVarName: string) { - const filePath = path.join(__dirname, "../../.env"); + const filePath = path.join(__dirname, "../../.env"); // .env file path + // @dev Read the .env file fs.readFile(filePath, "utf8", (readErr: any, data: string) => { if (readErr) { throw new Error("Error reading .env file:"); } + // @dev Replace the contract address in the .env file const updatedContent = data.replace( new RegExp(`${envVarName}=.*`), `${envVarName}=${contractAddress}`, ); + // @dev Write the updated content to the .env file fs.writeFile(filePath, updatedContent, "utf8", (writeErr: any) => { if (writeErr) { console.error("Error writing to .env file:", writeErr); From 3a4e8ab04219946d10cb3e915e777980f26ac316 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Sat, 9 Mar 2024 08:57:01 +0700 Subject: [PATCH 15/91] fix: env wasn't storing both types of ERC tokens --- .env.sample | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.env.sample b/.env.sample index 2841421..8eb0e0a 100644 --- a/.env.sample +++ b/.env.sample @@ -1,7 +1,8 @@ -# Swaplace last deployed contracts addresses. +# Swaplace last deployed contracts address. SWAPLACE_ADDRESS=0x000000000000000000000000000000000000000000 -# Mock last deployed contracts addresses. -MOCK_ADDRESS=0x000000000000000000000000000000000000000000 +# Mocks last deployed contracts addresses. +ERC20_ADDRESS=0x000000000000000000000000000000000000000000 +ERC721_ADDRESS=0x000000000000000000000000000000000000000000 # These are public known private keys and are here as an example. # You should change the private keys to your own private keys. From 23f5a417c29227f53e7d0c5dbc2ca125ee8d2396 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Sat, 9 Mar 2024 08:57:45 +0700 Subject: [PATCH 16/91] feat: makefile for both scripts --- Makefile | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..406ef7b --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +## Running compile first to generate the type-chain types +compile: + yarn compile + +## Deploying Swaplace contract to the desired network +## Please specify which network following the available ones in hardhat.config.ts +network=hardhat +deploy-swaplace: + make compile + yarn deploy:swaplace ${network} + +## The mocks are essential to run the tests precisely, we are swapping nonetherless +deploy-mocks: + make compile + yarn deploy:mocks ${network} + + From 1f99588340f93c99411ace14b60e4b6acd836a81 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 14 Mar 2024 18:07:32 +0700 Subject: [PATCH 17/91] feat: amount, tokenID, swapID saved on .env for global use --- .env.sample | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.env.sample b/.env.sample index 8eb0e0a..7d6c4a9 100644 --- a/.env.sample +++ b/.env.sample @@ -3,6 +3,12 @@ SWAPLACE_ADDRESS=0x000000000000000000000000000000000000000000 # Mocks last deployed contracts addresses. ERC20_ADDRESS=0x000000000000000000000000000000000000000000 ERC721_ADDRESS=0x000000000000000000000000000000000000000000 +# Amount of ERC20 tokens to be minted for signer address. +AMOUNT=1000 +# Token ID of ERC721 to be minted for signer address. +TOKEN_ID=1 +# The swap to be accepted by the acceptee. +SWAP_ID=3 # These are public known private keys and are here as an example. # You should change the private keys to your own private keys. From 06226f0d205316c88bd2ea7ab642cf1bf21aa142 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 14 Mar 2024 18:08:14 +0700 Subject: [PATCH 18/91] refactor: changed mintTo to just mint --- contracts/echidna/TestSwaplace.sol | 2 +- contracts/mock/MockERC20.sol | 2 +- contracts/mock/MockERC721.sol | 2 +- docs/solidity-docgen/mock/MockERC20.md | 5 ++--- docs/solidity-docgen/mock/MockERC721.md | 5 ++--- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/contracts/echidna/TestSwaplace.sol b/contracts/echidna/TestSwaplace.sol index 38d398f..d68c7f1 100644 --- a/contracts/echidna/TestSwaplace.sol +++ b/contracts/echidna/TestSwaplace.sol @@ -12,7 +12,7 @@ contract TestSwaplace is TestFactory { constructor() { _token = new MockERC20(); _swaplace = new Swaplace(); - _token.mintTo(address(this), 100); + _token.mint(address(this), 100); } function echidna_create_swap() public returns (bool) { diff --git a/contracts/mock/MockERC20.sol b/contracts/mock/MockERC20.sol index 149f73e..af45962 100644 --- a/contracts/mock/MockERC20.sol +++ b/contracts/mock/MockERC20.sol @@ -6,7 +6,7 @@ import {ERC20} from "./ERC20/ERC20.sol"; contract MockERC20 is ERC20 { constructor() ERC20("MockERC20", "ERC20") {} - function mintTo(address to, uint256 amount) public { + function mint(address to, uint256 amount) public { _mint(to, amount); } } diff --git a/contracts/mock/MockERC721.sol b/contracts/mock/MockERC721.sol index eee2835..e763dc5 100644 --- a/contracts/mock/MockERC721.sol +++ b/contracts/mock/MockERC721.sol @@ -8,7 +8,7 @@ contract MockERC721 is ERC721 { constructor() ERC721("MockERC721", "ERC721") {} - function mintTo(address to, uint256 id) public { + function mint(address to, uint256 id) public { totalSupply++; _mint(to, id); } diff --git a/docs/solidity-docgen/mock/MockERC20.md b/docs/solidity-docgen/mock/MockERC20.md index ee80b9c..3a8e0f6 100644 --- a/docs/solidity-docgen/mock/MockERC20.md +++ b/docs/solidity-docgen/mock/MockERC20.md @@ -8,9 +8,8 @@ constructor() public ``` -### mintTo +### mint ```solidity -function mintTo(address to, uint256 amount) public +function mint(address to, uint256 amount) public ``` - diff --git a/docs/solidity-docgen/mock/MockERC721.md b/docs/solidity-docgen/mock/MockERC721.md index ad0f209..3e7f2de 100644 --- a/docs/solidity-docgen/mock/MockERC721.md +++ b/docs/solidity-docgen/mock/MockERC721.md @@ -14,9 +14,8 @@ uint256 totalSupply constructor() public ``` -### mintTo +### mint ```solidity -function mintTo(address to, uint256 id) public +function mint(address to, uint256 id) public ``` - From d8593bd92528a007bb37cd236d7f8a576ebe2d68 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 14 Mar 2024 18:18:09 +0700 Subject: [PATCH 19/91] refactor: readfile is now async - was causing errors where the instance was not saving the replacements correctly after the script finished --- test/utils/utils.ts | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/test/utils/utils.ts b/test/utils/utils.ts index ccd33fd..486ad40 100644 --- a/test/utils/utils.ts +++ b/test/utils/utils.ts @@ -54,14 +54,16 @@ export async function deploy(contractName: any, signer: any) { return Contract; } -export function storeAddress(contractAddress: string, envVarName: string) { +export async function storeEnv( + contractAddress: any, + envVarName: string, + showLog: boolean, +) { const filePath = path.join(__dirname, "../../.env"); // .env file path - // @dev Read the .env file - fs.readFile(filePath, "utf8", (readErr: any, data: string) => { - if (readErr) { - throw new Error("Error reading .env file:"); - } + try { + /// @dev Read the file synchronously + let data = fs.readFileSync(filePath, "utf8"); // @dev Replace the contract address in the .env file const updatedContent = data.replace( @@ -69,23 +71,23 @@ export function storeAddress(contractAddress: string, envVarName: string) { `${envVarName}=${contractAddress}`, ); - // @dev Write the updated content to the .env file - fs.writeFile(filePath, updatedContent, "utf8", (writeErr: any) => { - if (writeErr) { - console.error("Error writing to .env file:", writeErr); - } else { - console.log( - "Stored contract address %s in .env file under the %s variable name", - contractAddress, - envVarName, - ); - } - }); - }); + /// @dev Write the updated content to the file synchronously + fs.writeFileSync(filePath, updatedContent, "utf8"); + + if (showLog) { + console.log( + "Stored the data %s in .env file at the %s variable", + contractAddress, + envVarName, + ); + } + } catch (err) { + console.error("Error reading or writing file:", err); + } } module.exports = { blocktimestamp, - storeAddress, + storeEnv, deploy, }; From c3f4356ae8caca38076934df8073378031147879 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 14 Mar 2024 18:18:34 +0700 Subject: [PATCH 20/91] refacotr: mintTo is now mint - following industry standards --- test/TestMockContracts.test.ts | 4 ++-- test/TestSwaplace.test.ts | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/test/TestMockContracts.test.ts b/test/TestMockContracts.test.ts index 5db1bf7..545b125 100644 --- a/test/TestMockContracts.test.ts +++ b/test/TestMockContracts.test.ts @@ -22,11 +22,11 @@ describe("Swaplace", async function () { it("Should test the {mint} function", async function () { // Testing the mint of ERC20 - await MockERC20.mintTo(owner.address, 1000); + await MockERC20.mint(owner.address, 1000); expect(await MockERC20.balanceOf(owner.address)).to.be.equals(1000); // Testing the mint of ERC721 - await MockERC721.mintTo(owner.address, 1); + await MockERC721.mint(owner.address, 1); expect(await MockERC721.balanceOf(owner.address)).to.be.equals(1); }); diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index 68c5b2a..76bcdc0 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -241,8 +241,8 @@ describe("Swaplace", async function () { MockERC20 = await deploy("MockERC20", deployer); MockERC721 = await deploy("MockERC721", deployer); - await MockERC721.mintTo(owner.address, 1); - await MockERC20.mintTo(allowed.address, 1000); + await MockERC721.mint(owner.address, 1); + await MockERC20.mint(allowed.address, 1000); await MockERC721.connect(owner).approve(Swaplace.address, 1); await MockERC20.connect(allowed).approve(Swaplace.address, 1000); @@ -284,8 +284,8 @@ describe("Swaplace", async function () { }); it("Should be able to {acceptSwap} as N-N Swap", async function () { - await MockERC20.mintTo(owner.address, 500); - await MockERC721.mintTo(allowed.address, 5); + await MockERC20.mint(owner.address, 500); + await MockERC721.mint(allowed.address, 5); await MockERC20.connect(owner).approve(Swaplace.address, 500); await MockERC721.connect(allowed).approve(Swaplace.address, 5); @@ -321,8 +321,8 @@ describe("Swaplace", async function () { }); it("Should be able to {acceptSwap} as P2P Swap", async function () { - await MockERC20.mintTo(owner.address, 1000); - await MockERC721.mintTo(allowed.address, 10); + await MockERC20.mint(owner.address, 1000); + await MockERC721.mint(allowed.address, 10); await MockERC20.connect(owner).approve(Swaplace.address, 1000); await MockERC721.connect(allowed).approve(Swaplace.address, 10); @@ -413,8 +413,8 @@ describe("Swaplace", async function () { }); it("Should revert when {acceptSwap} as not allowed to P2P Swap", async function () { - await MockERC20.mintTo(owner.address, 1000); - await MockERC721.mintTo(allowed.address, 10); + await MockERC20.mint(owner.address, 1000); + await MockERC721.mint(allowed.address, 10); await MockERC20.connect(owner).approve(Swaplace.address, 1000); await MockERC721.connect(allowed).approve(Swaplace.address, 10); @@ -502,8 +502,8 @@ describe("Swaplace", async function () { MockERC20 = await deploy("MockERC20", deployer); MockERC721 = await deploy("MockERC721", deployer); - await MockERC721.mintTo(owner.address, 1); - await MockERC20.mintTo(allowed.address, 1000); + await MockERC721.mint(owner.address, 1); + await MockERC20.mint(allowed.address, 1000); const bidingAddr = [MockERC721.address]; const bidingAmountOrId = [1]; From 0ca1733eb595e41e105b29a14ce9149e8ae59571 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 14 Mar 2024 18:19:05 +0700 Subject: [PATCH 21/91] refactor: new mint script with typings --- scripts/mint.ts | 118 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 98 insertions(+), 20 deletions(-) diff --git a/scripts/mint.ts b/scripts/mint.ts index 8882999..6362a39 100644 --- a/scripts/mint.ts +++ b/scripts/mint.ts @@ -1,25 +1,103 @@ -import { ethers } from "ethers"; +import { ethers } from "hardhat"; +import { Contract } from "ethers"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import dotenv from "dotenv"; +import { storeEnv } from "../test/utils/utils"; +dotenv.config(); -export async function mint( - contract: ethers.Contract, - amountOrId: bigint, - receiver: string, -) { +/// @notice This function will mint ERC20 or ERC721 tokens to the signer address. +/// The signer address is the first account in the list of accounts that were set in the hardhat config file. +async function main() { + /// @dev This is the list of mock deployments addresses that were stored in the `.env` file. + const ERC20_ADDRESS = process.env.ERC20_ADDRESS || 0x0; + const ERC721_ADDRESS = process.env.ERC721_ADDRESS || 0x0; + /// @dev Will throw an error if any of the addresses were not set in the `.env` file. + if (!ERC20_ADDRESS || !ERC721_ADDRESS) { + throw new Error( + "Invalid ERC20 or ERC721 address, please check if the addresse in the `.env` file is set up correctly.", + ); + } + + /// @dev This is the list of accounts that were set in the hardhat config file. + /// The first account will be performing the signing of the transactions, hence becoming the contract deployer. + let signers: SignerWithAddress[]; + + /// @dev The returned contract instance that will be deployed via the deploy function in utils. + let MockERC20: Contract; + let MockERC721: Contract; + + /// @dev will throw an error if any of the accounts was not set up correctly. + try { + signers = await ethers.getSigners(); + } catch (error) { + throw new Error( + `Error getting the first account from the list of accounts. Make sure it is + set up in correctly in hardhat.config.ts. + ${error}`, + ); + } + + /// @dev Will throw an error if we fail to load the contract instances. + try { + MockERC20 = await ethers.getContractAt( + "MockERC20", + ERC20_ADDRESS, + signers[0], + ); + MockERC721 = await ethers.getContractAt( + "MockERC721", + ERC721_ADDRESS, + signers[0], + ); + } catch (error) { + throw new Error( + `Error deploying one of the Mock Contracts. + Make sure if the network is correct and that the contract has the right + deployment address. Ultimately check for errors in the ABI by calling the + script 'npx hardhat clean' and then 'npx hardhat compile'. + ${error}`, + ); + } + + /// @dev Quantity of tokens to mint. + let amount = process.env.AMOUNT || 1000; + + /// @dev We fetch last token id to avoid minting the same token. + let tokenId = process.env.TOKEN_ID || 1; + tokenId = Number(tokenId) + 1; // Increment the token id by 1. + + /// @dev Responses from the minting transactions. + let txErc20; + let txErc721; + + /// @dev Minting function will throw an error if the minting fails. + /// We are minting for the first signer of `hardhat.config.ts` 1000 + /// tokens of ERC20 and the last token id for ERC721. + /// We start the mint from the last token id + 1 because it starts from 0. 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"); - } + txErc20 = await MockERC20.mint(signers[0].address, amount); + txErc721 = await MockERC721.mint(signers[0].address, tokenId); } catch (error) { - console.error(error); - process.exitCode = 1; + throw new Error( + `Error while minting tokens. Make sure that the minting function is + correctly implemented in the contract and that the signer address is + correctly set up in the hardhat.config.ts file. + ${error}`, + ); } + + /// @dev Log the transactions + console.log("\nERC20 Minted %s tokens \nAt Tx %s", amount, txErc20.hash); + console.log( + "\nERC721 Minted token ID #%s \nAt Tx %s", + tokenId, + txErc721.hash, + ); + + await storeEnv(tokenId, "TOKEN_ID", false); } + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); From d927f44ec46e18eeae9387773ec65a704bf785ea Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 14 Mar 2024 18:19:20 +0700 Subject: [PATCH 22/91] refactor: new approve script with typings --- scripts/approve.ts | 120 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 100 insertions(+), 20 deletions(-) diff --git a/scripts/approve.ts b/scripts/approve.ts index 4870c8d..30f5e06 100755 --- a/scripts/approve.ts +++ b/scripts/approve.ts @@ -1,25 +1,105 @@ -import { ethers } from "ethers"; +import { ethers } from "hardhat"; +import { Contract } from "ethers"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import dotenv from "dotenv"; +dotenv.config(); -export async function approve( - contract: ethers.Contract, - spender: string, - amountOrId: bigint, -) { +/// @notice This function will mint ERC20 or ERC721 tokens to the signer address. +/// The signer address is the first account in the list of accounts that were set in the hardhat config file. +async function main() { + /// @dev This is the list of mock deployments addresses that were stored in the `.env` file. + const ERC20_ADDRESS = process.env.ERC20_ADDRESS || 0x0; + const ERC721_ADDRESS = process.env.ERC721_ADDRESS || 0x0; + /// @dev The Swaplace address also needs to be instance to receive the approvals. + const SWAPLACE_ADDRESS = process.env.SWAPLACE_ADDRESS || 0x0; + /// @dev Will throw an error if any of the addresses were not set in the `.env` file. + if (!ERC20_ADDRESS || !ERC721_ADDRESS || !SWAPLACE_ADDRESS) { + throw new Error( + "Invalid ERC20, ERC721 or Swaplace address, please check if the addresse in the `.env` file is set up correctly.", + ); + } + + /// @dev Quantity of tokens to approve. + let amount = process.env.AMOUNT || 0x0; + + /// @dev Last token ID or choosed token ID to mint. + let tokenId = process.env.TOKEN_ID || 0x0; + + /// @dev No point in approving if the quantities and target token IDs are not set. + if (!tokenId || !amount) { + throw new Error( + "Invalid token ID or amount, please check if the values are set up correctly in the `.env` file.", + ); + } + + /// @dev This is the list of accounts that were set in the hardhat config file. + /// The first account will be performing the signing of the transactions, hence becoming the contract deployer. + let signers: SignerWithAddress[]; + + /// @dev The returned contract instance that will be deployed via the deploy function in utils. + let MockERC20: Contract; + let MockERC721: Contract; + + /// @dev will throw an error if any of the accounts was not set up correctly. 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"); - } + signers = await ethers.getSigners(); } catch (error) { - console.error(error); - process.exitCode = 1; + throw new Error( + `Error getting the first account from the list of accounts. Make sure it is + set up in correctly in hardhat.config.ts. + ${error}`, + ); } + + /// @dev Will throw an error if we fail to load the contract's instance. + try { + MockERC20 = await ethers.getContractAt( + "MockERC20", + ERC20_ADDRESS, + signers[0], + ); + MockERC721 = await ethers.getContractAt( + "MockERC721", + ERC721_ADDRESS, + signers[0], + ); + } catch (error) { + throw new Error( + `Error deploying one of the Mock Contracts. + Make sure if the network is correct and that the contract has the right + deployment address. Ultimately check for errors in the ABI by calling the + script 'npx hardhat clean' and then 'npx hardhat compile'. + ${error}`, + ); + } + + /// @dev Responses from the minting transactions. + let txErc20; + let txErc721; + + /// @dev We are approving the signer address to spend the amount of tokens. + try { + txErc20 = await MockERC20.approve(SWAPLACE_ADDRESS, amount); + txErc721 = await MockERC721.approve(SWAPLACE_ADDRESS, tokenId); + } catch (error) { + throw new Error( + `Error while approving the tokens. Make sure that the approve function is + correctly implemented in the contract and that the signer address is + correctly set up in the hardhat.config.ts file. + ${error}`, + ); + } + + /// @dev Log the transactions + console.log("\nERC20 Approved %s tokens \nAt Tx %s", amount, txErc20.hash); + console.log( + "\nERC721 Approved token ID #%s \nAt Tx %s", + tokenId, + txErc721.hash, + ); } + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); From 1c3881f4cd1d80ff4746a4a730b82478fce2d3e7 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 14 Mar 2024 18:20:05 +0700 Subject: [PATCH 23/91] feat: save on .env has a boolean for log emission --- scripts/deploy.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/deploy.ts b/scripts/deploy.ts index 0ae1ac3..78aae49 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -1,6 +1,6 @@ import { ethers } from "hardhat"; import { Contract } from "ethers"; -import { deploy, storeAddress } from "../test/utils/utils"; +import { deploy, storeEnv } from "../test/utils/utils"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; async function main() { @@ -16,7 +16,9 @@ async function main() { signers = await ethers.getSigners(); } catch (error) { throw new Error( - "Error getting the first account from the list of accounts. Make sure it is set up in correctly in hardhat.config.ts.", + `Error getting the first account from the list of accounts. Make sure it is + set up in correctly in hardhat.config.ts. + ${error}`, ); } @@ -32,7 +34,7 @@ async function main() { ); // @dev Store the contract address in the .env file. - storeAddress(Swaplace.address, "SWAPLACE_ADDRESS"); + await storeEnv(Swaplace.address, "SWAPLACE_ADDRESS", true); } main().catch((error) => { From 90740c41625e5a09e89beb75f7a72eed5a3839fd Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 14 Mar 2024 18:20:29 +0700 Subject: [PATCH 24/91] feat: new scripts where added --- package.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 60b3a11..b904e4b 100644 --- a/package.json +++ b/package.json @@ -28,8 +28,13 @@ "scripts": { "clean": "npx hardhat clean", "compile": "npx hardhat compile", - "test": "npx hardhat test", "docs": "npx hardhat docgen", + "test": "npx hardhat test", + "mint": "npx hardhat run scripts/mint.ts --network $1", + "create-swap": "npx hardhat run scripts/createSwap.ts --network $1", + "accept-swap": "npx hardhat run scripts/acceptSwap.ts --network $1", + "cancel-swap": "npx hardhat run scripts/cancelSwap.ts --network $1", + "approve": "npx hardhat run scripts/approve.ts --network $1", "deploy:mocks": "npx hardhat run scripts/deployMock.ts --network $1", "deploy:swaplace": "npx hardhat run scripts/deploy.ts --network $1", "compile-echidna": "crytic-compile . && slither . --print echidna", From e540410f59e1c897319e884ec1adab31283849e3 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 14 Mar 2024 18:20:45 +0700 Subject: [PATCH 25/91] feat: using makefile to run the test suit --- Makefile | 51 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/Makefile b/Makefile index 406ef7b..5596cd1 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,50 @@ -## Running compile first to generate the type-chain types +## Running compile to generate the abi's and the type-chain types. compile: yarn compile -## Deploying Swaplace contract to the desired network -## Please specify which network following the available ones in hardhat.config.ts -network=hardhat -deploy-swaplace: - make compile +## Please specify which network following the available ones in hardhat.config.ts. +network=localhost + +## Deploying Swaplace contract to the desired network. +swaplace: yarn deploy:swaplace ${network} -## The mocks are essential to run the tests precisely, we are swapping nonetherless -deploy-mocks: - make compile +## The mocks are essential to run the tests. We will be swapping token assets. +mocks: yarn deploy:mocks ${network} +## Mock deployments are a requirement to run the mint script, which will fund the signer's wallet. +mint: + yarn mint ${network} + +## Mock deployments are a requirement to run the mint script, which will fund the signer's wallet. +approve: + yarn approve ${network} + +## Create a swap on the swaplace contract and save the swap id in the .env file. +swap: + yarn create-swap ${network} +## Accept a swap on the swaplace contract based on the swap id in the .env file. +accept: + yarn accept-swap ${network} + +## Cancel a swap on the swaplace contract based on the swap id in the .env file. +cancel: + make compile + yarn cancel-swap ${network} + +## Run the entire test suite. +## Running a local node by using `npx hardhat node` in a separated terminal is +## a requirement to run the tests in the localhost network. +test-suite-runner: + yarn clean + yarn compile + make swaplace + make mocks + make mint + make approve + make swap + make accept + make swap + make cancel \ No newline at end of file From 7d4f46abcb2aa1f59e249cfca38f3f48f682e54a Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 14 Mar 2024 18:21:08 +0700 Subject: [PATCH 26/91] refactor: new typings for acceptSwap --- scripts/acceptSwap.ts | 85 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 70 insertions(+), 15 deletions(-) diff --git a/scripts/acceptSwap.ts b/scripts/acceptSwap.ts index 798c382..2c3c215 100644 --- a/scripts/acceptSwap.ts +++ b/scripts/acceptSwap.ts @@ -1,27 +1,82 @@ import { ethers } from "hardhat"; -import abi from "../artifacts/contracts/Swaplace.sol/Swaplace.json"; +import { Contract } from "ethers"; +import { blocktimestamp } from "../test/utils/utils"; +import { Swap, composeSwap } from "../test/utils/SwapFactory"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; export async function main() { - // Get the first account from the list of accounts - const [signer, receiver] = await ethers.getSigners(); + /// @dev The Swaplace address also needs to be instance to receive the approvals. + const SWAPLACE_ADDRESS = process.env.SWAPLACE_ADDRESS || 0x0; + /// @dev Will throw an error if any of the addresses were not set in the `.env` file. + if (!SWAPLACE_ADDRESS) { + throw new Error( + "Invalid Swaplace address, please check if the addresse in the `.env` file is set up correctly.", + ); + } - // Get the Swaplace address from .env file - const swaplaceAddress: string = process.env.SWAPLACE_ADDRESS || ""; + /// @dev The swap to be approved + let swapId = process.env.SWAP_ID || 0x0; - // Get the Swaplace instance - const Swaplace = new ethers.Contract(swaplaceAddress, abi.abi, signer); + /// @dev No point in approving if the quantities and target token IDs are not set. + if (!swapId) { + throw new Error( + "Invalid swap ID, please check if the values are set up correctly in the `.env` file.", + ); + } - // Get the swap ID to be accepted - const swapId = 1; + /// @dev This is the list of accounts that were set in the hardhat config file. + /// The first account will be performing the signing of the transactions, hence becoming the contract deployer. + let signers: SignerWithAddress[]; - // Accept the swap - const tx = await Swaplace.acceptSwap(swapId, receiver.address); + /// @dev The returned contract instance that will be deployed via the deploy function in utils. + let Swaplace: Contract; - // Wait for the transaction to be mined - await tx.wait(); + /// @dev will throw an error if any of the accounts was not set up correctly. + try { + signers = await ethers.getSigners(); + } catch (error) { + throw new Error( + `Error getting the first account from the list of accounts. Make sure it is + set up in correctly in hardhat.config.ts. + ${error}`, + ); + } - // Log the transaction hash - console.log("\nTransaction Hash: ", tx); + /// @dev Will throw an error if we fail to load the contract's instance. + try { + Swaplace = await ethers.getContractAt( + "Swaplace", + SWAPLACE_ADDRESS, + signers[0], + ); + } catch (error) { + throw new Error( + `Error instancing the Swaplace contract. + Make sure if the network is correct and that the contract has the right + deployment address. Ultimately check for errors in the ABI by calling the + script 'npx hardhat clean' and then 'npx hardhat compile'. + ${error}`, + ); + } + + /// @dev Response from the `creatSwap` transaction. + let tx; + + /// @dev Catching any errors while creating the swap. + try { + tx = await Swaplace.acceptSwap(swapId, signers[0].address); + // @dev Wait for the transaction to be mined + await tx.wait(); + } catch (error) { + throw new Error( + `Error while accepting the swap. Make sure that the swap exists + and if the tokens were correctly approved before transfer. + ${error}`, + ); + } + + /// @dev Log the transactions + console.log("\nSwaplace accepted the Swap \nAt Tx %s", tx.hash); } main().catch((error) => { From b9c7d424c557426bc5090463113ae7a6bd7f29ed Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 14 Mar 2024 18:21:14 +0700 Subject: [PATCH 27/91] refactor: new typings for cancelSwap --- scripts/cancelSwap.ts | 85 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 70 insertions(+), 15 deletions(-) diff --git a/scripts/cancelSwap.ts b/scripts/cancelSwap.ts index e316b44..1b8cce0 100644 --- a/scripts/cancelSwap.ts +++ b/scripts/cancelSwap.ts @@ -1,27 +1,82 @@ import { ethers } from "hardhat"; -import abi from "../artifacts/contracts/Swaplace.sol/Swaplace.json"; +import { Contract } from "ethers"; +import { blocktimestamp } from "../test/utils/utils"; +import { Swap, composeSwap } from "../test/utils/SwapFactory"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; export async function main() { - // Get the first account from the list of accounts - const [signer] = await ethers.getSigners(); + /// @dev The Swaplace address also needs to be instance to receive the approvals. + const SWAPLACE_ADDRESS = process.env.SWAPLACE_ADDRESS || 0x0; + /// @dev Will throw an error if any of the addresses were not set in the `.env` file. + if (!SWAPLACE_ADDRESS) { + throw new Error( + "Invalid Swaplace address, please check if the addresse in the `.env` file is set up correctly.", + ); + } - // Get the Swaplace address from .env file - const swaplaceAddress: string = process.env.SWAPLACE_ADDRESS || ""; + /// @dev The swap to be approved + let swapId = process.env.SWAP_ID || 0x0; - // Get the Swaplace instance - const Swaplace = new ethers.Contract(swaplaceAddress, abi.abi, signer); + /// @dev No point in approving if the quantities and target token IDs are not set. + if (!swapId) { + throw new Error( + "Invalid swap ID, please check if the values are set up correctly in the `.env` file.", + ); + } - // Get the swap ID to be canceled - const swapId = 1; + /// @dev This is the list of accounts that were set in the hardhat config file. + /// The first account will be performing the signing of the transactions, hence becoming the contract deployer. + let signers: SignerWithAddress[]; - // Cancel the swap - const tx = await Swaplace.cancelSwap(swapId); + /// @dev The returned contract instance that will be deployed via the deploy function in utils. + let Swaplace: Contract; - // Wait for the transaction to be mined - await tx.wait(); + /// @dev will throw an error if any of the accounts was not set up correctly. + try { + signers = await ethers.getSigners(); + } catch (error) { + throw new Error( + `Error getting the first account from the list of accounts. Make sure it is + set up in correctly in hardhat.config.ts. + ${error}`, + ); + } - // Log the transaction hash - console.log("\nTransaction Hash: ", tx); + /// @dev Will throw an error if we fail to load the contract's instance. + try { + Swaplace = await ethers.getContractAt( + "Swaplace", + SWAPLACE_ADDRESS, + signers[0], + ); + } catch (error) { + throw new Error( + `Error instancing the Swaplace contract. + Make sure if the network is correct and that the contract has the right + deployment address. Ultimately check for errors in the ABI by calling the + script 'npx hardhat clean' and then 'npx hardhat compile'. + ${error}`, + ); + } + + /// @dev Response from the `creatSwap` transaction. + let tx; + + /// @dev Catching any errors while creating the swap. + try { + tx = await Swaplace.cancelSwap(swapId); + // @dev Wait for the transaction to be mined + await tx.wait(); + } catch (error) { + throw new Error( + `Error while canceling the swap. Make sure that the swap exists + and if the tokens were correctly approved before transfer. + ${error}`, + ); + } + + /// @dev Log the transactions + console.log("\nSwaplace canceled the Swap \nAt Tx %s", tx.hash); } main().catch((error) => { From 260e0547f7e1449f9231412dee26d1a289195540 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 14 Mar 2024 18:21:21 +0700 Subject: [PATCH 28/91] refactor: new typings for createSwap --- scripts/createSwap.ts | 116 +++++++++++++++++++++++++++++++++--------- 1 file changed, 91 insertions(+), 25 deletions(-) diff --git a/scripts/createSwap.ts b/scripts/createSwap.ts index c6330b4..10db06d 100644 --- a/scripts/createSwap.ts +++ b/scripts/createSwap.ts @@ -1,35 +1,87 @@ import { ethers } from "hardhat"; -import { blocktimestamp } from "../test/utils/utils"; +import { Contract } from "ethers"; +import { blocktimestamp, storeEnv } from "../test/utils/utils"; import { Swap, composeSwap } from "../test/utils/SwapFactory"; -import abi from "../artifacts/contracts/Swaplace.sol/Swaplace.json"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; export async function main() { - // Get the first account from the list of accounts - const [signer] = await ethers.getSigners(); + /// @dev This is the list of mock deployments addresses that were stored in the `.env` file. + const ERC20_ADDRESS = process.env.ERC20_ADDRESS || 0x0; + const ERC721_ADDRESS = process.env.ERC721_ADDRESS || 0x0; + /// @dev The Swaplace address also needs to be instance to receive the approvals. + const SWAPLACE_ADDRESS = process.env.SWAPLACE_ADDRESS || 0x0; + /// @dev Will throw an error if any of the addresses were not set in the `.env` file. + if (!ERC20_ADDRESS || !ERC721_ADDRESS || !SWAPLACE_ADDRESS) { + throw new Error( + "Invalid ERC20, ERC721 or Swaplace address, please check if the addresse in the `.env` file is set up correctly.", + ); + } - // Get the Swaplace address from .env file - const swaplaceAddress: string = process.env.SWAPLACE_ADDRESS || ""; + /// @dev Quantity of tokens to approve. + let amount = process.env.AMOUNT || 0x0; - // Get the Swaplace instance - const Swaplace = new ethers.Contract(swaplaceAddress, abi.abi, signer); + /// @dev Last token ID or choosed token ID to mint. + let tokenId = process.env.TOKEN_ID || 0x0; - // Fill the Swap struct - const owner = signer.address; + /// @dev No point in approving if the quantities and target token IDs are not set. + if (!tokenId || !amount) { + throw new Error( + "Invalid token ID or amount, please check if the values are set up correctly in the `.env` file.", + ); + } + + /// @dev This is the list of accounts that were set in the hardhat config file. + /// The first account will be performing the signing of the transactions, hence becoming the contract deployer. + let signers: SignerWithAddress[]; + + /// @dev The returned contract instance that will be deployed via the deploy function in utils. + let Swaplace: Contract; + + /// @dev will throw an error if any of the accounts was not set up correctly. + try { + signers = await ethers.getSigners(); + } catch (error) { + throw new Error( + `Error getting the first account from the list of accounts. Make sure it is + set up in correctly in hardhat.config.ts. + ${error}`, + ); + } + + /// @dev Will throw an error if we fail to load the contract's instance. + try { + Swaplace = await ethers.getContractAt( + "Swaplace", + SWAPLACE_ADDRESS, + signers[0], + ); + } catch (error) { + throw new Error( + `Error instancing the Swaplace contract. + Make sure if the network is correct and that the contract has the right + deployment address. Ultimately check for errors in the ABI by calling the + script 'npx hardhat clean' and then 'npx hardhat compile'. + ${error}`, + ); + } + + /// @dev Fill the Swap struct + const owner = signers[0].address; const allowed = ethers.constants.AddressZero; const expiry = (await blocktimestamp()) * 2; - // Build the biding assets - const bidingAddr = ["0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"]; // USDC - const bidingAmountOrId = [1000]; + /// @dev Build the biding assets + const bidingAddr = [ERC20_ADDRESS]; + const bidingAmountOrId = [amount]; - // Build the asking assets - const askingAddr = ["0x2260fac5e5542a773aa44fbcfedf7c193bc2c599"]; // WBTC - const askingAmountOrId = [1]; + /// @dev Build the asking assets + const askingAddr = [ERC721_ADDRESS]; + const askingAmountOrId = [tokenId]; - // Pack the config together - const config = await Swaplace.packData(allowed, expiry); + /// @dev Pack the config together + const config = (BigInt(allowed) << BigInt(96)) | BigInt(expiry); - // Compose the swap + /// @dev Compose the above swap into the Swap Struct const swap: Swap = await composeSwap( owner, config, @@ -39,14 +91,28 @@ export async function main() { askingAmountOrId, ); - // Create the swap - const tx = await Swaplace.createSwap(swap); + /// @dev Response from the `creatSwap` transaction. + let tx; + + /// @dev Catching any errors while creating the swap. + try { + tx = await Swaplace.createSwap(swap); + // @dev Wait for the transaction to be mined + await tx.wait(); + } catch (error) { + throw new Error( + `Error while creating the swap. Make sure that the struct is correctly + filled accordingly to the contract interface specification. + ${error}`, + ); + } - // Wait for the transaction to be mined - await tx.wait(); + /// @dev Log the transactions + console.log("\nSwaplace created the Swap \nAt Tx %s", tx.hash); - // Log the transaction hash - console.log("\nTransaction Hash: ", tx); + /// @dev Store the recently created swap and it's corresponding ID in the `.env` file. + const swapId = await Swaplace.totalSwaps(); + await storeEnv(swapId, "SWAP_ID", tx.hash); } main().catch((error) => { From d4dd05cd3e293bb0432cb6c7be82f53befdbe262 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 14 Mar 2024 18:21:59 +0700 Subject: [PATCH 29/91] refactor: saving on .env has a boolean for log emission --- scripts/deployMock.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/deployMock.ts b/scripts/deployMock.ts index 09db613..5798e3e 100644 --- a/scripts/deployMock.ts +++ b/scripts/deployMock.ts @@ -1,6 +1,6 @@ import { ethers } from "hardhat"; import { Contract } from "ethers"; -import { deploy, storeAddress } from "../test/utils/utils"; +import { deploy, storeEnv } from "../test/utils/utils"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; async function main() { @@ -17,7 +17,9 @@ async function main() { signers = await ethers.getSigners(); } catch (error) { throw new Error( - "Error getting the first account from the list of accounts. Make sure it is set up in correctly in hardhat.config.ts.", + `Error getting the first account from the list of accounts. Make sure it is + set up in correctly in hardhat.config.ts. + ${error}`, ); } @@ -42,8 +44,8 @@ async function main() { ); // @dev Store the contract addresses in the .env file. - storeAddress(MockERC20.address, "ERC20_ADDRESS"); - storeAddress(MockERC721.address, "ERC721_ADDRESS"); + await storeEnv(MockERC20.address, "ERC20_ADDRESS", true); + await storeEnv(MockERC721.address, "ERC721_ADDRESS", true); } main().catch((error) => { From f75e6aa08c02bfcfa7d45de32b33932cab021d30 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 14 Mar 2024 19:11:43 +0700 Subject: [PATCH 30/91] feat: transactions overload parameters for gas and nonce --- scripts/acceptSwap.ts | 9 ++++++++- scripts/approve.ts | 19 ++++++++++++++++--- scripts/cancelSwap.ts | 9 ++++++++- scripts/createSwap.ts | 9 ++++++++- scripts/mint.ts | 17 ++++++++++++++--- 5 files changed, 54 insertions(+), 9 deletions(-) diff --git a/scripts/acceptSwap.ts b/scripts/acceptSwap.ts index 2c3c215..ae4f9af 100644 --- a/scripts/acceptSwap.ts +++ b/scripts/acceptSwap.ts @@ -64,7 +64,14 @@ export async function main() { /// @dev Catching any errors while creating the swap. try { - tx = await Swaplace.acceptSwap(swapId, signers[0].address); + let lastNonce = await ethers.provider.getTransactionCount( + signers[0].address, + ); + tx = await Swaplace.acceptSwap(swapId, signers[0].address, { + nonce: Number(lastNonce) + 1, + maxFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum fee per gas + maxPriorityFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum tip + }); // @dev Wait for the transaction to be mined await tx.wait(); } catch (error) { diff --git a/scripts/approve.ts b/scripts/approve.ts index 30f5e06..5d44b18 100755 --- a/scripts/approve.ts +++ b/scripts/approve.ts @@ -79,13 +79,26 @@ async function main() { /// @dev We are approving the signer address to spend the amount of tokens. try { - txErc20 = await MockERC20.approve(SWAPLACE_ADDRESS, amount); - txErc721 = await MockERC721.approve(SWAPLACE_ADDRESS, tokenId); + let lastNonce = await ethers.provider.getTransactionCount( + signers[0].address, + ); + txErc20 = await MockERC20.approve(SWAPLACE_ADDRESS, amount, { + gasLimit: 500000, + nonce: Number(lastNonce) + 1, + maxFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum fee per gas + maxPriorityFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum tip + }); + txErc721 = await MockERC721.approve(SWAPLACE_ADDRESS, tokenId, { + gasLimit: 500000, + nonce: Number(lastNonce) + 2, + maxFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum fee per gas + maxPriorityFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum tip + }); } catch (error) { throw new Error( `Error while approving the tokens. Make sure that the approve function is correctly implemented in the contract and that the signer address is - correctly set up in the hardhat.config.ts file. + correctly set up in the hardhat.config.ts file. Gas errors are common. ${error}`, ); } diff --git a/scripts/cancelSwap.ts b/scripts/cancelSwap.ts index 1b8cce0..04e2d5f 100644 --- a/scripts/cancelSwap.ts +++ b/scripts/cancelSwap.ts @@ -64,7 +64,14 @@ export async function main() { /// @dev Catching any errors while creating the swap. try { - tx = await Swaplace.cancelSwap(swapId); + let lastNonce = await ethers.provider.getTransactionCount( + signers[0].address, + ); + tx = await Swaplace.cancelSwap(swapId, { + nonce: Number(lastNonce) + 1, + maxFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum fee per gas + maxPriorityFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum tip + }); // @dev Wait for the transaction to be mined await tx.wait(); } catch (error) { diff --git a/scripts/createSwap.ts b/scripts/createSwap.ts index 10db06d..14b2c6a 100644 --- a/scripts/createSwap.ts +++ b/scripts/createSwap.ts @@ -96,7 +96,14 @@ export async function main() { /// @dev Catching any errors while creating the swap. try { - tx = await Swaplace.createSwap(swap); + let lastNonce = await ethers.provider.getTransactionCount( + signers[0].address, + ); + tx = await Swaplace.createSwap(swap, { + nonce: Number(lastNonce) + 1, + maxFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum fee per gas + maxPriorityFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum tip + }); // @dev Wait for the transaction to be mined await tx.wait(); } catch (error) { diff --git a/scripts/mint.ts b/scripts/mint.ts index 6362a39..6c7b108 100644 --- a/scripts/mint.ts +++ b/scripts/mint.ts @@ -75,13 +75,24 @@ async function main() { /// tokens of ERC20 and the last token id for ERC721. /// We start the mint from the last token id + 1 because it starts from 0. try { - txErc20 = await MockERC20.mint(signers[0].address, amount); - txErc721 = await MockERC721.mint(signers[0].address, tokenId); + let lastNonce = await ethers.provider.getTransactionCount( + signers[0].address, + ); + txErc20 = await MockERC20.mint(signers[0].address, amount, { + nonce: Number(lastNonce) + 1, + maxFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum fee per gas + maxPriorityFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum tip + }); + txErc721 = await MockERC721.mint(signers[0].address, tokenId, { + nonce: Number(lastNonce) + 2, + maxFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum fee per gas + maxPriorityFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum tip + }); } catch (error) { throw new Error( `Error while minting tokens. Make sure that the minting function is correctly implemented in the contract and that the signer address is - correctly set up in the hardhat.config.ts file. + correctly set up in the hardhat.config.ts file. Gas errors are common. ${error}`, ); } From 4edab4fde0f3cb15c912f49bb8e988a275e96edb Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 14 Mar 2024 19:11:55 +0700 Subject: [PATCH 31/91] feat: transaction filler script --- Makefile | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 5596cd1..9091319 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ compile: yarn compile ## Please specify which network following the available ones in hardhat.config.ts. -network=localhost +network=sepolia ## Deploying Swaplace contract to the desired network. swaplace: @@ -43,6 +43,15 @@ test-suite-runner: make swaplace make mocks make mint + make approve + make swap + make accept + make swap + make cancel + +## Feed existing Swaplace contract with some transactions. +## make mint +transactions: make approve make swap make accept From d44e46835a530685165181259d71491c700a2b05 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 14 Mar 2024 20:18:13 +0700 Subject: [PATCH 32/91] refactor: removing gas and nonce configuration as it is a Sepolia only related problem --- Makefile | 3 ++- scripts/acceptSwap.ts | 9 +-------- scripts/approve.ts | 17 ++--------------- scripts/cancelSwap.ts | 9 +-------- scripts/createSwap.ts | 9 +-------- scripts/getSwap.ts | 28 ---------------------------- scripts/mint.ts | 15 ++------------- 7 files changed, 9 insertions(+), 81 deletions(-) delete mode 100644 scripts/getSwap.ts diff --git a/Makefile b/Makefile index 9091319..21f4693 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ compile: yarn compile ## Please specify which network following the available ones in hardhat.config.ts. -network=sepolia +network=mumbai ## Deploying Swaplace contract to the desired network. swaplace: @@ -52,6 +52,7 @@ test-suite-runner: ## Feed existing Swaplace contract with some transactions. ## make mint transactions: + make mint make approve make swap make accept diff --git a/scripts/acceptSwap.ts b/scripts/acceptSwap.ts index ae4f9af..2c3c215 100644 --- a/scripts/acceptSwap.ts +++ b/scripts/acceptSwap.ts @@ -64,14 +64,7 @@ export async function main() { /// @dev Catching any errors while creating the swap. try { - let lastNonce = await ethers.provider.getTransactionCount( - signers[0].address, - ); - tx = await Swaplace.acceptSwap(swapId, signers[0].address, { - nonce: Number(lastNonce) + 1, - maxFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum fee per gas - maxPriorityFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum tip - }); + tx = await Swaplace.acceptSwap(swapId, signers[0].address); // @dev Wait for the transaction to be mined await tx.wait(); } catch (error) { diff --git a/scripts/approve.ts b/scripts/approve.ts index 5d44b18..5137afd 100755 --- a/scripts/approve.ts +++ b/scripts/approve.ts @@ -79,21 +79,8 @@ async function main() { /// @dev We are approving the signer address to spend the amount of tokens. try { - let lastNonce = await ethers.provider.getTransactionCount( - signers[0].address, - ); - txErc20 = await MockERC20.approve(SWAPLACE_ADDRESS, amount, { - gasLimit: 500000, - nonce: Number(lastNonce) + 1, - maxFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum fee per gas - maxPriorityFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum tip - }); - txErc721 = await MockERC721.approve(SWAPLACE_ADDRESS, tokenId, { - gasLimit: 500000, - nonce: Number(lastNonce) + 2, - maxFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum fee per gas - maxPriorityFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum tip - }); + txErc20 = await MockERC20.approve(SWAPLACE_ADDRESS, amount); + txErc721 = await MockERC721.approve(SWAPLACE_ADDRESS, tokenId); } catch (error) { throw new Error( `Error while approving the tokens. Make sure that the approve function is diff --git a/scripts/cancelSwap.ts b/scripts/cancelSwap.ts index 04e2d5f..1b8cce0 100644 --- a/scripts/cancelSwap.ts +++ b/scripts/cancelSwap.ts @@ -64,14 +64,7 @@ export async function main() { /// @dev Catching any errors while creating the swap. try { - let lastNonce = await ethers.provider.getTransactionCount( - signers[0].address, - ); - tx = await Swaplace.cancelSwap(swapId, { - nonce: Number(lastNonce) + 1, - maxFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum fee per gas - maxPriorityFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum tip - }); + tx = await Swaplace.cancelSwap(swapId); // @dev Wait for the transaction to be mined await tx.wait(); } catch (error) { diff --git a/scripts/createSwap.ts b/scripts/createSwap.ts index 14b2c6a..10db06d 100644 --- a/scripts/createSwap.ts +++ b/scripts/createSwap.ts @@ -96,14 +96,7 @@ export async function main() { /// @dev Catching any errors while creating the swap. try { - let lastNonce = await ethers.provider.getTransactionCount( - signers[0].address, - ); - tx = await Swaplace.createSwap(swap, { - nonce: Number(lastNonce) + 1, - maxFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum fee per gas - maxPriorityFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum tip - }); + tx = await Swaplace.createSwap(swap); // @dev Wait for the transaction to be mined await tx.wait(); } catch (error) { diff --git a/scripts/getSwap.ts b/scripts/getSwap.ts deleted file mode 100644 index dc52eab..0000000 --- a/scripts/getSwap.ts +++ /dev/null @@ -1,28 +0,0 @@ -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 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 swap ID - const swapId = 1; - - // Get the swap - const swap = await Swaplace.getSwap(swapId); - - // Log the swap - console.log("\nSwap %: ", swapId); - console.log(swap); -} - -main().catch((error) => { - console.error(error); - process.exitCode = 1; -}); diff --git a/scripts/mint.ts b/scripts/mint.ts index 6c7b108..e31e21b 100644 --- a/scripts/mint.ts +++ b/scripts/mint.ts @@ -75,19 +75,8 @@ async function main() { /// tokens of ERC20 and the last token id for ERC721. /// We start the mint from the last token id + 1 because it starts from 0. try { - let lastNonce = await ethers.provider.getTransactionCount( - signers[0].address, - ); - txErc20 = await MockERC20.mint(signers[0].address, amount, { - nonce: Number(lastNonce) + 1, - maxFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum fee per gas - maxPriorityFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum tip - }); - txErc721 = await MockERC721.mint(signers[0].address, tokenId, { - nonce: Number(lastNonce) + 2, - maxFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum fee per gas - maxPriorityFeePerGas: ethers.utils.parseUnits("200", "gwei"), // Maximum tip - }); + txErc20 = await MockERC20.mint(signers[0].address, amount); + txErc721 = await MockERC721.mint(signers[0].address, tokenId); } catch (error) { throw new Error( `Error while minting tokens. Make sure that the minting function is From 98da6ff8a718623d29a3a551391f58f8339417b0 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 14 Mar 2024 20:28:49 +0700 Subject: [PATCH 33/91] refactor: updating into localhost as standard --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 21f4693..6247249 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ compile: yarn compile ## Please specify which network following the available ones in hardhat.config.ts. -network=mumbai +network=localhost ## Deploying Swaplace contract to the desired network. swaplace: From 73c2302dd3d3e23b99fa9f0518403b0370b45da5 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 14 Mar 2024 20:35:10 +0700 Subject: [PATCH 34/91] fix: removing unused libs --- scripts/acceptSwap.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/acceptSwap.ts b/scripts/acceptSwap.ts index 2c3c215..1ae0430 100644 --- a/scripts/acceptSwap.ts +++ b/scripts/acceptSwap.ts @@ -1,7 +1,5 @@ import { ethers } from "hardhat"; import { Contract } from "ethers"; -import { blocktimestamp } from "../test/utils/utils"; -import { Swap, composeSwap } from "../test/utils/SwapFactory"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; export async function main() { From 5037a4fab13912ce8718ad6de02b92b8f5eb953e Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 14 Mar 2024 21:49:29 +0700 Subject: [PATCH 35/91] feat: waiting for transaction to be mined --- scripts/createSwap.ts | 3 +++ scripts/deploy.ts | 3 +++ scripts/deployMock.ts | 6 +++++- scripts/mint.ts | 4 ++++ 4 files changed, 15 insertions(+), 1 deletion(-) diff --git a/scripts/createSwap.ts b/scripts/createSwap.ts index 10db06d..a382b64 100644 --- a/scripts/createSwap.ts +++ b/scripts/createSwap.ts @@ -113,6 +113,9 @@ export async function main() { /// @dev Store the recently created swap and it's corresponding ID in the `.env` file. const swapId = await Swaplace.totalSwaps(); await storeEnv(swapId, "SWAP_ID", tx.hash); + + /// @dev Awaits for the transaction to be mined. + await tx.wait(); } main().catch((error) => { diff --git a/scripts/deploy.ts b/scripts/deploy.ts index 78aae49..472ba54 100644 --- a/scripts/deploy.ts +++ b/scripts/deploy.ts @@ -35,6 +35,9 @@ async function main() { // @dev Store the contract address in the .env file. await storeEnv(Swaplace.address, "SWAPLACE_ADDRESS", true); + + /// @dev Awaits for the transaction to be mined. + await Swaplace.deployed(); } main().catch((error) => { diff --git a/scripts/deployMock.ts b/scripts/deployMock.ts index 5798e3e..f8266d3 100644 --- a/scripts/deployMock.ts +++ b/scripts/deployMock.ts @@ -43,9 +43,13 @@ async function main() { MockERC721.deployTransaction.hash, ); - // @dev Store the contract addresses in the .env file. + /// @dev Store the contract addresses in the .env file. await storeEnv(MockERC20.address, "ERC20_ADDRESS", true); await storeEnv(MockERC721.address, "ERC721_ADDRESS", true); + + /// @dev Awaits for the transaction to be mined. + await MockERC20.deployed(); + await MockERC721.deployed(); } main().catch((error) => { diff --git a/scripts/mint.ts b/scripts/mint.ts index e31e21b..f96d286 100644 --- a/scripts/mint.ts +++ b/scripts/mint.ts @@ -95,6 +95,10 @@ async function main() { ); await storeEnv(tokenId, "TOKEN_ID", false); + + /// @dev Awaits for the transaction to be mined. + await txErc20.wait(); + await txErc721.wait(); } main().catch((error) => { From 9ca530877b90eaab57014f09f28cb94e29e7f0a9 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Fri, 15 Mar 2024 10:42:17 +0700 Subject: [PATCH 36/91] fix: wrong comment on make transactions --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 6247249..dcfbe51 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ compile: yarn compile ## Please specify which network following the available ones in hardhat.config.ts. -network=localhost +network=sepolia ## Deploying Swaplace contract to the desired network. swaplace: @@ -50,7 +50,7 @@ test-suite-runner: make cancel ## Feed existing Swaplace contract with some transactions. -## make mint +## Requires a deployed Swaplace contract and mocks. transactions: make mint make approve From 8ef44f44905d8d90d51009d1bc2819a7aa387bce Mon Sep 17 00:00:00 2001 From: 0xneves Date: Fri, 15 Mar 2024 10:44:37 +0700 Subject: [PATCH 37/91] fix: default should be set to localhost --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index dcfbe51..0b0403f 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ compile: yarn compile ## Please specify which network following the available ones in hardhat.config.ts. -network=sepolia +network=localhost ## Deploying Swaplace contract to the desired network. swaplace: From cf629b1dcf9c04a1395346c99279902f118248e2 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 28 Mar 2024 19:21:06 +0700 Subject: [PATCH 38/91] feat: added kakarot rpc --- hardhat.config.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hardhat.config.ts b/hardhat.config.ts index 513be85..5932a6c 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -26,6 +26,10 @@ const config: HardhatUserConfig = { /** * @dev Testnets */ + kakarot: { + url: `${process.env.KAKAROT_RPC_URL}`, + accounts: [`${DEPLOYER_PRIVATE_KEY}`], + }, sepolia: { url: `${process.env.SEPOLIA_RPC_URL}`, accounts: [`${DEPLOYER_PRIVATE_KEY}`], From 8655daad12ea358b0658ee39610edd89fccb002a Mon Sep 17 00:00:00 2001 From: 0xneves Date: Mon, 1 Apr 2024 20:45:33 +0700 Subject: [PATCH 39/91] fix: before moved out of context and changed into beforeEach in canceling swaps --- test/TestSwaplace.test.ts | 58 ++++++++++----------------------------- 1 file changed, 15 insertions(+), 43 deletions(-) diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index 76bcdc0..b6af0f6 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -445,13 +445,13 @@ describe("Swaplace", async function () { }); describe("Canceling Swaps", () => { - context("Canceling Swaps", () => { - var swap: Swap; - before(async () => { - swap = await mockSwap(); - await Swaplace.connect(owner).createSwap(swap); - }); + var swap: Swap; + beforeEach(async () => { + swap = await mockSwap(); + await Swaplace.connect(owner).createSwap(swap); + }); + context("Canceling Swaps", () => { it("Should be able to {cancelSwap} a Swap", async function () { const lastSwap = await Swaplace.totalSwaps(); await expect(await Swaplace.connect(owner).cancelSwap(lastSwap)) @@ -461,21 +461,16 @@ describe("Swaplace", async function () { it("Should not be able to {acceptSwap} a canceled a Swap", async function () { const lastSwap = await Swaplace.totalSwaps(); - await expect( - Swaplace.connect(owner).acceptSwap(lastSwap, receiver.address), - ) - .to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`) - .withArgs(0); + await Swaplace.connect(owner).acceptSwap(lastSwap, receiver.address), + await expect( + Swaplace.connect(owner).acceptSwap(lastSwap, receiver.address), + ) + .to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`) + .withArgs(0); }); }); context("Reverts when canceling Swaps", () => { - var swap: Swap; - before(async () => { - swap = await mockSwap(); - await Swaplace.connect(owner).createSwap(swap); - }); - it("Should revert when {owner} is not {msg.sender}", async function () { const lastSwap = await Swaplace.totalSwaps(); await expect(Swaplace.connect(allowed).cancelSwap(lastSwap)) @@ -498,31 +493,8 @@ describe("Swaplace", async function () { describe("Fetching Swaps", () => { var swap: Swap; - before(async () => { - MockERC20 = await deploy("MockERC20", deployer); - MockERC721 = await deploy("MockERC721", deployer); - - await MockERC721.mint(owner.address, 1); - await MockERC20.mint(allowed.address, 1000); - - const bidingAddr = [MockERC721.address]; - const bidingAmountOrId = [1]; - - const askingAddr = [MockERC20.address]; - const askingAmountOrId = [1000]; - - const currentTimestamp = (await blocktimestamp()) * 2; - const config = await Swaplace.packData(zeroAddress, currentTimestamp); - - swap = await composeSwap( - owner.address, - config, - bidingAddr, - bidingAmountOrId, - askingAddr, - askingAmountOrId, - ); - + beforeEach(async () => { + swap = await mockSwap(); await Swaplace.connect(owner).createSwap(swap); }); @@ -531,7 +503,7 @@ describe("Swaplace", async function () { const fetchedSwap = await Swaplace.getSwap(lastSwap); expect(fetchedSwap.owner).not.to.be.equals(zeroAddress); - // swap.allowed can be the zero address and shoul not be trusted for validation + // swap.allowed can be the zero address and should not be trusted for validation expect(fetchedSwap.expiry).not.to.be.equals(0); expect(fetchedSwap.biding.length).to.be.greaterThan(0); expect(fetchedSwap.asking.length).to.be.greaterThan(0); From e22c041c49efc8cf58d65cee2c3cb372a9eacd20 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Tue, 2 Apr 2024 04:33:39 +0700 Subject: [PATCH 40/91] feat: encodeConfig with native ether payment --- contracts/SwapFactory.sol | 41 ++++++--- contracts/Swaplace.sol | 123 ++++++++++++++++---------- contracts/echidna/TestSwapFactory.sol | 20 ++++- contracts/interfaces/IErrors.sol | 20 ++--- contracts/interfaces/ISwap.sol | 7 +- contracts/interfaces/ISwapFactory.sol | 26 ++++-- contracts/interfaces/ISwaplace.sol | 9 +- 7 files changed, 157 insertions(+), 89 deletions(-) diff --git a/contracts/SwapFactory.sol b/contracts/SwapFactory.sol index 5ec37fd..0293c98 100644 --- a/contracts/SwapFactory.sol +++ b/contracts/SwapFactory.sol @@ -18,6 +18,8 @@ import {ISwapFactory} from "./interfaces/ISwapFactory.sol"; * - The `allowed` address is the address that can accept the Swap. If the allowed * address is the zero address, then anyone can accept the Swap. * - The `expiry` date is the timestamp that the Swap will be available to accept. + * - The `recipient` is the address that will receive the ETH. + * - The `value` is the amount of ETH that the recipient will receive. * - The `biding` are the assets that the owner is offering. * - The `asking` are the assets that the owner wants in exchange. * @@ -49,33 +51,44 @@ abstract contract SwapFactory is ISwapFactory, ISwap, IErrors { function makeSwap( address owner, address allowed, - uint256 expiry, + uint32 expiry, + uint8 recipient, + uint56 value, Asset[] memory biding, Asset[] memory asking ) public view virtual returns (Swap memory) { - if (expiry < block.timestamp) revert InvalidExpiry(expiry); - - if (biding.length == 0 || asking.length == 0) revert InvalidAssetsLength(); - - uint256 config = packData(allowed, expiry); - + if (expiry < block.timestamp) revert InvalidExpiry(); + uint256 config = encodeConfig(allowed, expiry, recipient, value); return Swap(owner, config, biding, asking); } /** - * @dev See {ISwapFactory-packData}. + * @dev See {ISwapFactory-encodeConfig}. */ - function packData( + function encodeConfig( address allowed, - uint256 expiry + uint32 expiry, + uint8 recipient, + uint56 value ) public pure returns (uint256) { - return (uint256(uint160(allowed)) << 96) | uint256(expiry); + return + (uint256(uint160(allowed)) << 96) | + (uint256(expiry) << 64) | + (uint256(recipient) << 56) | + uint256(value); } /** - * @dev See {ISwapFactory-parseData}. + * @dev See {ISwapFactory-decodeConfig}. */ - function parseData(uint256 config) public pure returns (address, uint256) { - return (address(uint160(config >> 96)), uint256(config & ((1 << 96) - 1))); + function decodeConfig( + uint256 config + ) public pure returns (address, uint32, uint8, uint56) { + return ( + address(uint160(config >> 96)), + uint32(config >> 64), + uint8(config >> 56), + uint56(config) + ); } } diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index 901dfb5..64e985e 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -20,21 +20,40 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { /// @dev Mapping of Swap ID to Swap struct. See {ISwap-Swap}. mapping(uint256 => Swap) private _swaps; + /** + * @dev See {ISwaplace-getSwap}. + */ + function getSwap(uint256 swapId) public view returns (Swap memory) { + return _swaps[swapId]; + } + + /** + * @dev Getter function for _totalSwaps. + */ + function totalSwaps() public view returns (uint256) { + return _totalSwaps; + } + /** * @dev See {ISwaplace-createSwap}. */ - function createSwap(Swap calldata swap) public returns (uint256) { - if (swap.owner != msg.sender) revert InvalidAddress(msg.sender); + function createSwap(Swap calldata swap) public payable returns (uint256) { + if (swap.owner != msg.sender) revert InvalidAddress(); assembly { sstore(_totalSwaps.slot, add(sload(_totalSwaps.slot), 1)) } uint256 swapId = _totalSwaps; - _swaps[swapId] = swap; - (address allowed, ) = parseData(swap.config); + (address allowed, , uint8 recipient, uint56 value) = decodeConfig( + swap.config + ); + + if (value > 0 && recipient == 0) { + if (value * 1e12 != msg.value) revert InvalidValue(); + } emit SwapCreated(swapId, msg.sender, allowed); @@ -44,43 +63,31 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { /** * @dev See {ISwaplace-acceptSwap}. */ - function acceptSwap(uint256 swapId, address receiver) public returns (bool) { + function acceptSwap( + uint256 swapId, + address receiver + ) public payable returns (bool) { Swap memory swap = _swaps[swapId]; - (address allowed, uint256 expiry) = parseData(swap.config); - - if (allowed != address(0) && allowed != msg.sender) - revert InvalidAddress(msg.sender); - - if (expiry < block.timestamp) revert InvalidExpiry(expiry); + ( + address allowed, + uint32 expiry, + uint8 recipient, + uint56 value + ) = decodeConfig(swap.config); + if (allowed != address(0) && allowed != msg.sender) revert InvalidAddress(); + if (expiry < block.timestamp) revert InvalidExpiry(); _swaps[swapId].config = 0; - Asset[] memory assets = swap.asking; + _transferFrom(msg.sender, swap.owner, swap.asking); + _transferFrom(swap.owner, receiver, swap.biding); - for (uint256 i = 0; i < assets.length; ) { - ITransfer(assets[i].addr).transferFrom( - msg.sender, - swap.owner, - assets[i].amountOrId - ); - assembly { - i := add(i, 1) - } - } - - assets = swap.biding; - - for (uint256 i = 0; i < assets.length; ) { - ITransfer(assets[i].addr).transferFrom( - swap.owner, - receiver, - assets[i].amountOrId - ); - assembly { - i := add(i, 1) - } - } + if (value > 0) + if (recipient == 0) _payNativeEth(receiver, value * 1e12); + else if (recipient > 0 && value * 1e12 == msg.value) + _payNativeEth(swap.owner, value * 1e12); + else revert InvalidValue(); emit SwapAccepted(swapId, swap.owner, msg.sender); @@ -91,22 +98,47 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { * @dev See {ISwaplace-cancelSwap}. */ function cancelSwap(uint256 swapId) public { - if (_swaps[swapId].owner != msg.sender) revert InvalidAddress(msg.sender); - - (, uint256 expiry) = parseData(_swaps[swapId].config); + Swap memory swap = _swaps[swapId]; + if (swap.owner != msg.sender) revert InvalidAddress(); - if (expiry < block.timestamp) revert InvalidExpiry(expiry); + (, uint32 expiry, uint8 recipient, uint56 value) = decodeConfig( + swap.config + ); + if (expiry < block.timestamp) revert InvalidExpiry(); _swaps[swapId].config = 0; + if (value > 0 && recipient == 0) _payNativeEth(msg.sender, value * 1e12); + emit SwapCanceled(swapId, msg.sender); } /** - * @dev See {ISwaplace-getSwap}. + * @dev Send and amount of native Ether to the receiver. */ - function getSwap(uint256 swapId) public view returns (Swap memory) { - return _swaps[swapId]; + function _payNativeEth(address receiver, uint256 value) internal { + (bool success, ) = receiver.call{value: value}(""); + if (!success) revert InvalidValue(); + } + + /** + * @dev Transfer 'assets' from 'from' to 'to'. + * Where 0x23b872dd is the selector of the `transferFrom` function. + */ + function _transferFrom( + address from, + address to, + Asset[] memory assets + ) internal { + for (uint256 i; i < assets.length; ) { + (bool success, ) = address(assets[i].addr).call( + abi.encodeWithSelector(0x23b872dd, from, to, assets[i].amountOrId) + ); + if (!success) revert InvalidCall(); + assembly { + i := add(i, 1) + } + } } /** @@ -119,11 +151,4 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { interfaceID == type(IERC165).interfaceId || interfaceID == type(ISwaplace).interfaceId; } - - /** - * @dev Getter function for _totalSwaps. - */ - function totalSwaps() public view returns (uint256) { - return _totalSwaps; - } } diff --git a/contracts/echidna/TestSwapFactory.sol b/contracts/echidna/TestSwapFactory.sol index b8ff11b..3ebd53a 100644 --- a/contracts/echidna/TestSwapFactory.sol +++ b/contracts/echidna/TestSwapFactory.sol @@ -32,12 +32,14 @@ contract TestFactory is SwapFactory { Swap memory swap = makeSwap( owner, address(0), - block.timestamp + 1000, + uint32(block.timestamp + 1000), + 0, + 0, make_asset_array(addr, amountOrId), make_asset_array(addr, amountOrId) ); - (, uint256 expiry) = parseData(swap.config); + (, uint32 expiry, , ) = decodeConfig(swap.config); assert(expiry > block.timestamp); assert(swap.biding.length > 0); @@ -46,14 +48,24 @@ contract TestFactory is SwapFactory { } function echidna_revert_invalid_expiry() public view { - makeSwap(address(0), address(0), block.timestamp - 100, _asset, _asset); + makeSwap( + address(0), + address(0), + uint32(block.timestamp - 100), + 0, + 0, + _asset, + _asset + ); } function echidna_revert_invalid_length() public view { makeSwap( address(0), address(0), - block.timestamp + 100, + uint32(block.timestamp + 100), + 0, + 0, new Asset[](0), new Asset[](0) ); diff --git a/contracts/interfaces/IErrors.sol b/contracts/interfaces/IErrors.sol index 25e3f99..1395042 100644 --- a/contracts/interfaces/IErrors.sol +++ b/contracts/interfaces/IErrors.sol @@ -8,20 +8,20 @@ interface IErrors { /** * @dev Displayed when the caller is not the owner of the swap. */ - error InvalidAddress(address caller); + error InvalidAddress(); /** - * @dev Displayed when the amount of {ISwap-Asset} has a length of zero. - * - * NOTE: The `biding` or `asking` array must not be empty to avoid mistakes - * when creating a swap. Assuming one side of the swap is empty, the - * correct approach should be the usage of {transferFrom} and we reinforce - * this behavior by requiring the length of the array to be bigger than zero. + * @dev Displayed when the `expiry` date is in the past. */ - error InvalidAssetsLength(); + error InvalidExpiry(); /** - * @dev Displayed when the `expiry` date is in the past. + * @dev Displayed when the `msg.value` doesn't match the swap request. + */ + error InvalidValue(); + + /** + * @dev Displayed when a low level call failed to execute. */ - error InvalidExpiry(uint256 timestamp); + error InvalidCall(); } diff --git a/contracts/interfaces/ISwap.sol b/contracts/interfaces/ISwap.sol index 693f162..2b10721 100644 --- a/contracts/interfaces/ISwap.sol +++ b/contracts/interfaces/ISwap.sol @@ -24,8 +24,11 @@ interface ISwap { * * It is composed of: * - `owner` of the Swap. - * - `config` represents two packed values: 'allowed' for the allowed address - * to accept the swap and 'expiry' for the expiration date of the swap. + * - `config` represents two packed values: + * - - `allowed` for the allowed address to accept the swap + * - - `expiry` for the expiration date of the swap. + * - - `recipient` for the address that will receive the ETH. + * - - `value` for the amount of ETH that the recipient will receive. * - `biding` assets that are being bided by the owner. * - `asking` assets that are being asked by the owner. * diff --git a/contracts/interfaces/ISwapFactory.sol b/contracts/interfaces/ISwapFactory.sol index 70a3d43..82ca967 100644 --- a/contracts/interfaces/ISwapFactory.sol +++ b/contracts/interfaces/ISwapFactory.sol @@ -27,23 +27,33 @@ interface ISwapFactory { function makeSwap( address owner, address allowed, - uint256 expiry, + uint32 expiry, + uint8 recipient, + uint56 value, ISwap.Asset[] memory assets, ISwap.Asset[] memory asking ) external view returns (ISwap.Swap memory); /** - * @dev Packs `allowed` and the `expiry`. - * This function returns the bitwise packing of `allowed` and `expiry` as a uint256. + * @dev This function returns the bitwise packing as a uint256. */ - function packData( + function encodeConfig( address allowed, - uint256 expiry + uint32 expiry, + uint8 recipient, + uint56 value ) external pure returns (uint256); /** - * @dev Parsing the `config`. - * This function returns the extracted values of `allowed` and `expiry`. + * @dev Decode the `config` variable. + * + * This function returns the extracted values of: + * - `allowed` + * - `expiry` + * - `recipient` + * - `value` */ - function parseData(uint256 config) external pure returns (address, uint256); + function decodeConfig( + uint256 config + ) external pure returns (address, uint32, uint8, uint56); } diff --git a/contracts/interfaces/ISwaplace.sol b/contracts/interfaces/ISwaplace.sol index 5dedc9d..bc6f217 100644 --- a/contracts/interfaces/ISwaplace.sol +++ b/contracts/interfaces/ISwaplace.sol @@ -41,7 +41,9 @@ interface ISwaplace { * * Emits a {SwapCreated} event. */ - function createSwap(ISwap.Swap calldata Swap) external returns (uint256); + function createSwap( + ISwap.Swap calldata Swap + ) external payable returns (uint256); /** * @dev Accepts a Swap. Once the Swap is accepted, the expiry is set @@ -59,7 +61,10 @@ interface ISwaplace { * NOTE: The expiry is set to 0, because if the Swap is expired it * will revert, preventing reentrancy attacks. */ - function acceptSwap(uint256 swapId, address receiver) external returns (bool); + function acceptSwap( + uint256 swapId, + address receiver + ) external payable returns (bool); /** * @dev Cancels an active Swap by setting the expiry to zero. From 6ccbaefb1f4fcfd4179d711f4f55a76e8befb74d Mon Sep 17 00:00:00 2001 From: 0xneves Date: Tue, 2 Apr 2024 04:35:32 +0700 Subject: [PATCH 41/91] fix: grammar --- contracts/Swaplace.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index 64e985e..717cf29 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -9,7 +9,7 @@ import {SwapFactory} from "./SwapFactory.sol"; /** * @author @0xneves | @blockful_io * @dev Swaplace is a Decentralized Feeless DEX. It has no owners, it cannot be stopped. - * Its cern is to facilitate swaps between virtual assets following the ERC standard. + * It's core is to facilitate swaps between virtual assets following the ERC standard. * Users can propose or accept swaps by allowing Swaplace to move their assets using the * `approve` or `permit` function. */ From 2a73b3feb309eb5145e054863190a82d1b380cf6 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 3 Apr 2024 14:31:15 +0700 Subject: [PATCH 42/91] fix: grammar on _payNativeEther --- contracts/Swaplace.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index 717cf29..e8a9fa5 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -114,7 +114,7 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { } /** - * @dev Send and amount of native Ether to the receiver. + * @dev Send an amount of native Ether to the receiver address. */ function _payNativeEth(address receiver, uint256 value) internal { (bool success, ) = receiver.call{value: value}(""); From 095f5b675329b572bb766cdc47c73e1ce928e582 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 3 Apr 2024 14:33:32 +0700 Subject: [PATCH 43/91] fix: better narrative for contract description --- contracts/Swaplace.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index e8a9fa5..89b4e93 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -8,10 +8,10 @@ import {SwapFactory} from "./SwapFactory.sol"; /** * @author @0xneves | @blockful_io - * @dev Swaplace is a Decentralized Feeless DEX. It has no owners, it cannot be stopped. - * It's core is to facilitate swaps between virtual assets following the ERC standard. + * @dev Swaplace is a decentralized and feeless DEX/OTC. Ownerless, it cannot be stopped. + * It's core is to facilitate swaps between virtual assets using the ERC standard. * Users can propose or accept swaps by allowing Swaplace to move their assets using the - * `approve` or `permit` function. + * `approve`, `permit` or similar functions. */ contract Swaplace is SwapFactory, ISwaplace, IERC165 { /// @dev Swap Identifier counter. From e583f7637a562a45fdf7466a6709578cbe2db267 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 3 Apr 2024 14:38:26 +0700 Subject: [PATCH 44/91] fix: Swap struct description --- contracts/interfaces/ISwap.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/interfaces/ISwap.sol b/contracts/interfaces/ISwap.sol index 2b10721..c1fd5b7 100644 --- a/contracts/interfaces/ISwap.sol +++ b/contracts/interfaces/ISwap.sol @@ -23,14 +23,14 @@ interface ISwap { * @dev The Swap struct is the heart of Swaplace. * * It is composed of: - * - `owner` of the Swap. - * - `config` represents two packed values: - * - - `allowed` for the allowed address to accept the swap - * - - `expiry` for the expiration date of the swap. + * - `owner` creator of the Swap. + * - `config` configuration of four packed values: + * - - `allowed` for the allowed address to accept the swap. + * - - `expiry` for the expiration date of the swap in unix time. * - - `recipient` for the address that will receive the ETH. * - - `value` for the amount of ETH that the recipient will receive. - * - `biding` assets that are being bided by the owner. - * - `asking` assets that are being asked by the owner. + * - `biding` assets offered by the swap creator. + * - `asking` assets asked by the swap creator. * * NOTE: When `allowed` address is the zero address, anyone can accept the Swap. */ From 9f35feecbfce18d36306669da5680feb2d876b46 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Sat, 6 Apr 2024 22:59:45 +0700 Subject: [PATCH 45/91] fix: swapId should start at 1 --- .env.sample | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.env.sample b/.env.sample index 7d6c4a9..9c426f7 100644 --- a/.env.sample +++ b/.env.sample @@ -8,7 +8,7 @@ AMOUNT=1000 # Token ID of ERC721 to be minted for signer address. TOKEN_ID=1 # The swap to be accepted by the acceptee. -SWAP_ID=3 +SWAP_ID=1 # These are public known private keys and are here as an example. # You should change the private keys to your own private keys. From 432d18ce8f06dd9997e74e551d362af8bc3cd0db Mon Sep 17 00:00:00 2001 From: 0xneves Date: Fri, 12 Apr 2024 02:51:57 +0700 Subject: [PATCH 46/91] fix: enhancing documentation descriptions --- contracts/Swaplace.sol | 2 +- contracts/interfaces/ISwapFactory.sol | 22 +++++++++------------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index 89b4e93..9b4922e 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -123,7 +123,7 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { /** * @dev Transfer 'assets' from 'from' to 'to'. - * Where 0x23b872dd is the selector of the `transferFrom` function. + * The selector of the `transferFrom` function in bytes4: 0x23b872dd */ function _transferFrom( address from, diff --git a/contracts/interfaces/ISwapFactory.sol b/contracts/interfaces/ISwapFactory.sol index 82ca967..0b442bd 100644 --- a/contracts/interfaces/ISwapFactory.sol +++ b/contracts/interfaces/ISwapFactory.sol @@ -8,8 +8,8 @@ import {ISwap} from "./ISwap.sol"; */ interface ISwapFactory { /** - * @dev Constructs an asset struct that works for ERC20 or ERC721. - * This function is a utility to easily create an `Asset` struct on-chain or off-chain. + * @dev Constructs an asset struct that works for token standards. + * This function is a utility to easily create an `Asset` type on-chain or off-chain. */ function makeAsset( address addr, @@ -21,8 +21,7 @@ interface ISwapFactory { * * Requirements: * - * - `expiry` cannot be in the past timestamp. - * - `biding` and `asking` cannot be empty. + * - `expiry` cannot be in the past. */ function makeSwap( address owner, @@ -35,7 +34,7 @@ interface ISwapFactory { ) external view returns (ISwap.Swap memory); /** - * @dev This function returns the bitwise packing as a uint256. + * @dev This function uses bitwise to return a uint256. */ function encodeConfig( address allowed, @@ -45,15 +44,12 @@ interface ISwapFactory { ) external pure returns (uint256); /** - * @dev Decode the `config` variable. - * - * This function returns the extracted values of: - * - `allowed` - * - `expiry` - * - `recipient` - * - `value` + * @dev Decode `config` into their respective variables. */ function decodeConfig( uint256 config - ) external pure returns (address, uint32, uint8, uint56); + ) + external + pure + returns (address allowed, uint32 expiry, uint8 recipient, uint56 value); } From c5ededfa57d968daa54f7cc4cb27cd7d8805255d Mon Sep 17 00:00:00 2001 From: 0xneves Date: Fri, 12 Apr 2024 02:53:08 +0700 Subject: [PATCH 47/91] fix: enhance documentation of makeAsset --- contracts/interfaces/ISwapFactory.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/interfaces/ISwapFactory.sol b/contracts/interfaces/ISwapFactory.sol index 0b442bd..18aa5cc 100644 --- a/contracts/interfaces/ISwapFactory.sol +++ b/contracts/interfaces/ISwapFactory.sol @@ -8,8 +8,7 @@ import {ISwap} from "./ISwap.sol"; */ interface ISwapFactory { /** - * @dev Constructs an asset struct that works for token standards. - * This function is a utility to easily create an `Asset` type on-chain or off-chain. + * @dev Constructs an {ISwap-Asset} struct to work with token standards. */ function makeAsset( address addr, From 60d5a356a0d9c85259558563dd6cf36793cae39d Mon Sep 17 00:00:00 2001 From: 0xneves Date: Fri, 12 Apr 2024 06:30:31 +0700 Subject: [PATCH 48/91] feat: adding ERC1155 mock and test --- contracts/mock/MockERC1155.sol | 37 ++++++++++++++++++++++++++++++++++ test/TestMockContracts.test.ts | 23 +++++++++++++++++---- test/utils/utils.ts | 6 ++++-- 3 files changed, 60 insertions(+), 6 deletions(-) create mode 100644 contracts/mock/MockERC1155.sol diff --git a/contracts/mock/MockERC1155.sol b/contracts/mock/MockERC1155.sol new file mode 100644 index 0000000..b5904c0 --- /dev/null +++ b/contracts/mock/MockERC1155.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.9; + +import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; +import "@openzeppelin/contracts/utils/Strings.sol"; + +contract MockERC1155 is ERC1155 { + using Strings for uint256; + + string private _name; + string private _symbol; + + function name() public view returns (string memory) { + return _name; + } + + function symbol() public view returns (string memory) { + return _symbol; + } + + constructor() + ERC1155("ipfs://QmQJnHseE9VPw5qVxuEhxTiZ7avzgkCdFz69rg86UvTZdk/") + { + _name = "MockERC1155"; + _symbol = "M1155"; + } + + function mint(address to, uint256 id, uint256 amount) public { + _mint(to, id, amount, ""); + } + + function tokenURI( + uint256 tokenId + ) public view virtual returns (string memory) { + return uri(tokenId); + } +} diff --git a/test/TestMockContracts.test.ts b/test/TestMockContracts.test.ts index 545b125..b7be7eb 100644 --- a/test/TestMockContracts.test.ts +++ b/test/TestMockContracts.test.ts @@ -8,6 +8,7 @@ describe("Swaplace", async function () { // The deployed contracts let MockERC20: Contract; let MockERC721: Contract; + let MockERC1155: Contract; // The signers of the test let deployer: SignerWithAddress; @@ -18,27 +19,32 @@ describe("Swaplace", async function () { [deployer, owner, acceptee] = await ethers.getSigners(); MockERC20 = await deploy("MockERC20", deployer); MockERC721 = await deploy("MockERC721", deployer); + MockERC1155 = await deploy("MockERC1155", deployer); }); it("Should test the {mint} function", async function () { - // Testing the mint of ERC20 await MockERC20.mint(owner.address, 1000); expect(await MockERC20.balanceOf(owner.address)).to.be.equals(1000); - // Testing the mint of ERC721 await MockERC721.mint(owner.address, 1); expect(await MockERC721.balanceOf(owner.address)).to.be.equals(1); + + await MockERC1155.mint(owner.address, 1, 5); }); it("Should test the {approve} function", async function () { - // Testing the approval of ERC20 await MockERC20.connect(owner).approve(acceptee.address, 1000); expect( await MockERC20.allowance(owner.address, acceptee.address), ).to.be.equals("1000"); - // Testing the approval of ERC721 + await MockERC721.connect(owner).approve(acceptee.address, 1); expect(await MockERC721.getApproved(1)).to.be.equals(acceptee.address); + + await MockERC1155.connect(owner).setApprovalForAll(acceptee.address, true); + expect( + await MockERC1155.isApprovedForAll(owner.address, acceptee.address), + ).to.be.equals(true); }); it("Should test the {transferFrom} function", async function () { @@ -54,5 +60,14 @@ describe("Swaplace", async function () { ); expect(await MockERC721.balanceOf(owner.address)).to.be.equals(0); expect(await MockERC721.balanceOf(acceptee.address)).to.be.equals(1); + // Testing the transfer of ERC1155 + await MockERC1155.connect(owner).safeTransferFrom( + owner.address, + acceptee.address, + 1, + 5, + "0x", + ); + expect(await MockERC1155.balanceOf(acceptee.address, 1)).to.be.equals(5); }); }); diff --git a/test/utils/utils.ts b/test/utils/utils.ts index 486ad40..6c217bd 100644 --- a/test/utils/utils.ts +++ b/test/utils/utils.ts @@ -32,7 +32,8 @@ export async function deploy(contractName: any, signer: any) { throw new Error( `Error getting the Contract Factory for ${contractName}. Make sure the contract is compiled, the type-chain generated - and a valid Ethereum Address for signer set in hardhat.config.ts.`, + and a valid Ethereum Address for signer set in hardhat.config.ts. + ${error}`, ); } @@ -44,7 +45,8 @@ export async function deploy(contractName: any, signer: any) { `Error deploying the Contract ${contractName}. Make sure the network is correct, that you have a valid Ethereum Address for signer with enough funds for the transaction. The gas settings might - as well be lower than the amount required by the network at the moment.`, + as well be lower than the amount required by the network at the moment. + ${error}`, ); } From 2e7190532f2d083430256a37cafc502f1cb81538 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Fri, 12 Apr 2024 06:30:59 +0700 Subject: [PATCH 49/91] refactor: adding URI to the mock erc721 --- contracts/mock/MockERC721.sol | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/contracts/mock/MockERC721.sol b/contracts/mock/MockERC721.sol index e763dc5..f9e63ff 100644 --- a/contracts/mock/MockERC721.sol +++ b/contracts/mock/MockERC721.sol @@ -12,4 +12,10 @@ contract MockERC721 is ERC721 { totalSupply++; _mint(to, id); } + + function tokenURI( + uint256 + ) public view virtual override returns (string memory) { + return "ipfs://QmQJnHseE9VPw5qVxuEhxTiZ7avzgkCdFz69rg86UvTZdk/"; + } } From c707b9936680ca92233694d736ac6c6fd6c2dae7 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Fri, 12 Apr 2024 06:31:13 +0700 Subject: [PATCH 50/91] refactor: including new encoder in the pipeline --- test/TestSwapFactory.test.ts | 131 +++++++++++++++++++++++++------- test/TestSwaplace.test.ts | 142 +++++++++++++++++++++++------------ test/utils/SwapFactory.ts | 68 +++++++++++++---- 3 files changed, 250 insertions(+), 91 deletions(-) diff --git a/test/TestSwapFactory.test.ts b/test/TestSwapFactory.test.ts index bb1079d..b434fb4 100644 --- a/test/TestSwapFactory.test.ts +++ b/test/TestSwapFactory.test.ts @@ -2,7 +2,14 @@ import { expect } from "chai"; import { Contract } from "ethers"; import { ethers } from "hardhat"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; -import { Asset, makeAsset, makeSwap, composeSwap } from "./utils/SwapFactory"; +import { + Asset, + makeAsset, + makeSwap, + composeSwap, + encodeConfig, + decodeConfig, +} from "./utils/SwapFactory"; import { blocktimestamp, deploy } from "./utils/utils"; describe("Swaplace Factory", async function () { @@ -28,11 +35,11 @@ describe("Swaplace Factory", async function () { it("Should be able to {makeAsset} for ERC20 and ERC721", async function () { var asset: Asset = await makeAsset(MockERC20.address, 1000); expect(asset.addr).to.be.equals(MockERC20.address); - expect(asset.amountOrId).to.be.equals("1000"); + expect(asset.amountOrId).to.be.equals(1000); var asset: Asset = await makeAsset(MockERC721.address, 1); expect(asset.addr).to.be.equals(MockERC721.address); - expect(asset.amountOrId).to.be.equals("1"); + expect(asset.amountOrId).to.be.equals(1); }); it("Should be able to {makeAsset} in the off-chain matching on-chain", async function () { @@ -40,16 +47,47 @@ describe("Swaplace Factory", async function () { var asset: Asset = await makeAsset(MockERC20.address, 1000); expect(asset.addr).to.be.equals(MockERC20.address); - expect(asset.amountOrId).to.be.equals("1000"); + expect(asset.amountOrId).to.be.equals(1000); + }); + + it("Should be able to encode and decode config using off-chain", async function () { + const currentTimestamp = (await blocktimestamp()) + 2000000; + const configOnChain = await Swaplace.encodeConfig( + Swaplace.address, + currentTimestamp, + 0, + 0, + ); + const configOffChain = await encodeConfig( + Swaplace.address, + currentTimestamp, + 0, + 0, + ); + expect(configOnChain).to.be.equals(configOffChain); + + const [allowed, expiry, recipient, value] = await Swaplace.decodeConfig( + configOnChain, + ); + const decodedConfig = await decodeConfig(configOffChain); + expect(BigInt(allowed)).to.be.equals(decodedConfig.allowed); + expect(expiry).to.be.equals(decodedConfig.expiry); + expect(recipient).to.be.equals(decodedConfig.recipient); + expect(value).to.be.equals(decodedConfig.value); }); it("Should be able to {makeSwap} with ERC20 and ERC721", async function () { - const currentTimestamp = (await blocktimestamp()) * 2; + const currentTimestamp = (await blocktimestamp()) + 2000000; const ERC20Asset: Asset = await makeAsset(MockERC20.address, 1000); const ERC721Asset: Asset = await makeAsset(MockERC721.address, 1); - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); const swap = await makeSwap( owner.address, @@ -58,7 +96,7 @@ describe("Swaplace Factory", async function () { [ERC721Asset], ); - const [allowed, expiry] = await Swaplace.parseData(swap.config); + const [allowed, expiry, ,] = await Swaplace.decodeConfig(swap.config); expect(swap.owner).to.be.equals(owner.address); expect(expiry).to.be.equals(currentTimestamp); @@ -68,12 +106,17 @@ describe("Swaplace Factory", async function () { }); it("Should be able to {makeSwap} in the off-chain matching on-chain", async function () { - const currentTimestamp = (await blocktimestamp()) * 2; + const currentTimestamp = (await blocktimestamp()) + 2000000; const ERC20Asset: Asset = await makeAsset(MockERC20.address, 1000); const ERC721Asset: Asset = await makeAsset(MockERC721.address, 1); - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); const swap = await makeSwap( owner.address, @@ -86,12 +129,14 @@ describe("Swaplace Factory", async function () { owner.address, zeroAddress, currentTimestamp, + 0, + 0, [ERC20Asset], [ERC721Asset], ); - const [allowed, expiry] = await Swaplace.parseData(swap.config); - const [onChainAllowed, onChainExpiry] = await Swaplace.parseData( + const [allowed, expiry, ,] = await Swaplace.decodeConfig(swap.config); + const [onChainAllowed, onChainExpiry, ,] = await Swaplace.decodeConfig( onChainSwap.config, ); @@ -111,12 +156,17 @@ describe("Swaplace Factory", async function () { }); it("Should be able to {makeSwap} with multiple assets", async function () { - const currentTimestamp = (await blocktimestamp()) * 2; + const currentTimestamp = (await blocktimestamp()) + 2000000; const ERC20Asset = await makeAsset(MockERC20.address, 1000); const ERC721Asset = await makeAsset(MockERC721.address, 1); - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); const swap = await makeSwap( owner.address, @@ -125,7 +175,7 @@ describe("Swaplace Factory", async function () { [ERC20Asset, ERC721Asset], ); - const [, expiry] = await Swaplace.parseData(swap.config); + const [, expiry, ,] = await Swaplace.decodeConfig(swap.config); expect(swap.owner).to.be.equals(owner.address); expect(expiry).to.be.equals(expiry); @@ -136,7 +186,7 @@ describe("Swaplace Factory", async function () { }); it("Should be able to {composeSwap} using both ERC20, ERC721", async function () { - const currentTimestamp = (await blocktimestamp()) * 2; + const currentTimestamp = (await blocktimestamp()) + 2000000; const bidingAddr = [MockERC20.address, MockERC721.address]; const bidingAmountOrId = [1000, 1]; @@ -144,7 +194,12 @@ describe("Swaplace Factory", async function () { const askingAddr = [MockERC721.address]; const askingAmountOrId = [2]; - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); const swap = await composeSwap( owner.address, @@ -155,7 +210,7 @@ describe("Swaplace Factory", async function () { askingAmountOrId, ); - const [allowed, expiry] = await Swaplace.parseData(swap.config); + const [allowed, expiry, ,] = await Swaplace.decodeConfig(swap.config); expect(swap.owner).to.be.equals(owner.address); expect(allowed).to.be.equals(zeroAddress); @@ -172,7 +227,7 @@ describe("Swaplace Factory", async function () { const askingAmountOrId = [2]; try { - const config = await Swaplace.packData(zeroAddress, expiry); + const config = await Swaplace.encodeConfig(zeroAddress, expiry, 0, 0); await composeSwap( owner.address, config, @@ -187,7 +242,7 @@ describe("Swaplace Factory", async function () { }); it("Should revert using {composeSwap} with owner as address zero", async function () { - const currentTimestamp = (await blocktimestamp()) * 2; + const currentTimestamp = (await blocktimestamp()) + 2000000; const bidingAddr = [MockERC20.address]; const bidingAmountOrId = [1000]; @@ -196,7 +251,12 @@ describe("Swaplace Factory", async function () { const askingAmountOrId = [2]; try { - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); await composeSwap( zeroAddress, config, @@ -211,7 +271,7 @@ describe("Swaplace Factory", async function () { }); it("Should revert using {composeSwap} with empty assets", async function () { - const currentTimestamp = (await blocktimestamp()) * 2; + const currentTimestamp = (await blocktimestamp()) + 2000000; const bidingAddr = [MockERC20.address]; const bidingAmountOrId = [1000]; @@ -220,7 +280,12 @@ describe("Swaplace Factory", async function () { const askingAmountOrId: any[] = []; try { - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); await composeSwap( owner.address, config, @@ -235,7 +300,7 @@ describe("Swaplace Factory", async function () { }); it("Should revert using {composeSwap} with empty assets length", async function () { - const currentTimestamp = (await blocktimestamp()) * 2; + const currentTimestamp = (await blocktimestamp()) + 2000000; const bidingAddr = [MockERC20.address]; const bidingAmountOrId = [1000]; @@ -244,7 +309,12 @@ describe("Swaplace Factory", async function () { const askingAmountOrId = [1, 999, 777]; try { - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); await composeSwap( owner.address, config, @@ -258,12 +328,17 @@ describe("Swaplace Factory", async function () { } }); - it("Should ensure packData() and parseData() return the right values", async function () { - const currentTimestamp = (await blocktimestamp()) * 2; + it("Should ensure encodeConfig() and decodeConfig() return the right values", async function () { + const currentTimestamp = (await blocktimestamp()) + 2000000; - const config = await Swaplace.packData(acceptee.address, currentTimestamp); + const config = await Swaplace.encodeConfig( + acceptee.address, + currentTimestamp, + 0, + 0, + ); - const [allowed, expiry] = await Swaplace.parseData(config); + const [allowed, expiry, ,] = await Swaplace.decodeConfig(config); expect(allowed).to.be.equals(acceptee.address); expect(expiry).to.be.equals(currentTimestamp); diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index b6af0f6..8feccd1 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -32,8 +32,14 @@ describe("Swaplace", async function () { const askingAddr = [MockERC20.address]; const askingAmountOrId = [50]; - const currentTimestamp = (await blocktimestamp()) * 2; - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const currentTimestamp = (await blocktimestamp()) + 1000000; + + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); const swap: Swap = await composeSwap( owner.address, @@ -63,8 +69,13 @@ describe("Swaplace", async function () { const askingAddr = [MockERC20.address]; const askingAmountOrId = [50]; - const currentTimestamp = (await blocktimestamp()) * 2; - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); const swap: Swap = await composeSwap( owner.address, @@ -91,8 +102,13 @@ describe("Swaplace", async function () { ]; const askingAmountOrId = [50, 100, 150]; - const currentTimestamp = (await blocktimestamp()) * 2; - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); const swap: Swap = await composeSwap( owner.address, @@ -123,8 +139,13 @@ describe("Swaplace", async function () { ]; const askingAmountOrId = [50, 100, 150]; - const currentTimestamp = (await blocktimestamp()) * 2; - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); const swap: Swap = await composeSwap( owner.address, @@ -147,8 +168,13 @@ describe("Swaplace", async function () { const askingAddr = [MockERC721.address]; const askingAmountOrId = [4]; - const currentTimestamp = (await blocktimestamp()) * 2; - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); const swap: Swap = await composeSwap( owner.address, @@ -175,8 +201,13 @@ describe("Swaplace", async function () { ]; const askingAmountOrId = [4, 5, 6]; - const currentTimestamp = (await blocktimestamp()) * 2; - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); const swap: Swap = await composeSwap( owner.address, @@ -207,8 +238,13 @@ describe("Swaplace", async function () { ]; const askingAmountOrId = [4, 5, 6]; - const currentTimestamp = (await blocktimestamp()) * 2; - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); const swap: Swap = await composeSwap( owner.address, @@ -228,9 +264,9 @@ describe("Swaplace", async function () { context("Reverts when creating Swaps", () => { it("Should revert when {owner} is not {msg.sender}", async function () { const swap = await mockSwap(); - await expect(Swaplace.connect(allowed).createSwap(swap)) - .to.be.revertedWithCustomError(Swaplace, `InvalidAddress`) - .withArgs(allowed.address); + await expect( + Swaplace.connect(allowed).createSwap(swap), + ).to.be.revertedWithCustomError(Swaplace, `InvalidAddress`); }); }); }); @@ -253,8 +289,13 @@ describe("Swaplace", async function () { const askingAddr = [MockERC20.address]; const askingAmountOrId = [1000]; - const currentTimestamp = (await blocktimestamp()) * 2; - const config = await Swaplace.packData(zeroAddress, currentTimestamp); + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); swap = await composeSwap( owner.address, @@ -328,9 +369,14 @@ describe("Swaplace", async function () { await MockERC721.connect(allowed).approve(Swaplace.address, 10); const swap = await mockSwap(); - const [, expiry] = await Swaplace.parseData(swap.config); + const [, expiry, ,] = await Swaplace.decodeConfig(swap.config); - swap.config = await Swaplace.packData(allowed.address, expiry); + swap.config = await Swaplace.encodeConfig( + allowed.address, + expiry, + 0, + 0, + ); await expect(await Swaplace.connect(owner).createSwap(swap)) .to.emit(Swaplace, "SwapCreated") @@ -377,26 +423,22 @@ describe("Swaplace", async function () { await Swaplace.totalSwaps(), receiver.address, ), - ) - .to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`) - .withArgs(0); + ).to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`); }); it("Should revert when {expiry} is smaller than {block.timestamp}", async function () { await Swaplace.connect(owner).createSwap(swap); - const [, expiry] = await Swaplace.parseData(swap.config); + const [, expiry, ,] = await Swaplace.decodeConfig(swap.config); - await network.provider.send("evm_increaseTime", [expiry * 2]); + await network.provider.send("evm_increaseTime", [2000000]); await expect( Swaplace.connect(owner).acceptSwap( await Swaplace.totalSwaps(), receiver.address, ), - ) - .to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`) - .withArgs(expiry); + ).to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`); }); it("Should revert when {allowance} is not provided", async function () { @@ -409,7 +451,7 @@ describe("Swaplace", async function () { await Swaplace.totalSwaps(), receiver.address, ), - ).to.be.revertedWith(`ERC721: caller is not token owner or approved`); + ).to.be.revertedWithCustomError(Swaplace, `InvalidCall`); }); it("Should revert when {acceptSwap} as not allowed to P2P Swap", async function () { @@ -421,8 +463,13 @@ describe("Swaplace", async function () { const swap = await mockSwap(); - const [, expiry] = await Swaplace.parseData(swap.config); - swap.config = await Swaplace.packData(deployer.address, expiry); + const [, expiry, ,] = await Swaplace.decodeConfig(swap.config); + swap.config = await Swaplace.encodeConfig( + deployer.address, + expiry, + 0, + 0, + ); await expect(await Swaplace.connect(owner).createSwap(swap)) .to.emit(Swaplace, "SwapCreated") @@ -437,9 +484,7 @@ describe("Swaplace", async function () { await Swaplace.totalSwaps(), receiver.address, ), - ) - .to.be.revertedWithCustomError(Swaplace, "InvalidAddress") - .withArgs(allowed.address); + ).to.be.revertedWithCustomError(Swaplace, "InvalidAddress"); }); }); }); @@ -464,29 +509,27 @@ describe("Swaplace", async function () { await Swaplace.connect(owner).acceptSwap(lastSwap, receiver.address), await expect( Swaplace.connect(owner).acceptSwap(lastSwap, receiver.address), - ) - .to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`) - .withArgs(0); + ).to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`); }); }); context("Reverts when canceling Swaps", () => { it("Should revert when {owner} is not {msg.sender}", async function () { const lastSwap = await Swaplace.totalSwaps(); - await expect(Swaplace.connect(allowed).cancelSwap(lastSwap)) - .to.be.revertedWithCustomError(Swaplace, `InvalidAddress`) - .withArgs(allowed.address); + await expect( + Swaplace.connect(allowed).cancelSwap(lastSwap), + ).to.be.revertedWithCustomError(Swaplace, `InvalidAddress`); }); it("Should revert when {expiry} is smaller than {block.timestamp}", async function () { - const [, expiry] = await Swaplace.parseData(swap.config); + const [, expiry, ,] = await Swaplace.decodeConfig(swap.config); - await network.provider.send("evm_increaseTime", [expiry * 2]); + await network.provider.send("evm_increaseTime", [2000000]); const lastSwap = await Swaplace.totalSwaps(); - await expect(Swaplace.connect(owner).cancelSwap(lastSwap)) - .to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`) - .withArgs(expiry); + await expect( + Swaplace.connect(owner).cancelSwap(lastSwap), + ).to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`); }); }); }); @@ -502,9 +545,10 @@ describe("Swaplace", async function () { const lastSwap = await Swaplace.totalSwaps(); const fetchedSwap = await Swaplace.getSwap(lastSwap); + const [, expiry, ,] = await Swaplace.decodeConfig(swap.config); + expect(fetchedSwap.owner).not.to.be.equals(zeroAddress); - // swap.allowed can be the zero address and should not be trusted for validation - expect(fetchedSwap.expiry).not.to.be.equals(0); + expect(expiry).not.to.be.equals(0); expect(fetchedSwap.biding.length).to.be.greaterThan(0); expect(fetchedSwap.asking.length).to.be.greaterThan(0); }); @@ -514,7 +558,7 @@ describe("Swaplace", async function () { const fetchedSwap = await Swaplace.getSwap(imaginarySwapId); // swap.allowed can be the zero address and shoul not be trusted for validation expect(fetchedSwap.owner).to.be.deep.equals(zeroAddress); - const [fetchedAllowed, fetchedExpiry] = await Swaplace.parseData( + const [fetchedAllowed, fetchedExpiry, ,] = await Swaplace.decodeConfig( fetchedSwap.config, ); expect(fetchedAllowed).to.be.deep.equals(zeroAddress); diff --git a/test/utils/SwapFactory.ts b/test/utils/SwapFactory.ts index 9879aa8..1629d87 100644 --- a/test/utils/SwapFactory.ts +++ b/test/utils/SwapFactory.ts @@ -5,7 +5,7 @@ import { ethers } from "hardhat"; */ export interface Asset { addr: string; - amountOrId: bigint; + amountOrId: bigint | number; } /** @@ -13,17 +13,55 @@ export interface Asset { */ export interface Swap { owner: string; - config: number; + config: bigint; biding: Asset[]; asking: Asset[]; } +/** + * @dev See {ISwapFactory-encodeConfig}. + */ +export async function encodeConfig( + allowed: string, + expiry: bigint | number, + recipient: bigint | number, + value: bigint | number, +): Promise { + return ( + (BigInt(allowed) << BigInt(96)) | + (BigInt(expiry) << BigInt(64)) | + (BigInt(recipient) << BigInt(56)) | + BigInt(value) + ); +} + +/** + * @dev See {ISwapFactory-decodeConfig}. + */ +export async function decodeConfig(config: bigint): Promise<{ + allowed: string; + expiry: bigint | number; + recipient: bigint | number; + value: bigint | number; +}> { + // ethers check sum address + return { + allowed: + config >> BigInt(96) == BigInt(0) + ? ethers.constants.AddressZero + : ethers.utils.getAddress((config >> BigInt(96)).toString(16)), + expiry: (config >> BigInt(64)) & ((BigInt(1) << BigInt(32)) - BigInt(1)), + recipient: (config >> BigInt(56)) & ((BigInt(1) << BigInt(8)) - BigInt(1)), + value: config & ((BigInt(1) << BigInt(56)) - BigInt(1)), + }; +} + /** * @dev See {ISwapFactory-makeAsset}. */ export async function makeAsset( addr: string, - amountOrId: number | bigint, + amountOrId: bigint | number, ): Promise { // validate if its an ethereum address if (!ethers.utils.isAddress(addr)) { @@ -43,7 +81,7 @@ export async function makeAsset( */ const asset: Asset = { addr: addr, - amountOrId: typeof amountOrId == "number" ? BigInt(amountOrId) : amountOrId, + amountOrId: amountOrId, }; return asset; @@ -53,8 +91,8 @@ export async function makeAsset( * @dev See {ISwapFactory-makeSwap}. */ export async function makeSwap( - owner: any, - config: any, + owner: string, + config: bigint, biding: Asset[], asking: Asset[], ) { @@ -108,12 +146,12 @@ export async function makeSwap( * - `askingAddr` and `askingAmountOrId` must have the same length. */ export async function composeSwap( - owner: any, - config: any, - bidingAddr: any[], - bidingAmountOrId: any[], - askingAddr: any[], - askingAmountOrId: any[], + owner: string, + config: bigint, + bidingAddr: string[], + bidingAmountOrId: bigint[] | number[], + askingAddr: string[], + askingAmountOrId: bigint[] | number[], ) { // lenght of addresses and their respective amounts must be equal if ( @@ -124,12 +162,12 @@ export async function composeSwap( } // push new assets to the array of bids and asks - const biding: any[] = []; + const biding: Asset[] = []; bidingAddr.forEach(async (addr, index) => { biding.push(await makeAsset(addr, bidingAmountOrId[index])); }); - const asking: any[] = []; + const asking: Asset[] = []; askingAddr.forEach(async (addr, index) => { asking.push(await makeAsset(addr, askingAmountOrId[index])); }); @@ -141,4 +179,6 @@ module.exports = { makeAsset, makeSwap, composeSwap, + encodeConfig, + decodeConfig, }; From f79e1c68c1044b5e2cbbb9c6dfa863d7fd8ecb04 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 17 Apr 2024 15:02:51 +0700 Subject: [PATCH 51/91] feat: ERC1155 can be traded --- contracts/SwapFactory.sol | 26 ++++++++++++++++++++++++++ contracts/Swaplace.sol | 28 +++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/contracts/SwapFactory.sol b/contracts/SwapFactory.sol index 0293c98..d56531e 100644 --- a/contracts/SwapFactory.sol +++ b/contracts/SwapFactory.sol @@ -62,6 +62,32 @@ abstract contract SwapFactory is ISwapFactory, ISwap, IErrors { return Swap(owner, config, biding, asking); } + /** + * @dev See {ISwapFactory-encodeAsset}. + */ + function encodeAsset( + uint120 tokenId, + uint120 tokenAmount + ) public pure returns (uint256 amountOrId) { + return + (uint256(type(uint16).max) << 240) | + (uint256(tokenId) << 120) | + uint256(tokenAmount); + } + + /** + * @dev See {ISwapFactory-decodeAsset}. + */ + function decodeAsset( + uint256 amountOrId + ) public pure returns (uint16, uint256, uint256) { + return ( + uint16(amountOrId >> 240), + uint256(amountOrId >> 120), + uint256(amountOrId) + ); + } + /** * @dev See {ISwapFactory-encodeConfig}. */ diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index 9b4922e..b7128ba 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -125,7 +125,7 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { * @dev Transfer 'assets' from 'from' to 'to'. * The selector of the `transferFrom` function in bytes4: 0x23b872dd */ - function _transferFrom( + function _transferFrom2( address from, address to, Asset[] memory assets @@ -141,6 +141,32 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { } } + function _transferFrom( + address from, + address to, + Asset[] memory assets + ) internal { + for (uint256 i; i < assets.length; ) { + (uint16 assetType, uint256 tokenId, uint256 tokenAmount) = decodeAsset( + assets[i].amountOrId + ); + if (assetType == type(uint16).max) { + (bool success, ) = address(assets[i].addr).call( + abi.encodeWithSelector(0xf242432a, from, to, tokenId, tokenAmount, "") + ); + if (!success) revert InvalidCall(); + } else { + (bool success, ) = address(assets[i].addr).call( + abi.encodeWithSelector(0x23b872dd, from, to, tokenAmount) + ); + if (!success) revert InvalidCall(); + } + assembly { + i := add(i, 1) + } + } + } + /** * @dev See {IERC165-supportsInterface}. */ From 95f72f1576fcabcdca615c8c27603cf53744d2ea Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 17 Apr 2024 21:55:12 +0700 Subject: [PATCH 52/91] feat: encode and decode Asset --- contracts/SwapFactory.sol | 17 ++++++++++- contracts/Swaplace.sol | 22 +++----------- contracts/interfaces/ISwapFactory.sol | 41 +++++++++++++++++++++++++-- 3 files changed, 59 insertions(+), 21 deletions(-) diff --git a/contracts/SwapFactory.sol b/contracts/SwapFactory.sol index d56531e..9024c86 100644 --- a/contracts/SwapFactory.sol +++ b/contracts/SwapFactory.sol @@ -45,6 +45,17 @@ abstract contract SwapFactory is ISwapFactory, ISwap, IErrors { return Asset(addr, amountOrId); } + /** + * @dev See {ISwapFactory-make1155Asset}. + */ + function make1155Asset( + address addr, + uint120 tokenId, + uint120 tokenAmount + ) public pure virtual returns (Asset memory) { + return Asset(addr, encodeAsset(tokenId, tokenAmount)); + } + /** * @dev See {ISwapFactory-makeSwap}. */ @@ -80,7 +91,11 @@ abstract contract SwapFactory is ISwapFactory, ISwap, IErrors { */ function decodeAsset( uint256 amountOrId - ) public pure returns (uint16, uint256, uint256) { + ) + public + pure + returns (uint16 tokenType, uint256 tokenId, uint256 tokenAmount) + { return ( uint16(amountOrId >> 240), uint256(amountOrId >> 120), diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index b7128ba..24fce2f 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -122,25 +122,11 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { } /** - * @dev Transfer 'assets' from 'from' to 'to'. - * The selector of the `transferFrom` function in bytes4: 0x23b872dd + * @dev Transfer multiple 'assets' from 'from' to 'to'. + * + * `0x23b872dd` - Selector of the `transferFrom` function (ERC20, ERC721). + * `0xf242432a` - Selector of the `safeTransferFrom` function (ERC1155). */ - function _transferFrom2( - address from, - address to, - Asset[] memory assets - ) internal { - for (uint256 i; i < assets.length; ) { - (bool success, ) = address(assets[i].addr).call( - abi.encodeWithSelector(0x23b872dd, from, to, assets[i].amountOrId) - ); - if (!success) revert InvalidCall(); - assembly { - i := add(i, 1) - } - } - } - function _transferFrom( address from, address to, diff --git a/contracts/interfaces/ISwapFactory.sol b/contracts/interfaces/ISwapFactory.sol index 18aa5cc..a636c00 100644 --- a/contracts/interfaces/ISwapFactory.sol +++ b/contracts/interfaces/ISwapFactory.sol @@ -8,13 +8,26 @@ import {ISwap} from "./ISwap.sol"; */ interface ISwapFactory { /** - * @dev Constructs an {ISwap-Asset} struct to work with token standards. + * @dev Make an {ISwap-Asset} struct to work with token standards. */ function makeAsset( address addr, uint256 amountOrId ) external pure returns (ISwap.Asset memory); + /** + * @dev Make an {ISwap-Asset} struct to work with token standards. + * + * NOTE: Different from the {makeAsset} function, this function is used to + * encode the token ID and token amount into a single uint256. This is made + * to work with the ERC1155 standard. + */ + function make1155Asset( + address addr, + uint120 tokenId, + uint120 tokenAmount + ) external pure returns (ISwap.Asset memory); + /** * @dev Build a swap struct to use in the {Swaplace-createSwap} function. * @@ -33,7 +46,31 @@ interface ISwapFactory { ) external view returns (ISwap.Swap memory); /** - * @dev This function uses bitwise to return a uint256. + * @dev Encode `tokenId` and `tokenAmount` into a single uint256 while adding a flag + * to indicate that it's an ERC1155 token. + */ + function encodeAsset( + uint120 tokenId, + uint120 tokenAmount + ) external pure returns (uint256 amountOrId); + + /** + * @dev Decode `amountOrId` to check if the first 4 bytes are set to 0xFFFFFFFF. + * If the flag is set to 0xFFFFFFFF, then it's an ERC1155 standard, otherwise it's + * assumed to be an ERC20 or ERC721 standard. + * + * NOTE: If it's an ERC1155 token, then the next 120 bits are the token ID and the next + * 120 bits are the token amount. + */ + function decodeAsset( + uint256 amountOrId + ) + external + pure + returns (uint16 tokenType, uint256 tokenId, uint256 tokenAmount); + + /** + * @dev This function uses bitwise to return an encoded uint256 of the following parameters. */ function encodeConfig( address allowed, From 0f2a9f443ffb46b081e4b9b887d896b1b8c5d209 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 17 Apr 2024 22:29:26 +0700 Subject: [PATCH 53/91] fix: leading zero addresses incorrectly padded --- test/TestSwapFactory.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/test/TestSwapFactory.test.ts b/test/TestSwapFactory.test.ts index b434fb4..472bdb7 100644 --- a/test/TestSwapFactory.test.ts +++ b/test/TestSwapFactory.test.ts @@ -70,6 +70,7 @@ describe("Swaplace Factory", async function () { configOnChain, ); const decodedConfig = await decodeConfig(configOffChain); + expect(BigInt(allowed)).to.be.equals(decodedConfig.allowed); expect(expiry).to.be.equals(decodedConfig.expiry); expect(recipient).to.be.equals(decodedConfig.recipient); From fd56aa3e94731d830a2f2633b646ddc511dd4501 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 17 Apr 2024 23:17:21 +0700 Subject: [PATCH 54/91] fix: types was not adjusting to uint120 at first --- contracts/SwapFactory.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/SwapFactory.sol b/contracts/SwapFactory.sol index 9024c86..3c6e2e6 100644 --- a/contracts/SwapFactory.sol +++ b/contracts/SwapFactory.sol @@ -98,8 +98,8 @@ abstract contract SwapFactory is ISwapFactory, ISwap, IErrors { { return ( uint16(amountOrId >> 240), - uint256(amountOrId >> 120), - uint256(amountOrId) + uint256(uint120(amountOrId >> 120)), + uint256(uint120(amountOrId)) ); } From dd6f6631a5208fa2e5c69163f44c4f766199d338 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 17 Apr 2024 23:17:51 +0700 Subject: [PATCH 55/91] refactor: removed ITransfer as it's being used func selector --- contracts/Swaplace.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index 24fce2f..cebad6b 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -3,7 +3,6 @@ pragma solidity ^0.8.17; import {IERC165} from "./interfaces/IERC165.sol"; import {ISwaplace} from "./interfaces/ISwaplace.sol"; -import {ITransfer} from "./interfaces/ITransfer.sol"; import {SwapFactory} from "./SwapFactory.sol"; /** @@ -136,6 +135,7 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { (uint16 assetType, uint256 tokenId, uint256 tokenAmount) = decodeAsset( assets[i].amountOrId ); + if (assetType == type(uint16).max) { (bool success, ) = address(assets[i].addr).call( abi.encodeWithSelector(0xf242432a, from, to, tokenId, tokenAmount, "") @@ -147,6 +147,7 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { ); if (!success) revert InvalidCall(); } + assembly { i := add(i, 1) } From 7d5b4ccd53d64a946662f8fab8f4e016da3c099c Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 17 Apr 2024 23:18:04 +0700 Subject: [PATCH 56/91] delete: ITransfer sol file --- contracts/interfaces/ITransfer.sol | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 contracts/interfaces/ITransfer.sol diff --git a/contracts/interfaces/ITransfer.sol b/contracts/interfaces/ITransfer.sol deleted file mode 100644 index cfde089..0000000 --- a/contracts/interfaces/ITransfer.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.17; - -/** - * @dev Generalized Interface for {IERC20} and {IERC721} `transferFrom` functions. - */ -interface ITransfer { - /** - * @dev See {IERC20-transferFrom} or {IERC721-transferFrom}. - * - * Moves an `amount` for ERC20 or `tokenId` for ERC721 from `from` to `to`. - * - * Emits a {Transfer} event. - */ - function transferFrom(address from, address to, uint256 amountOrId) external; -} From 705084ab011833eaccd0b3d228f6e73614460dc8 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 17 Apr 2024 23:19:14 +0700 Subject: [PATCH 57/91] fix: incorrect padding when address has leading zeroes --- test/utils/SwapFactory.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/utils/SwapFactory.ts b/test/utils/SwapFactory.ts index 1629d87..fd721a2 100644 --- a/test/utils/SwapFactory.ts +++ b/test/utils/SwapFactory.ts @@ -44,12 +44,13 @@ export async function decodeConfig(config: bigint): Promise<{ recipient: bigint | number; value: bigint | number; }> { - // ethers check sum address return { allowed: config >> BigInt(96) == BigInt(0) ? ethers.constants.AddressZero - : ethers.utils.getAddress((config >> BigInt(96)).toString(16)), + : ethers.utils.getAddress( + `0x${(config >> BigInt(96)).toString(16).padStart(40, "0")}`, + ), expiry: (config >> BigInt(64)) & ((BigInt(1) << BigInt(32)) - BigInt(1)), recipient: (config >> BigInt(56)) & ((BigInt(1) << BigInt(8)) - BigInt(1)), value: config & ((BigInt(1) << BigInt(56)) - BigInt(1)), From ef23c09b735cdddcf1fa8925902817e733fc677f Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 17 Apr 2024 23:19:31 +0700 Subject: [PATCH 58/91] feat: tests with ERC1155 --- test/TestSwaplace.test.ts | 67 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index 8feccd1..35e6c5b 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -10,6 +10,7 @@ describe("Swaplace", async function () { let Swaplace: Contract; let MockERC20: Contract; let MockERC721: Contract; + let MockERC1155: Contract; // The signers of the test let deployer: SignerWithAddress; @@ -58,10 +59,76 @@ describe("Swaplace", async function () { Swaplace = await deploy("Swaplace", deployer); MockERC20 = await deploy("MockERC20", deployer); MockERC721 = await deploy("MockERC721", deployer); + MockERC1155 = await deploy("MockERC1155", deployer); }); describe("Creating Swaps", () => { context("Creating different types of Swaps", () => { + it("Should be able to create a 1-1 swap with ERC1155", async function () { + const bidingAddr = [MockERC1155.address]; + const tokenId = 1; + const amount = 3; + const amountAndId = await Swaplace.encodeAsset(tokenId, amount); + const bidingAmountOrId = [amountAndId]; + + const askingAddr = [MockERC721.address]; + const askingAmountOrId = [50]; + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + allowed.address, + currentTimestamp, + 0, + 0, + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await MockERC1155.mint(owner.address, tokenId, amount); + await MockERC1155.connect(owner).setApprovalForAll( + Swaplace.address, + true, + ); + await MockERC721.mint(allowed.address, askingAmountOrId[0]); + await MockERC721.connect(allowed).approve( + Swaplace.address, + askingAmountOrId[0], + ); + + const nextSwapId = Number(await Swaplace.totalSwaps()) + 1; + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs(nextSwapId, owner.address, allowed.address); + + await expect( + await Swaplace.connect(allowed).acceptSwap( + nextSwapId, + receiver.address, + ), + ) + .to.emit(Swaplace, "SwapAccepted") + .withArgs(nextSwapId, owner.address, allowed.address); + + expect( + await MockERC1155.balanceOf(owner.address, tokenId), + ).to.be.equals(0); + expect( + await MockERC1155.balanceOf(receiver.address, tokenId), + ).to.be.equals(amount); + expect(await MockERC721.ownerOf(askingAmountOrId[0])).to.be.equals( + owner.address, + ); + expect(await MockERC721.balanceOf(allowed.address)).to.be.equals(0); + }); + it("Should be able to create a 1-1 swap with ERC20", async function () { const bidingAddr = [MockERC20.address]; const bidingAmountOrId = [50]; From 3ee46605778183b8c01c0e0194ce6a508dbdfd49 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Wed, 17 Apr 2024 23:32:51 +0700 Subject: [PATCH 59/91] docs: explaining support for ERC1155 --- contracts/SwapFactory.sol | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/contracts/SwapFactory.sol b/contracts/SwapFactory.sol index 3c6e2e6..9fc5710 100644 --- a/contracts/SwapFactory.sol +++ b/contracts/SwapFactory.sol @@ -26,13 +26,11 @@ import {ISwapFactory} from "./interfaces/ISwapFactory.sol"; * The Swap struct uses an {Asset} struct to represent the asset. This struct is * composed of: * - * - The `address` of the asset. This address can be from an ERC20 or ERC721 contract. + * - The `address` of the token asset. * - The `amount` or `id` of the asset. This amount can be the amount of ERC20 tokens - * or the ID of an ERC721 token. - * - * To use other standards, like ERC1155, you can wrap the ownership of the asset - * in an a trusted contract and Swap as an ERC721. This way, you can tokenize any - * on-chain execution and trade on Swaplace. + * or the NFT ID of an ERC721. + * - The `amount` and `id` can be encoded together in a single uint256, allowing the + * ERC1155 tokens to be swapped. */ abstract contract SwapFactory is ISwapFactory, ISwap, IErrors { /** From e123c977cc27ab8028ab5591e70b6d28969fe610 Mon Sep 17 00:00:00 2001 From: Deepu Date: Wed, 1 May 2024 23:30:00 +0530 Subject: [PATCH 60/91] closes #205 --- test/TestSwapFactory.test.ts | 95 +++++++++++++++++++++++++++++++++++- 1 file changed, 94 insertions(+), 1 deletion(-) diff --git a/test/TestSwapFactory.test.ts b/test/TestSwapFactory.test.ts index 472bdb7..b04fcc5 100644 --- a/test/TestSwapFactory.test.ts +++ b/test/TestSwapFactory.test.ts @@ -1,5 +1,5 @@ import { expect } from "chai"; -import { Contract } from "ethers"; +import { BigNumber, Contract } from "ethers"; import { ethers } from "hardhat"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { @@ -156,6 +156,61 @@ describe("Swaplace Factory", async function () { ); }); + it("Should be able to {makeSwap} with native ethers value", async function () { + const currentTimestamp = (await blocktimestamp()) + 2000000; + + const ERC20Asset: Asset = await makeAsset(MockERC20.address, 1000); + const ERC721Asset: Asset = await makeAsset(MockERC721.address, 1); + + const valueToSend: BigNumber = ethers.utils.parseEther("0.2"); + + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 1, + valueToSend.div(1e12), + ); + + const swap = await makeSwap( + owner.address, + config, + [ERC20Asset], + [ERC721Asset], + ); + + const onChainSwap = await Swaplace.makeSwap( + owner.address, + zeroAddress, + currentTimestamp, + 1, + valueToSend.div(1e12), + [ERC20Asset], + [ERC721Asset], + ); + + const [allowed, expiry, recipient, value] = await Swaplace.decodeConfig( + swap.config, + ); + const [onChainAllowed, onChainExpiry, onChainRecipient, onChainValue] = + await Swaplace.decodeConfig(onChainSwap.config); + + expect(swap.owner).to.be.equals(onChainSwap.owner); + expect(expiry).to.be.equals(onChainExpiry); + expect(allowed).to.be.equals(onChainAllowed); + expect(recipient).to.be.equals(onChainRecipient); + expect(value).to.be.equals(onChainValue); + + expect(swap.biding[0].addr).to.be.equals(onChainSwap.biding[0].addr); + expect(swap.biding[0].amountOrId).to.be.equals( + onChainSwap.biding[0].amountOrId, + ); + + expect(swap.asking[0].addr).to.be.equals(onChainSwap.asking[0].addr); + expect(swap.asking[0].amountOrId).to.be.equals( + onChainSwap.asking[0].amountOrId, + ); + }); + it("Should be able to {makeSwap} with multiple assets", async function () { const currentTimestamp = (await blocktimestamp()) + 2000000; @@ -218,6 +273,44 @@ describe("Swaplace Factory", async function () { expect(expiry).to.be.equals(expiry); }); + it("Should be able to {composeSwap} using native ethers value", async function () { + const currentTimestamp = (await blocktimestamp()) + 2000000; + + const bidingAddr = [MockERC20.address, MockERC721.address]; + const bidingAmountOrId = [1000, 1]; + + const askingAddr = [MockERC721.address]; + const askingAmountOrId = [2]; + + const valueToSend: BigNumber = ethers.utils.parseEther("1"); + + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + valueToSend.div(1e12), + ); + + const swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + const [allowed, expiry, recipient, value] = await Swaplace.decodeConfig( + swap.config, + ); + + expect(swap.owner).to.be.equals(owner.address); + expect(allowed).to.be.equals(zeroAddress); + expect(expiry).to.be.equals(expiry); + expect(recipient).to.be.equals(0); + expect(value).to.be.equals(valueToSend.div(1e12)); + }); + it("Should revert using {composeSwap} without minimum expiry", async function () { const expiry = 0; From b46209ec1c92d240074aeda0911bda65a20959e4 Mon Sep 17 00:00:00 2001 From: Deepu Date: Thu, 9 May 2024 14:38:46 +0530 Subject: [PATCH 61/91] closes #214 --- contracts/Swaplace.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index cebad6b..d105556 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -46,7 +46,7 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { uint256 swapId = _totalSwaps; _swaps[swapId] = swap; - (address allowed, , uint8 recipient, uint56 value) = decodeConfig( + (address allowed, , uint8 recipient, uint256 value) = decodeConfig( swap.config ); @@ -72,7 +72,7 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { address allowed, uint32 expiry, uint8 recipient, - uint56 value + uint256 value ) = decodeConfig(swap.config); if (allowed != address(0) && allowed != msg.sender) revert InvalidAddress(); @@ -100,7 +100,7 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { Swap memory swap = _swaps[swapId]; if (swap.owner != msg.sender) revert InvalidAddress(); - (, uint32 expiry, uint8 recipient, uint56 value) = decodeConfig( + (, uint32 expiry, uint8 recipient, uint256 value) = decodeConfig( swap.config ); From 18d04d18a1b701ca88aa1a5efd711a1916f5b603 Mon Sep 17 00:00:00 2001 From: Deepu Date: Fri, 10 May 2024 18:38:45 +0530 Subject: [PATCH 62/91] closes #206 --- test/TestSwaplace.test.ts | 235 +++++++++++++++++++++++++++++++++++++- 1 file changed, 234 insertions(+), 1 deletion(-) diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index 35e6c5b..414307b 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -1,5 +1,5 @@ import { expect } from "chai"; -import { Contract } from "ethers"; +import { BigNumber, Contract } from "ethers"; import { ethers, network } from "hardhat"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { Asset, Swap, composeSwap } from "./utils/SwapFactory"; @@ -326,6 +326,44 @@ describe("Swaplace", async function () { .to.emit(Swaplace, "SwapCreated") .withArgs(await Swaplace.totalSwaps(), owner.address, zeroAddress); }); + + it("Should be able to create a swap with native ethers", async function () { + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [50]; + + const askingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const askingAmountOrId = [50, 100, 150]; + + const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + valueToSend.div(1e12), + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + await expect( + await Swaplace.connect(owner).createSwap(swap, { + value: valueToSend, + }), + ) + .to.emit(Swaplace, "SwapCreated") + .withArgs(await Swaplace.totalSwaps(), owner.address, zeroAddress); + }); }); context("Reverts when creating Swaps", () => { @@ -335,6 +373,40 @@ describe("Swaplace", async function () { Swaplace.connect(allowed).createSwap(swap), ).to.be.revertedWithCustomError(Swaplace, `InvalidAddress`); }); + + it("Should revert when the wrong amount of ethers is sent by the {owner}", async function () { + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [50]; + + const askingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const askingAmountOrId = [50, 100, 150]; + + const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + valueToSend.div(1e12), + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + await expect( + Swaplace.connect(owner).createSwap(swap, { value: 69 }), + ).to.be.revertedWithCustomError(Swaplace, `InvalidValue`); + }); }); }); @@ -466,6 +538,69 @@ describe("Swaplace", async function () { allowed.address, ); }); + + it("Should be able to {acceptSwap} with native ethers", async function () { + await MockERC20.mint(owner.address, 1000); + await MockERC721.mint(allowed.address, 10); + + await MockERC20.connect(owner).approve(Swaplace.address, 1000); + await MockERC721.connect(allowed).approve(Swaplace.address, 10); + + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [50]; + + const askingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const askingAmountOrId = [50, 100, 150]; + + const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); + + const expiry = (await blocktimestamp()) + 1000000; + + const config = await Swaplace.encodeConfig( + allowed.address, + expiry, + 0, + valueToSend.div(1e12), + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await expect( + await Swaplace.connect(owner).createSwap(swap, { + value: valueToSend, + }), + ) + .to.emit(Swaplace, "SwapCreated") + .withArgs( + await Swaplace.totalSwaps(), + owner.address, + allowed.address, + ); + + await expect( + await Swaplace.connect(allowed).acceptSwap( + await Swaplace.totalSwaps(), + receiver.address, + ), + ) + .to.emit(Swaplace, "SwapAccepted") + .withArgs( + await Swaplace.totalSwaps(), + owner.address, + allowed.address, + ); + }); }); context("Reverts when accepting Swaps", () => { @@ -553,6 +688,60 @@ describe("Swaplace", async function () { ), ).to.be.revertedWithCustomError(Swaplace, "InvalidAddress"); }); + + it("Should revert when wrong amount of ethers are sent by the {acceptee}", async function () { + await MockERC20.mint(owner.address, 1000); + await MockERC721.mint(allowed.address, 10); + + await MockERC20.connect(owner).approve(Swaplace.address, 1000); + await MockERC721.connect(allowed).approve(Swaplace.address, 10); + + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [50]; + + const askingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const askingAmountOrId = [50, 100, 150]; + + const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); + + const expiry = (await blocktimestamp()) + 1000000; + + const config = await Swaplace.encodeConfig( + allowed.address, + expiry, + 1, + valueToSend.div(1e12), + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs( + await Swaplace.totalSwaps(), + owner.address, + allowed.address, + ); + + await expect( + Swaplace.connect(allowed).acceptSwap( + await Swaplace.totalSwaps(), + receiver.address, + { value: 69 }, + ), + ).to.be.revertedWithCustomError(Swaplace, "InvalidValue"); + }); }); }); @@ -578,6 +767,50 @@ describe("Swaplace", async function () { Swaplace.connect(owner).acceptSwap(lastSwap, receiver.address), ).to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`); }); + + it("Should be able to {cancelSwap} and return ethers to {owner}", async function () { + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [50]; + + const askingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const askingAmountOrId = [50, 100, 150]; + + const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + valueToSend.div(1e12), + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + const lastSwap = await Swaplace.totalSwaps(); + await expect( + await Swaplace.connect(owner).createSwap(swap, { + value: valueToSend, + }), + ) + .to.emit(Swaplace, "SwapCreated") + .withArgs(await Swaplace.totalSwaps(), owner.address, zeroAddress); + + await expect(Swaplace.connect(owner).cancelSwap(lastSwap)) + .to.emit(Swaplace, "SwapCanceled") + .withArgs(lastSwap, owner.address); + }); }); context("Reverts when canceling Swaps", () => { From 5e91f545c4fb6b8f592aa38ad5580f030327272c Mon Sep 17 00:00:00 2001 From: Deepu Date: Fri, 10 May 2024 22:08:43 +0530 Subject: [PATCH 63/91] closes #207 --- test/TestSwapFactory.test.ts | 113 +++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) diff --git a/test/TestSwapFactory.test.ts b/test/TestSwapFactory.test.ts index b04fcc5..12e88f0 100644 --- a/test/TestSwapFactory.test.ts +++ b/test/TestSwapFactory.test.ts @@ -9,6 +9,7 @@ import { composeSwap, encodeConfig, decodeConfig, + Swap, } from "./utils/SwapFactory"; import { blocktimestamp, deploy } from "./utils/utils"; @@ -17,6 +18,7 @@ describe("Swaplace Factory", async function () { let Swaplace: Contract; let MockERC20: Contract; let MockERC721: Contract; + let MockERC1155: Contract; // The signers of the test let deployer: SignerWithAddress; @@ -30,6 +32,7 @@ describe("Swaplace Factory", async function () { Swaplace = await deploy("Swaplace", deployer); MockERC20 = await deploy("MockERC20", deployer); MockERC721 = await deploy("MockERC721", deployer); + MockERC1155 = await deploy("MockERC1155", deployer); }); it("Should be able to {makeAsset} for ERC20 and ERC721", async function () { @@ -241,6 +244,116 @@ describe("Swaplace Factory", async function () { expect(swap.asking[1]).to.be.equals(ERC721Asset); }); + it("Should be able to {makeSwap} with ERC1155 tokens", async function () { + const bidingAddr = [MockERC1155.address]; + const tokenId = 1; + const amount = 3; + const amountAndId = await Swaplace.encodeAsset(tokenId, amount); + const bidingAmountOrId = [amountAndId]; + + const askingAddr = [MockERC721.address]; + const askingAmountOrId = [50]; + + const ERC1155Asset: Asset = await makeAsset( + bidingAddr[0], + bidingAmountOrId[0], + ); + const ERC721Asset: Asset = await makeAsset( + askingAddr[0], + askingAmountOrId[0], + ); + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); + + const swap = await makeSwap( + owner.address, + config, + [ERC1155Asset], + [ERC721Asset], + ); + + const onChainSwap = await Swaplace.makeSwap( + owner.address, + zeroAddress, + currentTimestamp, + 0, + 0, + [ERC1155Asset], + [ERC721Asset], + ); + + const [allowed, expiry, recipient, value] = await Swaplace.decodeConfig( + swap.config, + ); + + const [onChainAllowed, onChainExpiry, onChainRecipient, onChainValue] = + await Swaplace.decodeConfig(onChainSwap.config); + + expect(swap.owner).to.be.equals(onChainSwap.owner); + expect(expiry).to.be.equals(onChainExpiry); + expect(allowed).to.be.equals(onChainAllowed); + expect(recipient).to.be.equals(onChainRecipient); + expect(value).to.be.equals(onChainValue); + expect(swap.biding[0].addr).to.be.equals(onChainSwap.biding[0].addr); + expect(swap.biding[0].amountOrId).to.be.equals( + onChainSwap.biding[0].amountOrId, + ); + + expect(swap.asking[0].addr).to.be.equals(onChainSwap.asking[0].addr); + expect(swap.asking[0].amountOrId).to.be.equals( + onChainSwap.asking[0].amountOrId, + ); + }); + + it("Should be able to {composeSwap} using ERC1155", async function () { + const bidingAddr = [MockERC1155.address]; + const tokenId = 1; + const amount = 3; + const amountAndId = await Swaplace.encodeAsset(tokenId, amount); + const bidingAmountOrId = [amountAndId]; + + const askingAddr = [MockERC721.address]; + const askingAmountOrId = [50]; + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + 0, + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + const [allowed, expiry, recipient, value] = await Swaplace.decodeConfig( + swap.config, + ); + + expect(swap.owner).to.be.equals(owner.address); + expect(allowed).to.be.equals(zeroAddress); + expect(expiry).to.be.equals(expiry); + expect(recipient).to.be.equals(0); + expect(value).to.be.equals(0); + expect(swap.biding[0].addr).to.be.equals(bidingAddr[0]); + expect(swap.biding[0].amountOrId).to.be.equals(bidingAmountOrId[0]); + + expect(swap.asking[0].addr).to.be.equals(askingAddr[0]); + expect(swap.asking[0].amountOrId).to.be.equals(askingAmountOrId[0]); + }); + it("Should be able to {composeSwap} using both ERC20, ERC721", async function () { const currentTimestamp = (await blocktimestamp()) + 2000000; From 5ba014991fe03605580157a8a57c6cac56a28fa3 Mon Sep 17 00:00:00 2001 From: Deepu Date: Sat, 11 May 2024 00:01:37 +0530 Subject: [PATCH 64/91] closes #208 --- test/TestSwaplace.test.ts | 184 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 184 insertions(+) diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index 35e6c5b..e12ca1c 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -129,6 +129,190 @@ describe("Swaplace", async function () { expect(await MockERC721.balanceOf(allowed.address)).to.be.equals(0); }); + it("Should be able to create a 1-N swap with ERC1155", async function () { + const bidingAddr = [ + MockERC1155.address, + MockERC1155.address, + MockERC1155.address, + ]; + const tokenId1 = 69; + const amount1 = 3; + const tokenId2 = 2; + const amount2 = 6; + const tokenId3 = 3; + const amount3 = 9; + const amountAndId1 = await Swaplace.encodeAsset(tokenId1, amount1); + const amountAndId2 = await Swaplace.encodeAsset(tokenId2, amount2); + const amountAndId3 = await Swaplace.encodeAsset(tokenId3, amount3); + const bidingAmountOrId = [amountAndId1, amountAndId2, amountAndId3]; + + const askingAddr = [MockERC721.address]; + const askingAmountOrId = [69]; + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + allowed.address, + currentTimestamp, + 0, + 0, + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await MockERC1155.mint(owner.address, tokenId1, amount1); + await MockERC1155.mint(owner.address, tokenId2, amount2); + await MockERC1155.mint(owner.address, tokenId3, amount3); + await MockERC1155.connect(owner).setApprovalForAll( + Swaplace.address, + true, + ); + await MockERC721.mint(allowed.address, askingAmountOrId[0]); + await MockERC721.connect(allowed).approve( + Swaplace.address, + askingAmountOrId[0], + ); + + const nextSwapId = Number(await Swaplace.totalSwaps()) + 1; + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs(nextSwapId, owner.address, allowed.address); + + await expect( + await Swaplace.connect(allowed).acceptSwap( + nextSwapId, + receiver.address, + ), + ) + .to.emit(Swaplace, "SwapAccepted") + .withArgs(nextSwapId, owner.address, allowed.address); + + expect( + await MockERC1155.balanceOf(owner.address, tokenId1), + ).to.be.equals(0); + expect( + await MockERC1155.balanceOf(owner.address, tokenId2), + ).to.be.equals(0); + expect( + await MockERC1155.balanceOf(owner.address, tokenId3), + ).to.be.equals(0); + expect( + await MockERC1155.balanceOf(receiver.address, tokenId1), + ).to.be.equals(amount1); + expect( + await MockERC1155.balanceOf(receiver.address, tokenId2), + ).to.be.equals(amount2); + expect( + await MockERC1155.balanceOf(receiver.address, tokenId3), + ).to.be.equals(amount3); + expect(await MockERC721.ownerOf(askingAmountOrId[0])).to.be.equals( + owner.address, + ); + expect(await MockERC721.balanceOf(allowed.address)).to.be.equals(0); + }); + + it("Should be able to create a N-N swap with ERC1155", async function () { + const bidingAddr = [ + MockERC1155.address, + MockERC1155.address, + MockERC1155.address, + ]; + const tokenId1 = 4; + const amount1 = 69; + const tokenId2 = 5; + const amount2 = 69; + const tokenId3 = 6; + const amount3 = 69; + const amountAndId1 = await Swaplace.encodeAsset(tokenId1, amount1); + const amountAndId2 = await Swaplace.encodeAsset(tokenId2, amount2); + const amountAndId3 = await Swaplace.encodeAsset(tokenId3, amount3); + const bidingAmountOrId = [amountAndId1, amountAndId2, amountAndId3]; + + const askingAddr = [ + MockERC721.address, + MockERC721.address, + MockERC721.address, + ]; + const askingAmountOrId = [59, 79, 89]; + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + allowed.address, + currentTimestamp, + 0, + 0, + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await MockERC1155.mint(owner.address, tokenId1, amount1); + await MockERC1155.mint(owner.address, tokenId2, amount2); + await MockERC1155.mint(owner.address, tokenId3, amount3); + await MockERC1155.connect(owner).setApprovalForAll( + Swaplace.address, + true, + ); + await MockERC721.mint(allowed.address, askingAmountOrId[0]); + await MockERC721.mint(allowed.address, askingAmountOrId[1]); + await MockERC721.mint(allowed.address, askingAmountOrId[2]); + await MockERC721.connect(allowed).setApprovalForAll( + Swaplace.address, + true, + ); + + const nextSwapId = Number(await Swaplace.totalSwaps()) + 1; + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs(nextSwapId, owner.address, allowed.address); + + await expect( + await Swaplace.connect(allowed).acceptSwap( + nextSwapId, + receiver.address, + ), + ) + .to.emit(Swaplace, "SwapAccepted") + .withArgs(nextSwapId, owner.address, allowed.address); + + expect( + await MockERC1155.balanceOf(owner.address, tokenId1), + ).to.be.equals(0); + expect( + await MockERC1155.balanceOf(owner.address, tokenId2), + ).to.be.equals(0); + expect( + await MockERC1155.balanceOf(owner.address, tokenId3), + ).to.be.equals(0); + expect( + await MockERC1155.balanceOf(receiver.address, tokenId1), + ).to.be.equals(amount1); + expect( + await MockERC1155.balanceOf(receiver.address, tokenId2), + ).to.be.equals(amount2); + expect( + await MockERC1155.balanceOf(receiver.address, tokenId3), + ).to.be.equals(amount3); + expect(await MockERC721.ownerOf(askingAmountOrId[0])).to.be.equals( + owner.address, + ); + expect(await MockERC721.balanceOf(allowed.address)).to.be.equals(0); + }); + it("Should be able to create a 1-1 swap with ERC20", async function () { const bidingAddr = [MockERC20.address]; const bidingAmountOrId = [50]; From 1da56f7de2167b2a0da76b17759f4d0bada823d8 Mon Sep 17 00:00:00 2001 From: Rafael Sanches Date: Fri, 10 May 2024 17:57:37 -0300 Subject: [PATCH 65/91] feat: Kakarot rpc and ipfs on mockerc721 --- .env.sample | 1 + contracts/mock/MockERC721.sol | 4 ++-- hardhat.config.ts | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.env.sample b/.env.sample index 9c426f7..18dd70c 100644 --- a/.env.sample +++ b/.env.sample @@ -28,6 +28,7 @@ SEPOLIA_RPC_URL=https://ethereum-sepolia.publicnode.com MUMBAI_RPC_URL=https://polygon-mumbai-bor.publicnode.com FUJI_RPC_URL=https://avalanche-fuji-c-chain.publicnode.com BNB_TESTNET_RPC_URL=https://bsc-testnet.publicnode.com +KAKAROT_SEPOLIA_RPC_URL=https://sepolia-rpc.kakarot.org # MAINNETS ETH_RPC_URL="https://eth.llamarpc.com" diff --git a/contracts/mock/MockERC721.sol b/contracts/mock/MockERC721.sol index f9e63ff..80bbfc8 100644 --- a/contracts/mock/MockERC721.sol +++ b/contracts/mock/MockERC721.sol @@ -16,6 +16,6 @@ contract MockERC721 is ERC721 { function tokenURI( uint256 ) public view virtual override returns (string memory) { - return "ipfs://QmQJnHseE9VPw5qVxuEhxTiZ7avzgkCdFz69rg86UvTZdk/"; + return "ipfs://QmWodCkovJk18U75g8Veg6rCnw7951vQvTjYfS7J3nMFma/"; } -} +} \ No newline at end of file diff --git a/hardhat.config.ts b/hardhat.config.ts index 5932a6c..5e9334d 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -27,7 +27,7 @@ const config: HardhatUserConfig = { * @dev Testnets */ kakarot: { - url: `${process.env.KAKAROT_RPC_URL}`, + url: `${process.env.KAKAROT_SEPOLIA_RPC_URL}`, accounts: [`${DEPLOYER_PRIVATE_KEY}`], }, sepolia: { From 9ffdc10ad3d58223b742da7e60609065313d036a Mon Sep 17 00:00:00 2001 From: Deepu Date: Mon, 13 May 2024 21:10:50 +0530 Subject: [PATCH 66/91] closes #210 --- scripts/deployMock.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/scripts/deployMock.ts b/scripts/deployMock.ts index f8266d3..660c10e 100644 --- a/scripts/deployMock.ts +++ b/scripts/deployMock.ts @@ -11,6 +11,7 @@ async function main() { /// @dev The returned contract instance that will be deployed via the deploy function in utils. let MockERC20: Contract; let MockERC721: Contract; + let MockERC1155: Contract; /// @dev will throw an error if any of the accounts was not set up correctly. try { @@ -27,6 +28,7 @@ async function main() { // We are deploying both contracts to test the user flux with the entire functionality. MockERC20 = await deploy("MockERC20", signers[0]); MockERC721 = await deploy("MockERC721", signers[0]); + MockERC1155 = await deploy("MockERC1155", signers[0]); // @dev Log Contract address and the Tx hash which can be searched on Etherscan (or any other block explorer). console.log( @@ -43,13 +45,22 @@ async function main() { MockERC721.deployTransaction.hash, ); + console.log( + "\nContract %s \nDeployed to %s \nAt Tx %s\n", + "MockERC1155", + MockERC1155.address, + MockERC1155.deployTransaction.hash, + ); + /// @dev Store the contract addresses in the .env file. await storeEnv(MockERC20.address, "ERC20_ADDRESS", true); await storeEnv(MockERC721.address, "ERC721_ADDRESS", true); + await storeEnv(MockERC1155.address, "ERC1155_ADDRESS", true); /// @dev Awaits for the transaction to be mined. await MockERC20.deployed(); await MockERC721.deployed(); + await MockERC1155.deployed(); } main().catch((error) => { From bb4b941898ff6b669140ad8556f8302633a461bf Mon Sep 17 00:00:00 2001 From: Deepu Date: Tue, 14 May 2024 00:19:05 +0530 Subject: [PATCH 67/91] closes #211 --- scripts/approve.ts | 19 +++++++++++++++++-- scripts/mint.ts | 20 ++++++++++++++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/scripts/approve.ts b/scripts/approve.ts index 5137afd..34faa3c 100755 --- a/scripts/approve.ts +++ b/scripts/approve.ts @@ -10,12 +10,18 @@ async function main() { /// @dev This is the list of mock deployments addresses that were stored in the `.env` file. const ERC20_ADDRESS = process.env.ERC20_ADDRESS || 0x0; const ERC721_ADDRESS = process.env.ERC721_ADDRESS || 0x0; + const ERC1155_ADDRESS = process.env.ERC1155_ADDRESS || 0x0; /// @dev The Swaplace address also needs to be instance to receive the approvals. const SWAPLACE_ADDRESS = process.env.SWAPLACE_ADDRESS || 0x0; /// @dev Will throw an error if any of the addresses were not set in the `.env` file. - if (!ERC20_ADDRESS || !ERC721_ADDRESS || !SWAPLACE_ADDRESS) { + if ( + !ERC20_ADDRESS || + !ERC721_ADDRESS || + !SWAPLACE_ADDRESS || + !ERC1155_ADDRESS + ) { throw new Error( - "Invalid ERC20, ERC721 or Swaplace address, please check if the addresse in the `.env` file is set up correctly.", + "Invalid ERC20, ERC721, ERC1155 or Swaplace address, please check if the addresse in the `.env` file is set up correctly.", ); } @@ -39,6 +45,7 @@ async function main() { /// @dev The returned contract instance that will be deployed via the deploy function in utils. let MockERC20: Contract; let MockERC721: Contract; + let MockERC1155: Contract; /// @dev will throw an error if any of the accounts was not set up correctly. try { @@ -63,6 +70,11 @@ async function main() { ERC721_ADDRESS, signers[0], ); + MockERC1155 = await ethers.getContractAt( + "MockERC1155", + ERC1155_ADDRESS, + signers[0], + ); } catch (error) { throw new Error( `Error deploying one of the Mock Contracts. @@ -76,11 +88,13 @@ async function main() { /// @dev Responses from the minting transactions. let txErc20; let txErc721; + let txErc1155; /// @dev We are approving the signer address to spend the amount of tokens. try { txErc20 = await MockERC20.approve(SWAPLACE_ADDRESS, amount); txErc721 = await MockERC721.approve(SWAPLACE_ADDRESS, tokenId); + txErc1155 = await MockERC1155.setApprovalForAll(SWAPLACE_ADDRESS, true); } catch (error) { throw new Error( `Error while approving the tokens. Make sure that the approve function is @@ -97,6 +111,7 @@ async function main() { tokenId, txErc721.hash, ); + console.log("\nERC1155 Approved all tokens \nAt Tx %s", txErc1155.hash); } main().catch((error) => { diff --git a/scripts/mint.ts b/scripts/mint.ts index f96d286..64ee9a0 100644 --- a/scripts/mint.ts +++ b/scripts/mint.ts @@ -11,10 +11,11 @@ async function main() { /// @dev This is the list of mock deployments addresses that were stored in the `.env` file. const ERC20_ADDRESS = process.env.ERC20_ADDRESS || 0x0; const ERC721_ADDRESS = process.env.ERC721_ADDRESS || 0x0; + const ERC1155_ADDRESS = process.env.ERC1155_ADDRESS || 0x0; /// @dev Will throw an error if any of the addresses were not set in the `.env` file. - if (!ERC20_ADDRESS || !ERC721_ADDRESS) { + if (!ERC20_ADDRESS || !ERC721_ADDRESS || !ERC1155_ADDRESS) { throw new Error( - "Invalid ERC20 or ERC721 address, please check if the addresse in the `.env` file is set up correctly.", + "Invalid ERC20 or ERC721 or ERC1155 address, please check if the addresses in the `.env` file are set up correctly.", ); } @@ -25,6 +26,7 @@ async function main() { /// @dev The returned contract instance that will be deployed via the deploy function in utils. let MockERC20: Contract; let MockERC721: Contract; + let MockERC1155: Contract; /// @dev will throw an error if any of the accounts was not set up correctly. try { @@ -49,6 +51,11 @@ async function main() { ERC721_ADDRESS, signers[0], ); + MockERC1155 = await ethers.getContractAt( + "MockERC1155", + ERC1155_ADDRESS, + signers[0], + ); } catch (error) { throw new Error( `Error deploying one of the Mock Contracts. @@ -69,6 +76,7 @@ async function main() { /// @dev Responses from the minting transactions. let txErc20; let txErc721; + let txErc1155; /// @dev Minting function will throw an error if the minting fails. /// We are minting for the first signer of `hardhat.config.ts` 1000 @@ -77,6 +85,7 @@ async function main() { try { txErc20 = await MockERC20.mint(signers[0].address, amount); txErc721 = await MockERC721.mint(signers[0].address, tokenId); + txErc1155 = await MockERC1155.mint(signers[0].address, tokenId, amount); } catch (error) { throw new Error( `Error while minting tokens. Make sure that the minting function is @@ -93,12 +102,19 @@ async function main() { tokenId, txErc721.hash, ); + console.log( + "\nERC1155 Minted %s tokens with ID #%s \nAt Tx %s", + amount, + tokenId, + txErc1155.hash, + ); await storeEnv(tokenId, "TOKEN_ID", false); /// @dev Awaits for the transaction to be mined. await txErc20.wait(); await txErc721.wait(); + await txErc1155.wait(); } main().catch((error) => { From 4a2849fd34063fb8a768763a2fe7c1572a16666f Mon Sep 17 00:00:00 2001 From: Deepu Date: Sat, 18 May 2024 00:09:00 +0530 Subject: [PATCH 68/91] closes #212 --- scripts/createSwap.ts | 32 ++++++++++++++++++++++++-------- test/utils/SwapFactory.ts | 21 +++++++++++++++++++++ 2 files changed, 45 insertions(+), 8 deletions(-) diff --git a/scripts/createSwap.ts b/scripts/createSwap.ts index a382b64..28de0a7 100644 --- a/scripts/createSwap.ts +++ b/scripts/createSwap.ts @@ -1,19 +1,30 @@ import { ethers } from "hardhat"; import { Contract } from "ethers"; import { blocktimestamp, storeEnv } from "../test/utils/utils"; -import { Swap, composeSwap } from "../test/utils/SwapFactory"; +import { + Swap, + composeSwap, + encodeAsset, + encodeConfig, +} from "../test/utils/SwapFactory"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; export async function main() { /// @dev This is the list of mock deployments addresses that were stored in the `.env` file. const ERC20_ADDRESS = process.env.ERC20_ADDRESS || 0x0; const ERC721_ADDRESS = process.env.ERC721_ADDRESS || 0x0; + const ERC1155_ADDRESS = process.env.ERC1155_ADDRESS || 0x0; /// @dev The Swaplace address also needs to be instance to receive the approvals. const SWAPLACE_ADDRESS = process.env.SWAPLACE_ADDRESS || 0x0; /// @dev Will throw an error if any of the addresses were not set in the `.env` file. - if (!ERC20_ADDRESS || !ERC721_ADDRESS || !SWAPLACE_ADDRESS) { + if ( + !ERC20_ADDRESS || + !ERC721_ADDRESS || + !SWAPLACE_ADDRESS || + !ERC1155_ADDRESS + ) { throw new Error( - "Invalid ERC20, ERC721 or Swaplace address, please check if the addresse in the `.env` file is set up correctly.", + "Invalid ERC20, ERC721, ERC1155 or Swaplace address, please check if the addresses in the `.env` file are set up correctly.", ); } @@ -65,21 +76,26 @@ export async function main() { ); } - /// @dev Fill the Swap struct + /// @dev Fill the Swap struct and config const owner = signers[0].address; const allowed = ethers.constants.AddressZero; const expiry = (await blocktimestamp()) * 2; + const recipient = 0; + const value = 0; + + /// @dev Encode ERC1155 asset + const amountAndId = await encodeAsset(BigInt(tokenId), BigInt(amount)); /// @dev Build the biding assets - const bidingAddr = [ERC20_ADDRESS]; - const bidingAmountOrId = [amount]; + const bidingAddr = [ERC20_ADDRESS, ERC1155_ADDRESS]; + const bidingAmountOrId = [BigInt(amount), amountAndId]; /// @dev Build the asking assets const askingAddr = [ERC721_ADDRESS]; - const askingAmountOrId = [tokenId]; + const askingAmountOrId = [BigInt(tokenId)]; /// @dev Pack the config together - const config = (BigInt(allowed) << BigInt(96)) | BigInt(expiry); + const config = await encodeConfig(allowed, expiry, recipient, value); /// @dev Compose the above swap into the Swap Struct const swap: Swap = await composeSwap( diff --git a/test/utils/SwapFactory.ts b/test/utils/SwapFactory.ts index fd721a2..e7afabd 100644 --- a/test/utils/SwapFactory.ts +++ b/test/utils/SwapFactory.ts @@ -88,6 +88,27 @@ export async function makeAsset( return asset; } +/** + * @dev See {ISwapFactory-encodeAsset}. + */ +export async function encodeAsset( + tokenId: bigint | number, + tokenAmount: bigint | number, +): Promise { + // if the amount or ID is negative, it will throw an error + if (tokenId < 0 || tokenAmount < 0) { + throw new Error("tokenId or tokenAmount cannot be less than 0"); + } + + const uint16Max = 65535; + + return BigInt( + (BigInt(uint16Max) << BigInt(240)) | + (BigInt(tokenId) << BigInt(120)) | + BigInt(tokenAmount), + ); +} + /** * @dev See {ISwapFactory-makeSwap}. */ From 247965f6df1a06d8b12d0cc588786142df3c1ce0 Mon Sep 17 00:00:00 2001 From: Deepu Date: Sat, 18 May 2024 15:23:16 +0530 Subject: [PATCH 69/91] Requested changes made #217 --- test/TestSwaplace.test.ts | 83 ++++++++++++++++++++++++++++++++++----- 1 file changed, 73 insertions(+), 10 deletions(-) diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index 414307b..8f99819 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -327,7 +327,7 @@ describe("Swaplace", async function () { .withArgs(await Swaplace.totalSwaps(), owner.address, zeroAddress); }); - it("Should be able to create a swap with native ethers", async function () { + it("Should be able to {createSwap} with native ethers sent by the {owner}", async function () { const bidingAddr = [MockERC20.address]; const bidingAmountOrId = [50]; @@ -339,7 +339,7 @@ describe("Swaplace", async function () { const askingAmountOrId = [50, 100, 150]; const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); - + const currentTimestamp = (await blocktimestamp()) + 1000000; const config = await Swaplace.encodeConfig( zeroAddress, @@ -374,7 +374,7 @@ describe("Swaplace", async function () { ).to.be.revertedWithCustomError(Swaplace, `InvalidAddress`); }); - it("Should revert when the wrong amount of ethers is sent by the {owner}", async function () { + it("Should revert when the wrong amount of ethers are sent by the {owner}", async function () { const bidingAddr = [MockERC20.address]; const bidingAmountOrId = [50]; @@ -539,7 +539,7 @@ describe("Swaplace", async function () { ); }); - it("Should be able to {acceptSwap} with native ethers", async function () { + it("Should be able to {acceptSwap} with native ethers sent by the {owner}", async function () { await MockERC20.mint(owner.address, 1000); await MockERC721.mint(allowed.address, 10); @@ -555,11 +555,8 @@ describe("Swaplace", async function () { MockERC20.address, ]; const askingAmountOrId = [50, 100, 150]; - const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); - const expiry = (await blocktimestamp()) + 1000000; - const config = await Swaplace.encodeConfig( allowed.address, expiry, @@ -576,6 +573,9 @@ describe("Swaplace", async function () { askingAmountOrId, ); + const balanceBefore: BigNumber = await receiver.getBalance(); + const expectedBalance: BigNumber = balanceBefore.add(valueToSend); + await expect( await Swaplace.connect(owner).createSwap(swap, { value: valueToSend, @@ -600,6 +600,72 @@ describe("Swaplace", async function () { owner.address, allowed.address, ); + + const balanceAfter: BigNumber = await receiver.getBalance(); + await expect(balanceAfter).to.be.equals(expectedBalance); + }); + + it("Should be able to {acceptSwap} with native ethers sent by the {acceptee}", async function () { + await MockERC20.mint(owner.address, 1000); + await MockERC721.mint(allowed.address, 10); + + await MockERC20.connect(owner).approve(Swaplace.address, 1000); + await MockERC721.connect(allowed).approve(Swaplace.address, 10); + + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [50]; + + const askingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const askingAmountOrId = [50, 100, 150]; + const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); + const expiry = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + allowed.address, + expiry, + 1, + valueToSend.div(1e12), + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs( + await Swaplace.totalSwaps(), + owner.address, + allowed.address, + ); + + const balanceBefore: BigNumber = await owner.getBalance(); + const expectedBalance: BigNumber = balanceBefore.add(valueToSend); + + await expect( + Swaplace.connect(allowed).acceptSwap( + await Swaplace.totalSwaps(), + receiver.address, + { value: valueToSend }, + ), + ) + .to.emit(Swaplace, "SwapAccepted") + .withArgs( + await Swaplace.totalSwaps(), + owner.address, + allowed.address, + ); + + const balanceAfter: BigNumber = await owner.getBalance(); + await expect(balanceAfter).to.be.equals(expectedBalance); }); }); @@ -705,11 +771,8 @@ describe("Swaplace", async function () { MockERC20.address, ]; const askingAmountOrId = [50, 100, 150]; - const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); - const expiry = (await blocktimestamp()) + 1000000; - const config = await Swaplace.encodeConfig( allowed.address, expiry, From 9da0bd46e943f4ce78ff38cc7ec6163655068f73 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Sun, 19 May 2024 11:15:05 +0700 Subject: [PATCH 70/91] fix: removing length tests from echidna as it no longer exists --- contracts/echidna/TestSwapFactory.sol | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/contracts/echidna/TestSwapFactory.sol b/contracts/echidna/TestSwapFactory.sol index 3ebd53a..931c506 100644 --- a/contracts/echidna/TestSwapFactory.sol +++ b/contracts/echidna/TestSwapFactory.sol @@ -58,16 +58,4 @@ contract TestFactory is SwapFactory { _asset ); } - - function echidna_revert_invalid_length() public view { - makeSwap( - address(0), - address(0), - uint32(block.timestamp + 100), - 0, - 0, - new Asset[](0), - new Asset[](0) - ); - } } From 2bed24fbbae62156be0a051e0a2387f056e77915 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Sun, 19 May 2024 16:34:09 +0700 Subject: [PATCH 71/91] fix: adding ERC1155 on sample --- .env.sample | 1 + 1 file changed, 1 insertion(+) diff --git a/.env.sample b/.env.sample index 9c426f7..8981182 100644 --- a/.env.sample +++ b/.env.sample @@ -3,6 +3,7 @@ SWAPLACE_ADDRESS=0x000000000000000000000000000000000000000000 # Mocks last deployed contracts addresses. ERC20_ADDRESS=0x000000000000000000000000000000000000000000 ERC721_ADDRESS=0x000000000000000000000000000000000000000000 +ERC1155_ADDRESS=0x000000000000000000000000000000000000000000 # Amount of ERC20 tokens to be minted for signer address. AMOUNT=1000 # Token ID of ERC721 to be minted for signer address. From c9f3df0093f36d33b010765e29a0339722447020 Mon Sep 17 00:00:00 2001 From: Deepu Date: Sun, 19 May 2024 15:20:33 +0530 Subject: [PATCH 72/91] removed white space #217 --- test/TestSwaplace.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index 8f99819..0f34d48 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -339,7 +339,6 @@ describe("Swaplace", async function () { const askingAmountOrId = [50, 100, 150]; const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); - const currentTimestamp = (await blocktimestamp()) + 1000000; const config = await Swaplace.encodeConfig( zeroAddress, From b4b46cc71c108ffd4cd8a23c68e8bf130be40856 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Sun, 19 May 2024 18:52:50 +0700 Subject: [PATCH 73/91] fix: encodeAsset was not being exported --- test/utils/SwapFactory.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/utils/SwapFactory.ts b/test/utils/SwapFactory.ts index e7afabd..5f2c277 100644 --- a/test/utils/SwapFactory.ts +++ b/test/utils/SwapFactory.ts @@ -101,6 +101,13 @@ export async function encodeAsset( } const uint16Max = 65535; + const uint120Max = BigInt(2) ** BigInt(120) - BigInt(1); + + if (tokenId > uint120Max || tokenAmount > uint120Max) { + throw new Error( + "Maxium bits exceeded for tokenId or tokenAmount. Max: 120 bits.", + ); + } return BigInt( (BigInt(uint16Max) << BigInt(240)) | @@ -203,4 +210,5 @@ module.exports = { composeSwap, encodeConfig, decodeConfig, + encodeAsset, }; From e84906afa90c59303b57d47656496bbab1ed556b Mon Sep 17 00:00:00 2001 From: Deepu Date: Thu, 6 Jun 2024 00:11:53 +0530 Subject: [PATCH 74/91] closes #227 --- contracts/Swaplace.sol | 7 +++++-- contracts/interfaces/IErrors.sol | 5 +++++ test/TestSwaplace.test.ts | 34 ++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index d105556..365aa6d 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -50,8 +50,11 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { swap.config ); - if (value > 0 && recipient == 0) { - if (value * 1e12 != msg.value) revert InvalidValue(); + if (msg.value > 0) { + if (recipient == 0) { + if (value * 1e12 != msg.value) revert InvalidValue(); + } + else revert InvalidSender(); } emit SwapCreated(swapId, msg.sender, allowed); diff --git a/contracts/interfaces/IErrors.sol b/contracts/interfaces/IErrors.sol index 1395042..2219165 100644 --- a/contracts/interfaces/IErrors.sol +++ b/contracts/interfaces/IErrors.sol @@ -24,4 +24,9 @@ interface IErrors { * @dev Displayed when a low level call failed to execute. */ error InvalidCall(); + + /** + * @dev Displayed when the wrong caller sends ethers. + */ + error InvalidSender(); } diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index 54ed5ac..b608744 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -590,6 +590,40 @@ describe("Swaplace", async function () { Swaplace.connect(owner).createSwap(swap, { value: 69 }), ).to.be.revertedWithCustomError(Swaplace, `InvalidValue`); }); + + it("Should revert when the {owner} sends ethers while not being set as the {recipient}", async function () { + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [50]; + + const askingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const askingAmountOrId = [50, 100, 150]; + + const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 1, + valueToSend.div(1e12), + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + await expect( + Swaplace.connect(owner).createSwap(swap, { value: valueToSend }), + ).to.be.revertedWithCustomError(Swaplace, `InvalidSender`); + }); }); }); From bd5a9020233af71ce4580f598884a86a5a8d66b7 Mon Sep 17 00:00:00 2001 From: Deepu Date: Thu, 6 Jun 2024 15:56:46 +0530 Subject: [PATCH 75/91] Fixed a blunder #227 --- contracts/Swaplace.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index 365aa6d..03ef9aa 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -50,11 +50,11 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { swap.config ); - if (msg.value > 0) { + if (value > 0) { if (recipient == 0) { if (value * 1e12 != msg.value) revert InvalidValue(); } - else revert InvalidSender(); + else if (msg.value > 0) revert InvalidSender(); } emit SwapCreated(swapId, msg.sender, allowed); From bc8d90f694117415e0b8ce1248eda35ebac7734f Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 6 Jun 2024 18:44:33 +0700 Subject: [PATCH 76/91] remove: unused error --- contracts/interfaces/IErrors.sol | 5 ----- 1 file changed, 5 deletions(-) diff --git a/contracts/interfaces/IErrors.sol b/contracts/interfaces/IErrors.sol index 2219165..1395042 100644 --- a/contracts/interfaces/IErrors.sol +++ b/contracts/interfaces/IErrors.sol @@ -24,9 +24,4 @@ interface IErrors { * @dev Displayed when a low level call failed to execute. */ error InvalidCall(); - - /** - * @dev Displayed when the wrong caller sends ethers. - */ - error InvalidSender(); } From 175709b48fbd792a2212bbfb715f4d42247e339f Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 6 Jun 2024 18:44:51 +0700 Subject: [PATCH 77/91] fix: wrong identation and error name --- contracts/Swaplace.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index 03ef9aa..968f5d0 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -53,8 +53,7 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { if (value > 0) { if (recipient == 0) { if (value * 1e12 != msg.value) revert InvalidValue(); - } - else if (msg.value > 0) revert InvalidSender(); + } else if (msg.value > 0) revert InvalidValue(); } emit SwapCreated(swapId, msg.sender, allowed); From c39dfaf3c6aa3af745510b6ee292b487d752ca8a Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 6 Jun 2024 18:46:47 +0700 Subject: [PATCH 78/91] fix: wrong test name --- test/TestSwaplace.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index b608744..c896b43 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -591,7 +591,7 @@ describe("Swaplace", async function () { ).to.be.revertedWithCustomError(Swaplace, `InvalidValue`); }); - it("Should revert when the {owner} sends ethers while not being set as the {recipient}", async function () { + it("Should revert when the {owner} sends ethers while being the {recipient}", async function () { const bidingAddr = [MockERC20.address]; const bidingAmountOrId = [50]; From 694c4a1115cf2baa469ce8a8bc498d355d6c15bb Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 6 Jun 2024 18:48:46 +0700 Subject: [PATCH 79/91] docs: explaining the recipient and value var usage --- contracts/SwapFactory.sol | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/contracts/SwapFactory.sol b/contracts/SwapFactory.sol index 9fc5710..acaa2ad 100644 --- a/contracts/SwapFactory.sol +++ b/contracts/SwapFactory.sol @@ -18,8 +18,11 @@ import {ISwapFactory} from "./interfaces/ISwapFactory.sol"; * - The `allowed` address is the address that can accept the Swap. If the allowed * address is the zero address, then anyone can accept the Swap. * - The `expiry` date is the timestamp that the Swap will be available to accept. - * - The `recipient` is the address that will receive the ETH. - * - The `value` is the amount of ETH that the recipient will receive. + * - The `recipient` is the address that will receive the ETH as type uint8. If the + * recipient is equals to 0, the acceptee will receive the ETH. If the recipient is + * between 1<>255 then the recipient will be the owner of the Swap. + * - The `value` is the amount of ETH that the recipient will receive with a maximum + * of 6 decimals (0.000001 ETH). The contract will fill the value up to 18 decimals. * - The `biding` are the assets that the owner is offering. * - The `asking` are the assets that the owner wants in exchange. * From 490f89d6802e8c8bf536a6e3dc0fd5cb8c195929 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 6 Jun 2024 19:02:17 +0700 Subject: [PATCH 80/91] fix: erc20/erc721 was using wrong amountOrId parameter --- contracts/Swaplace.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index 968f5d0..a5c33e2 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -145,7 +145,7 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { if (!success) revert InvalidCall(); } else { (bool success, ) = address(assets[i].addr).call( - abi.encodeWithSelector(0x23b872dd, from, to, tokenAmount) + abi.encodeWithSelector(0x23b872dd, from, to, assets[i].amountOrId) ); if (!success) revert InvalidCall(); } From 41ef97dd37e6e4cbb992c540870e36e652f54e5f Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 6 Jun 2024 19:12:20 +0700 Subject: [PATCH 81/91] fix: no need to check for recipient since value can only be present when the owner truly sent it --- contracts/Swaplace.sol | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index a5c33e2..7d19180 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -102,14 +102,12 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { Swap memory swap = _swaps[swapId]; if (swap.owner != msg.sender) revert InvalidAddress(); - (, uint32 expiry, uint8 recipient, uint256 value) = decodeConfig( - swap.config - ); + (, uint32 expiry, , uint256 value) = decodeConfig(swap.config); if (expiry < block.timestamp) revert InvalidExpiry(); _swaps[swapId].config = 0; - if (value > 0 && recipient == 0) _payNativeEth(msg.sender, value * 1e12); + if (value > 0) _payNativeEth(msg.sender, value * 1e12); emit SwapCanceled(swapId, msg.sender); } From 091789757b5396c20623a78ef22981b9aaa51e6e Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 6 Jun 2024 19:12:38 +0700 Subject: [PATCH 82/91] fix: wrong return variable name --- contracts/SwapFactory.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/SwapFactory.sol b/contracts/SwapFactory.sol index acaa2ad..4e039fc 100644 --- a/contracts/SwapFactory.sol +++ b/contracts/SwapFactory.sol @@ -80,7 +80,7 @@ abstract contract SwapFactory is ISwapFactory, ISwap, IErrors { function encodeAsset( uint120 tokenId, uint120 tokenAmount - ) public pure returns (uint256 amountOrId) { + ) public pure returns (uint256 amountAndId) { return (uint256(type(uint16).max) << 240) | (uint256(tokenId) << 120) | From 55963c8884eeeaa221deec657b33456450521250 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 6 Jun 2024 19:13:50 +0700 Subject: [PATCH 83/91] docs: added param types and stricly rules of variables --- contracts/interfaces/ISwapFactory.sol | 51 ++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/contracts/interfaces/ISwapFactory.sol b/contracts/interfaces/ISwapFactory.sol index a636c00..a975ce6 100644 --- a/contracts/interfaces/ISwapFactory.sol +++ b/contracts/interfaces/ISwapFactory.sol @@ -9,6 +9,9 @@ import {ISwap} from "./ISwap.sol"; interface ISwapFactory { /** * @dev Make an {ISwap-Asset} struct to work with token standards. + * + * @param addr is the address of the token asset. + * @param amountOrId is the amount of tokens or the ID of the NFT. */ function makeAsset( address addr, @@ -21,6 +24,10 @@ interface ISwapFactory { * NOTE: Different from the {makeAsset} function, this function is used to * encode the token ID and token amount into a single uint256. This is made * to work with the ERC1155 standard. + * + * @param addr is the address of the token asset. + * @param tokenId is the ID of the ERC1155 token. + * @param tokenAmount is the amount of the ERC1155 token. */ function make1155Asset( address addr, @@ -34,6 +41,15 @@ interface ISwapFactory { * Requirements: * * - `expiry` cannot be in the past. + * + * @param owner is the address that created the Swap. + * @param allowed is the address that can accept the Swap. If the allowed + * address is the zero address, then anyone can accept the Swap. + * @param expiry is the timestamp that the Swap will be available to accept. + * @param recipient is the address that will receive the ETH. `0` for the acceptee + * and `1<>255` for the owner. + * @param value is the amount of ETH that the recipient will receive. Maximum of + * 6 decimals (0.000001 ETH). The contract will fill the value up to 18 decimals. */ function makeSwap( address owner, @@ -48,22 +64,36 @@ interface ISwapFactory { /** * @dev Encode `tokenId` and `tokenAmount` into a single uint256 while adding a flag * to indicate that it's an ERC1155 token. + * + * NOTE: The flag is set to 0xFFFFFFFF. + * + * @param tokenId is the ID of the ERC1155 token. + * @param tokenAmount is the amount of the ERC1155 token. */ function encodeAsset( uint120 tokenId, uint120 tokenAmount - ) external pure returns (uint256 amountOrId); + ) external pure returns (uint256 amountAndId); /** - * @dev Decode `amountOrId` to check if the first 4 bytes are set to 0xFFFFFFFF. + * @dev Decode `amountOrId` returning the first 4 bytes to try match with 0xFFFFFFFF. * If the flag is set to 0xFFFFFFFF, then it's an ERC1155 standard, otherwise it's - * assumed to be an ERC20 or ERC721 standard. + * assumed to be an ERC20 or ERC721. * * NOTE: If it's an ERC1155 token, then the next 120 bits are the token ID and the next * 120 bits are the token amount. + * + * WARNING: Swaplace cannot handle ERC1155 tokens where the ID or the amount is greater + * than 120 bits. + * + * @param amountAndId is the amount of tokens and the ID of the ERC1155 token. + * @return tokenType is the flag to indicate the token standard. + * @return tokenId is the ID of the ERC1155 token. + * @return tokenAmount is the amount of the ERC1155 token. + * */ function decodeAsset( - uint256 amountOrId + uint256 amountAndId ) external pure @@ -71,16 +101,27 @@ interface ISwapFactory { /** * @dev This function uses bitwise to return an encoded uint256 of the following parameters. + * + * @param allowed address is the address that can accept the Swap. If the allowed + * address is the zero address, then anyone can accept the Swap. + * @param expiry date is the timestamp that the Swap will be available to accept. + * @param recipient is the address that will receive the ETH as type uint8. If the + * recipient is equals to 0, the acceptee will receive the ETH. If the recipient is + * between 1<>255 then the recipient will be the owner of the Swap. + * @param value is the amount of ETH that the recipient will receive with a maximum + * of 6 decimals (0.000001 ETH). The contract will fill the value up to 18 decimals. */ function encodeConfig( address allowed, uint32 expiry, uint8 recipient, uint56 value - ) external pure returns (uint256); + ) external pure returns (uint256 config); /** * @dev Decode `config` into their respective variables. + * + * @param config is the encoded uint256 configuration of the Swap. */ function decodeConfig( uint256 config From ce31d03af3fe3563b558cf06ddd851e3c608aa49 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 6 Jun 2024 19:14:01 +0700 Subject: [PATCH 84/91] fix: wrong error name --- test/TestSwaplace.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index c896b43..f499e53 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -622,7 +622,7 @@ describe("Swaplace", async function () { ); await expect( Swaplace.connect(owner).createSwap(swap, { value: valueToSend }), - ).to.be.revertedWithCustomError(Swaplace, `InvalidSender`); + ).to.be.revertedWithCustomError(Swaplace, `InvalidValue`); }); }); }); From d452e4fa03c33f3ce5590459da96ced91f747a65 Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 6 Jun 2024 19:41:06 +0700 Subject: [PATCH 85/91] fix: cancel swap test wasn't being validated --- test/TestSwaplace.test.ts | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index f499e53..9b86dfb 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -1078,7 +1078,6 @@ describe("Swaplace", async function () { askingAmountOrId, ); - const lastSwap = await Swaplace.totalSwaps(); await expect( await Swaplace.connect(owner).createSwap(swap, { value: valueToSend, @@ -1087,9 +1086,18 @@ describe("Swaplace", async function () { .to.emit(Swaplace, "SwapCreated") .withArgs(await Swaplace.totalSwaps(), owner.address, zeroAddress); - await expect(Swaplace.connect(owner).cancelSwap(lastSwap)) - .to.emit(Swaplace, "SwapCanceled") - .withArgs(lastSwap, owner.address); + const balanceBefore = await owner.getBalance(); + + const lastSwap = await Swaplace.totalSwaps(); + const tx = await Swaplace.connect(owner).cancelSwap(lastSwap); + const receipt = await tx.wait(); + const gasUsed = receipt.gasUsed; + const gasPrice = receipt.effectiveGasPrice; + + const balanceAfter = await owner.getBalance(); + expect(balanceBefore.add(valueToSend)).to.be.equals( + balanceAfter.add(gasPrice.mul(gasUsed)), + ); }); }); @@ -1102,8 +1110,6 @@ describe("Swaplace", async function () { }); it("Should revert when {expiry} is smaller than {block.timestamp}", async function () { - const [, expiry, ,] = await Swaplace.decodeConfig(swap.config); - await network.provider.send("evm_increaseTime", [2000000]); const lastSwap = await Swaplace.totalSwaps(); From a773428f6c3d4be7eac3a13dd52823cab6ba1a0a Mon Sep 17 00:00:00 2001 From: 0xneves Date: Thu, 6 Jun 2024 20:56:45 +0700 Subject: [PATCH 86/91] fix: expired swaps couldnt withdraw their ether --- contracts/Swaplace.sol | 12 +++- test/TestSwaplace.test.ts | 140 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+), 3 deletions(-) diff --git a/contracts/Swaplace.sol b/contracts/Swaplace.sol index 7d19180..444858e 100644 --- a/contracts/Swaplace.sol +++ b/contracts/Swaplace.sol @@ -102,12 +102,18 @@ contract Swaplace is SwapFactory, ISwaplace, IERC165 { Swap memory swap = _swaps[swapId]; if (swap.owner != msg.sender) revert InvalidAddress(); - (, uint32 expiry, , uint256 value) = decodeConfig(swap.config); + (, uint32 expiry, uint8 recipient, uint256 value) = decodeConfig( + swap.config + ); - if (expiry < block.timestamp) revert InvalidExpiry(); + if (expiry < block.timestamp && (value == 0 || recipient > 0)) { + revert InvalidExpiry(); + } _swaps[swapId].config = 0; - if (value > 0) _payNativeEth(msg.sender, value * 1e12); + if (value > 0 && recipient == 0) { + _payNativeEth(msg.sender, value * 1e12); + } emit SwapCanceled(swapId, msg.sender); } diff --git a/test/TestSwaplace.test.ts b/test/TestSwaplace.test.ts index 9b86dfb..13be114 100644 --- a/test/TestSwaplace.test.ts +++ b/test/TestSwaplace.test.ts @@ -1099,6 +1099,146 @@ describe("Swaplace", async function () { balanceAfter.add(gasPrice.mul(gasUsed)), ); }); + + it("Should be able to {cancelSwap} and return ethers to {owner} even after expiration", async function () { + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [50]; + + const askingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const askingAmountOrId = [50, 100, 150]; + + const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 0, + valueToSend.div(1e12), + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await expect( + await Swaplace.connect(owner).createSwap(swap, { + value: valueToSend, + }), + ) + .to.emit(Swaplace, "SwapCreated") + .withArgs(await Swaplace.totalSwaps(), owner.address, zeroAddress); + + await network.provider.send("evm_increaseTime", [1000000]); + + const balanceBefore = await owner.getBalance(); + + const lastSwap = await Swaplace.totalSwaps(); + const tx = await Swaplace.connect(owner).cancelSwap(lastSwap); + const receipt = await tx.wait(); + const gasUsed = receipt.gasUsed; + const gasPrice = receipt.effectiveGasPrice; + + const balanceAfter = await owner.getBalance(); + expect(balanceBefore.add(valueToSend)).to.be.equals( + balanceAfter.add(gasPrice.mul(gasUsed)), + ); + }); + + it("Should be able to {cancelSwap} before expiration if the recipient is the {owner}", async function () { + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [50]; + + const askingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const askingAmountOrId = [50, 100, 150]; + + const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 1, + valueToSend.div(1e12), + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs(await Swaplace.totalSwaps(), owner.address, zeroAddress); + + const lastSwap = await Swaplace.totalSwaps(); + await expect(await Swaplace.connect(owner).cancelSwap(lastSwap)) + .to.emit(Swaplace, "SwapCanceled") + .withArgs(lastSwap, owner.address); + + await expect( + Swaplace.connect(owner).cancelSwap(lastSwap), + ).to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`); + }); + + it("Should not be able to {cancelSwap} after expiration if the recipient is the {owner}", async function () { + const bidingAddr = [MockERC20.address]; + const bidingAmountOrId = [50]; + + const askingAddr = [ + MockERC20.address, + MockERC20.address, + MockERC20.address, + ]; + const askingAmountOrId = [50, 100, 150]; + + const valueToSend: BigNumber = ethers.utils.parseEther("0.5"); + + const currentTimestamp = (await blocktimestamp()) + 1000000; + const config = await Swaplace.encodeConfig( + zeroAddress, + currentTimestamp, + 1, + valueToSend.div(1e12), + ); + + const swap: Swap = await composeSwap( + owner.address, + config, + bidingAddr, + bidingAmountOrId, + askingAddr, + askingAmountOrId, + ); + + await expect(await Swaplace.connect(owner).createSwap(swap)) + .to.emit(Swaplace, "SwapCreated") + .withArgs(await Swaplace.totalSwaps(), owner.address, zeroAddress); + + await network.provider.send("evm_increaseTime", [1000000]); + + const lastSwap = await Swaplace.totalSwaps(); + await expect( + Swaplace.connect(owner).cancelSwap(lastSwap), + ).to.be.revertedWithCustomError(Swaplace, `InvalidExpiry`); + }); }); context("Reverts when canceling Swaps", () => { From 9764e07b1bdee693fcc59e0af04f154a8b964335 Mon Sep 17 00:00:00 2001 From: Rafael Sanches Date: Thu, 6 Jun 2024 11:35:03 -0300 Subject: [PATCH 87/91] Updating docs with docgen-solidity --- docs/solidity-docgen/SwapFactory.md | 53 +++-- docs/solidity-docgen/Swaplace.md | 47 ++-- .../echidna/TestSwapFactory.md | 34 --- docs/solidity-docgen/echidna/TestSwaplace.md | 34 --- docs/solidity-docgen/interfaces/IErrors.md | 25 ++- .../interfaces/ISwapFactory.md | 124 +++++++++-- docs/solidity-docgen/interfaces/ISwaplace.md | 4 +- docs/solidity-docgen/interfaces/ITransfer.md | 18 -- docs/solidity-docgen/mock/ERC20/ERC20.md | 205 ------------------ .../mock/ERC20/interfaces/IERC20.md | 154 ------------- .../mock/ERC20/interfaces/IERC20Errors.md | 98 --------- .../mock/ERC20/interfaces/IERC20Permit.md | 69 ------ docs/solidity-docgen/mock/MockERC20.md | 15 -- docs/solidity-docgen/mock/MockERC721.md | 21 -- 14 files changed, 200 insertions(+), 701 deletions(-) delete mode 100644 docs/solidity-docgen/echidna/TestSwapFactory.md delete mode 100644 docs/solidity-docgen/echidna/TestSwaplace.md delete mode 100644 docs/solidity-docgen/interfaces/ITransfer.md delete mode 100644 docs/solidity-docgen/mock/ERC20/ERC20.md delete mode 100644 docs/solidity-docgen/mock/ERC20/interfaces/IERC20.md delete mode 100644 docs/solidity-docgen/mock/ERC20/interfaces/IERC20Errors.md delete mode 100644 docs/solidity-docgen/mock/ERC20/interfaces/IERC20Permit.md delete mode 100644 docs/solidity-docgen/mock/MockERC20.md delete mode 100644 docs/solidity-docgen/mock/MockERC721.md diff --git a/docs/solidity-docgen/SwapFactory.md b/docs/solidity-docgen/SwapFactory.md index 2560a6d..e26aff6 100644 --- a/docs/solidity-docgen/SwapFactory.md +++ b/docs/solidity-docgen/SwapFactory.md @@ -14,19 +14,22 @@ composed of: - The `allowed` address is the address that can accept the Swap. If the allowed address is the zero address, then anyone can accept the Swap. - The `expiry` date is the timestamp that the Swap will be available to accept. +- The `recipient` is the address that will receive the ETH as type uint8. If the +recipient is equals to 0, the acceptee will receive the ETH. If the recipient is +between 1<>255 then the recipient will be the owner of the Swap. +- The `value` is the amount of ETH that the recipient will receive with a maximum +of 6 decimals (0.000001 ETH). The contract will fill the value up to 18 decimals. - The `biding` are the assets that the owner is offering. - The `asking` are the assets that the owner wants in exchange. The Swap struct uses an {Asset} struct to represent the asset. This struct is composed of: -- The `address` of the asset. This address can be from an ERC20 or ERC721 contract. +- The `address` of the token asset. - The `amount` or `id` of the asset. This amount can be the amount of ERC20 tokens - or the ID of an ERC721 token. - -To use other standards, like ERC1155, you can wrap the ownership of the asset -in an a trusted contract and Swap as an ERC721. This way, you can tokenize any -on-chain execution and trade on Swaplace._ + or the NFT ID of an ERC721. +- The `amount` and `id` can be encoded together in a single uint256, allowing the +ERC1155 tokens to be swapped._ ### makeAsset @@ -36,27 +39,51 @@ function makeAsset(address addr, uint256 amountOrId) public pure virtual returns _See {ISwapFactory-makeAsset}._ +### make1155Asset + +```solidity +function make1155Asset(address addr, uint120 tokenId, uint120 tokenAmount) public pure virtual returns (struct ISwap.Asset) +``` + +_See {ISwapFactory-make1155Asset}._ + ### makeSwap ```solidity -function makeSwap(address owner, address allowed, uint256 expiry, struct ISwap.Asset[] biding, struct ISwap.Asset[] asking) public view virtual returns (struct ISwap.Swap) +function makeSwap(address owner, address allowed, uint32 expiry, uint8 recipient, uint56 value, struct ISwap.Asset[] biding, struct ISwap.Asset[] asking) public view virtual returns (struct ISwap.Swap) ``` _See {ISwapFactory-makeSwap}._ -### packData +### encodeAsset + +```solidity +function encodeAsset(uint120 tokenId, uint120 tokenAmount) public pure returns (uint256 amountAndId) +``` + +_See {ISwapFactory-encodeAsset}._ + +### decodeAsset + +```solidity +function decodeAsset(uint256 amountOrId) public pure returns (uint16 tokenType, uint256 tokenId, uint256 tokenAmount) +``` + +_See {ISwapFactory-decodeAsset}._ + +### encodeConfig ```solidity -function packData(address allowed, uint256 expiry) public pure returns (uint256) +function encodeConfig(address allowed, uint32 expiry, uint8 recipient, uint56 value) public pure returns (uint256) ``` -_See {ISwapFactory-packData}._ +_See {ISwapFactory-encodeConfig}._ -### parseData +### decodeConfig ```solidity -function parseData(uint256 config) public pure returns (address, uint256) +function decodeConfig(uint256 config) public pure returns (address, uint32, uint8, uint56) ``` -_See {ISwapFactory-parseData}._ +_See {ISwapFactory-decodeConfig}._ diff --git a/docs/solidity-docgen/Swaplace.md b/docs/solidity-docgen/Swaplace.md index 1a76ad9..5025de7 100644 --- a/docs/solidity-docgen/Swaplace.md +++ b/docs/solidity-docgen/Swaplace.md @@ -2,15 +2,31 @@ ## Swaplace -_Swaplace is a Decentralized Feeless DEX. It has no owners, it cannot be stopped. -Its cern is to facilitate swaps between virtual assets following the ERC standard. +_Swaplace is a decentralized and feeless DEX/OTC. Ownerless, it cannot be stopped. +It's core is to facilitate swaps between virtual assets using the ERC standard. Users can propose or accept swaps by allowing Swaplace to move their assets using the -`approve` or `permit` function._ +`approve`, `permit` or similar functions._ + +### getSwap + +```solidity +function getSwap(uint256 swapId) public view returns (struct ISwap.Swap) +``` + +_See {ISwaplace-getSwap}._ + +### totalSwaps + +```solidity +function totalSwaps() public view returns (uint256) +``` + +_Getter function for _totalSwaps._ ### createSwap ```solidity -function createSwap(struct ISwap.Swap swap) public returns (uint256) +function createSwap(struct ISwap.Swap swap) public payable returns (uint256) ``` _See {ISwaplace-createSwap}._ @@ -18,7 +34,7 @@ _See {ISwaplace-createSwap}._ ### acceptSwap ```solidity -function acceptSwap(uint256 swapId, address receiver) public returns (bool) +function acceptSwap(uint256 swapId, address receiver) public payable returns (bool) ``` _See {ISwaplace-acceptSwap}._ @@ -31,27 +47,30 @@ function cancelSwap(uint256 swapId) public _See {ISwaplace-cancelSwap}._ -### getSwap +### _payNativeEth ```solidity -function getSwap(uint256 swapId) public view returns (struct ISwap.Swap) +function _payNativeEth(address receiver, uint256 value) internal ``` -_See {ISwaplace-getSwap}._ +_Send an amount of native Ether to the receiver address._ -### supportsInterface +### _transferFrom ```solidity -function supportsInterface(bytes4 interfaceID) external pure returns (bool) +function _transferFrom(address from, address to, struct ISwap.Asset[] assets) internal ``` -_See {IERC165-supportsInterface}._ +_Transfer multiple 'assets' from 'from' to 'to'. -### totalSwaps +`0x23b872dd` - Selector of the `transferFrom` function (ERC20, ERC721). +`0xf242432a` - Selector of the `safeTransferFrom` function (ERC1155)._ + +### supportsInterface ```solidity -function totalSwaps() public view returns (uint256) +function supportsInterface(bytes4 interfaceID) external pure returns (bool) ``` -_Getter function for _totalSwaps._ +_See {IERC165-supportsInterface}._ diff --git a/docs/solidity-docgen/echidna/TestSwapFactory.md b/docs/solidity-docgen/echidna/TestSwapFactory.md deleted file mode 100644 index 19a47e6..0000000 --- a/docs/solidity-docgen/echidna/TestSwapFactory.md +++ /dev/null @@ -1,34 +0,0 @@ -# Solidity API - -## TestFactory - -### has_values - -```solidity -function has_values() public -``` - -### make_asset_array - -```solidity -function make_asset_array(address addr, uint256 amountOrId) public pure returns (struct ISwap.Asset[]) -``` - -### make_valid_swap - -```solidity -function make_valid_swap(address owner, address addr, uint256 amountOrId) public view returns (struct ISwap.Swap) -``` - -### echidna_revert_invalid_expiry - -```solidity -function echidna_revert_invalid_expiry() public view -``` - -### echidna_revert_invalid_length - -```solidity -function echidna_revert_invalid_length() public view -``` - diff --git a/docs/solidity-docgen/echidna/TestSwaplace.md b/docs/solidity-docgen/echidna/TestSwaplace.md deleted file mode 100644 index 322734e..0000000 --- a/docs/solidity-docgen/echidna/TestSwaplace.md +++ /dev/null @@ -1,34 +0,0 @@ -# Solidity API - -## TestSwaplace - -### constructor - -```solidity -constructor() public -``` - -### echidna_create_swap - -```solidity -function echidna_create_swap() public returns (bool) -``` - -### echidna_accept_swap - -```solidity -function echidna_accept_swap() public returns (bool) -``` - -### echidna_id_overflow - -```solidity -function echidna_id_overflow() public view returns (bool) -``` - -### echidna_id_never_zero_after_init - -```solidity -function echidna_id_never_zero_after_init() public view returns (bool) -``` - diff --git a/docs/solidity-docgen/interfaces/IErrors.md b/docs/solidity-docgen/interfaces/IErrors.md index b30555a..4b1b1bc 100644 --- a/docs/solidity-docgen/interfaces/IErrors.md +++ b/docs/solidity-docgen/interfaces/IErrors.md @@ -7,29 +7,32 @@ _Errors only interface for the {Swaplace} implementations._ ### InvalidAddress ```solidity -error InvalidAddress(address caller) +error InvalidAddress() ``` _Displayed when the caller is not the owner of the swap._ -### InvalidAssetsLength +### InvalidExpiry ```solidity -error InvalidAssetsLength() +error InvalidExpiry() ``` -_Displayed when the amount of {ISwap-Asset} has a length of zero. +_Displayed when the `expiry` date is in the past._ -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._ +### InvalidValue -### InvalidExpiry +```solidity +error InvalidValue() +``` + +_Displayed when the `msg.value` doesn't match the swap request._ + +### InvalidCall ```solidity -error InvalidExpiry(uint256 timestamp) +error InvalidCall() ``` -_Displayed when the `expiry` date is in the past._ +_Displayed when a low level call failed to execute._ diff --git a/docs/solidity-docgen/interfaces/ISwapFactory.md b/docs/solidity-docgen/interfaces/ISwapFactory.md index e90f35e..076fe9a 100644 --- a/docs/solidity-docgen/interfaces/ISwapFactory.md +++ b/docs/solidity-docgen/interfaces/ISwapFactory.md @@ -10,37 +10,135 @@ _Interface of the {SwapFactory} implementation._ function makeAsset(address addr, uint256 amountOrId) external pure returns (struct ISwap.Asset) ``` -_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._ +_Make an {ISwap-Asset} struct to work with token standards._ + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| addr | address | is the address of the token asset. | +| amountOrId | uint256 | is the amount of tokens or the ID of the NFT. | + +### make1155Asset + +```solidity +function make1155Asset(address addr, uint120 tokenId, uint120 tokenAmount) external pure returns (struct ISwap.Asset) +``` + +_Make an {ISwap-Asset} struct to work with token standards. + +NOTE: Different from the {makeAsset} function, this function is used to +encode the token ID and token amount into a single uint256. This is made +to work with the ERC1155 standard._ + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| addr | address | is the address of the token asset. | +| tokenId | uint120 | is the ID of the ERC1155 token. | +| tokenAmount | uint120 | is the amount of the ERC1155 token. | ### makeSwap ```solidity -function makeSwap(address owner, address allowed, uint256 expiry, struct ISwap.Asset[] assets, struct ISwap.Asset[] asking) external view returns (struct ISwap.Swap) +function makeSwap(address owner, address allowed, uint32 expiry, uint8 recipient, uint56 value, struct ISwap.Asset[] assets, struct ISwap.Asset[] asking) external view returns (struct ISwap.Swap) ``` _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._ +- `expiry` cannot be in the past._ + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| owner | address | is the address that created the Swap. | +| allowed | address | is the address that can accept the Swap. If the allowed address is the zero address, then anyone can accept the Swap. | +| expiry | uint32 | is the timestamp that the Swap will be available to accept. | +| recipient | uint8 | is the address that will receive the ETH. `0` for the acceptee and `1<>255` for the owner. | +| value | uint56 | is the amount of ETH that the recipient will receive. Maximum of 6 decimals (0.000001 ETH). The contract will fill the value up to 18 decimals. | +| assets | struct ISwap.Asset[] | | +| asking | struct ISwap.Asset[] | | -### packData +### encodeAsset ```solidity -function packData(address allowed, uint256 expiry) external pure returns (uint256) +function encodeAsset(uint120 tokenId, uint120 tokenAmount) external pure returns (uint256 amountAndId) ``` -_Packs `allowed` and the `expiry`. -This function returns the bitwise packing of `allowed` and `expiry` as a uint256._ +_Encode `tokenId` and `tokenAmount` into a single uint256 while adding a flag +to indicate that it's an ERC1155 token. + +NOTE: The flag is set to 0xFFFFFFFF._ + +#### Parameters -### parseData +| Name | Type | Description | +| ---- | ---- | ----------- | +| tokenId | uint120 | is the ID of the ERC1155 token. | +| tokenAmount | uint120 | is the amount of the ERC1155 token. | + +### decodeAsset ```solidity -function parseData(uint256 config) external pure returns (address, uint256) +function decodeAsset(uint256 amountAndId) external pure returns (uint16 tokenType, uint256 tokenId, uint256 tokenAmount) ``` -_Parsing the `config`. -This function returns the extracted values of `allowed` and `expiry`._ +_Decode `amountOrId` returning the first 4 bytes to try match with 0xFFFFFFFF. +If the flag is set to 0xFFFFFFFF, then it's an ERC1155 standard, otherwise it's +assumed to be an ERC20 or ERC721. + +NOTE: If it's an ERC1155 token, then the next 120 bits are the token ID and the next +120 bits are the token amount. + +WARNING: Swaplace cannot handle ERC1155 tokens where the ID or the amount is greater +than 120 bits._ + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| amountAndId | uint256 | is the amount of tokens and the ID of the ERC1155 token. | + +#### Return Values + +| Name | Type | Description | +| ---- | ---- | ----------- | +| tokenType | uint16 | is the flag to indicate the token standard. | +| tokenId | uint256 | is the ID of the ERC1155 token. | +| tokenAmount | uint256 | is the amount of the ERC1155 token. | + +### encodeConfig + +```solidity +function encodeConfig(address allowed, uint32 expiry, uint8 recipient, uint56 value) external pure returns (uint256 config) +``` + +_This function uses bitwise to return an encoded uint256 of the following parameters._ + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| allowed | address | address is the address that can accept the Swap. If the allowed address is the zero address, then anyone can accept the Swap. | +| expiry | uint32 | date is the timestamp that the Swap will be available to accept. | +| recipient | uint8 | is the address that will receive the ETH as type uint8. If the recipient is equals to 0, the acceptee will receive the ETH. If the recipient is between 1<>255 then the recipient will be the owner of the Swap. | +| value | uint56 | is the amount of ETH that the recipient will receive with a maximum of 6 decimals (0.000001 ETH). The contract will fill the value up to 18 decimals. | + +### decodeConfig + +```solidity +function decodeConfig(uint256 config) external pure returns (address allowed, uint32 expiry, uint8 recipient, uint56 value) +``` + +_Decode `config` into their respective variables._ + +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| config | uint256 | is the encoded uint256 configuration of the Swap. | diff --git a/docs/solidity-docgen/interfaces/ISwaplace.md b/docs/solidity-docgen/interfaces/ISwaplace.md index 04846c2..e2ee438 100644 --- a/docs/solidity-docgen/interfaces/ISwaplace.md +++ b/docs/solidity-docgen/interfaces/ISwaplace.md @@ -31,7 +31,7 @@ _Emitted when a Swap is canceled._ ### createSwap ```solidity -function createSwap(struct ISwap.Swap Swap) external returns (uint256) +function createSwap(struct ISwap.Swap Swap) external payable returns (uint256) ``` _Allow users to create a Swap. Each new Swap self-increments its ID by one. @@ -47,7 +47,7 @@ Emits a {SwapCreated} event._ ### acceptSwap ```solidity -function acceptSwap(uint256 swapId, address receiver) external returns (bool) +function acceptSwap(uint256 swapId, address receiver) external payable returns (bool) ``` _Accepts a Swap. Once the Swap is accepted, the expiry is set diff --git a/docs/solidity-docgen/interfaces/ITransfer.md b/docs/solidity-docgen/interfaces/ITransfer.md deleted file mode 100644 index b9e50de..0000000 --- a/docs/solidity-docgen/interfaces/ITransfer.md +++ /dev/null @@ -1,18 +0,0 @@ -# Solidity API - -## ITransfer - -_Generalized Interface for {IERC20} and {IERC721} `transferFrom` functions._ - -### transferFrom - -```solidity -function transferFrom(address from, address to, uint256 amountOrId) external -``` - -_See {IERC20-transferFrom} or {IERC721-transferFrom}. - -Moves an `amount` for ERC20 or `tokenId` for ERC721 from `from` to `to`. - -Emits a {Transfer} event._ - diff --git a/docs/solidity-docgen/mock/ERC20/ERC20.md b/docs/solidity-docgen/mock/ERC20/ERC20.md deleted file mode 100644 index 1075c96..0000000 --- a/docs/solidity-docgen/mock/ERC20/ERC20.md +++ /dev/null @@ -1,205 +0,0 @@ -# Solidity API - -## ERC20 - -_Lightweight ERC20 with Permit extension._ - -### constructor - -```solidity -constructor(string name_, string symbol_) internal -``` - -_Sets the values for {name} and {symbol}._ - -### name - -```solidity -function name() public view virtual returns (string) -``` - -_Returns the name of the token._ - -### symbol - -```solidity -function symbol() public view virtual returns (string) -``` - -_Returns the symbol of the token, usually a shorter version of the -name._ - -### decimals - -```solidity -function decimals() public view virtual returns (uint8) -``` - -_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}._ - -### totalSupply - -```solidity -function totalSupply() public view virtual returns (uint256) -``` - -_See {IERC20-totalSupply}._ - -### balanceOf - -```solidity -function balanceOf(address account) public view virtual returns (uint256) -``` - -_See {IERC20-balanceOf}._ - -### allowance - -```solidity -function allowance(address owner, address spender) public view virtual returns (uint256) -``` - -_See {IERC20-allowance}._ - -### nonces - -```solidity -function nonces(address owner) public view virtual returns (uint256) -``` - -_Returns the current nonce of an address._ - -### DOMAIN_SEPARATOR - -```solidity -function DOMAIN_SEPARATOR() public view virtual returns (bytes32) -``` - -_See {IERC20Permit-DOMAIN_SEPARATOR}._ - -### approve - -```solidity -function approve(address spender, uint256 value) public virtual returns (bool) -``` - -_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._ - -### increaseAllowance - -```solidity -function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) -``` - -_See {IERC20-increaseAllowance}._ - -### decreaseAllowance - -```solidity -function decreaseAllowance(address spender, uint256 requestedDecrease) public virtual returns (bool) -``` - -_See {IERC20-decreaseAllowance}. - -Requirements: - -- `spender` must have allowance for the caller of at least -`requestedDecrease`._ - -### permit - -```solidity -function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public virtual returns (bool) -``` - -_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})._ - -### _mint - -```solidity -function _mint(address to, uint256 value) internal -``` - -_Creates an `value` of tokens and assigns them to `to` by creating supply. - -Emits a {Transfer} event with `from` set to the zero address._ - -### _burn - -```solidity -function _burn(address from, uint256 value) internal -``` - -_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._ - -### transfer - -```solidity -function transfer(address to, uint256 value) public virtual returns (bool) -``` - -_See {IERC20-transfer}. - -Requirements: - -- the caller must have a balance of at least `value`._ - -### transferFrom - -```solidity -function transferFrom(address from, address to, uint256 value) public virtual returns (bool) -``` - -_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`._ - -### permitTransfer - -```solidity -function permitTransfer(address from, address to, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public virtual returns (bool) -``` - -_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})._ - diff --git a/docs/solidity-docgen/mock/ERC20/interfaces/IERC20.md b/docs/solidity-docgen/mock/ERC20/interfaces/IERC20.md deleted file mode 100644 index d5bf51b..0000000 --- a/docs/solidity-docgen/mock/ERC20/interfaces/IERC20.md +++ /dev/null @@ -1,154 +0,0 @@ -# Solidity API - -## IERC20 - -_Interface of the ERC20 standard as defined in the EIP._ - -### Approval - -```solidity -event Approval(address owner, address spender, uint256 value) -``` - -_Emitted when the allowance of a `spender` for an `owner` is set by -a call to {approve}. `value` is the new allowance._ - -### Transfer - -```solidity -event Transfer(address from, address to, uint256 value) -``` - -_Emitted when `value` tokens are moved from `from` to `to`. - -NOTE: `value` can be zero._ - -### name - -```solidity -function name() external view returns (string) -``` - -_Returns the name of the token._ - -### symbol - -```solidity -function symbol() external view returns (string) -``` - -_Returns the symbol of the token._ - -### decimals - -```solidity -function decimals() external view returns (uint8) -``` - -_Returns the decimals places of the token._ - -### totalSupply - -```solidity -function totalSupply() external view returns (uint256) -``` - -_Returns the value of tokens in existence._ - -### balanceOf - -```solidity -function balanceOf(address account) external view returns (uint256) -``` - -_Returns the value of tokens owned by `account`._ - -### allowance - -```solidity -function allowance(address owner, address spender) external view returns (uint256) -``` - -_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._ - -### approve - -```solidity -function approve(address spender, uint256 value) external returns (bool) -``` - -_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._ - -### increaseAllowance - -```solidity -function increaseAllowance(address spender, uint256 addedValue) external returns (bool) -``` - -_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._ - -### decreaseAllowance - -```solidity -function decreaseAllowance(address spender, uint256 requestedDecrease) external returns (bool) -``` - -_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._ - -### transfer - -```solidity -function transfer(address to, uint256 value) external returns (bool) -``` - -_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._ - -### transferFrom - -```solidity -function transferFrom(address from, address to, uint256 value) external returns (bool) -``` - -_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._ - diff --git a/docs/solidity-docgen/mock/ERC20/interfaces/IERC20Errors.md b/docs/solidity-docgen/mock/ERC20/interfaces/IERC20Errors.md deleted file mode 100644 index b0f5ae6..0000000 --- a/docs/solidity-docgen/mock/ERC20/interfaces/IERC20Errors.md +++ /dev/null @@ -1,98 +0,0 @@ -# Solidity API - -## IERC20Errors - -_Standard ERC20 Errors_ - -### ERC20InsufficientBalance - -```solidity -error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed) -``` - -_Indicates an error related to the current `balance` of a `sender`. Used in transfers._ - -#### Parameters - -| Name | Type | Description | -| ---- | ---- | ----------- | -| sender | address | Address whose tokens are being transferred. | -| balance | uint256 | Current balance for the interacting account. | -| needed | uint256 | Minimum amount required to perform a transfer. | - -### ERC20InsufficientAllowance - -```solidity -error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed) -``` - -_Indicates a failure with the `spender`’s `allowance`. Used in transfers._ - -#### Parameters - -| Name | Type | Description | -| ---- | ---- | ----------- | -| spender | address | Address that may be allowed to operate on tokens without being their owner. | -| allowance | uint256 | Amount of tokens a `spender` is allowed to operate with. | -| needed | uint256 | Minimum amount required to perform a transfer. | - -### ERC20FailedDecreaseAllowance - -```solidity -error ERC20FailedDecreaseAllowance(address spender, uint256 allowance, uint256 needed) -``` - -_Indicates a failed `decreaseAllowance` request._ - -#### Parameters - -| Name | Type | Description | -| ---- | ---- | ----------- | -| spender | address | Address that may be allowed to operate on tokens without being their owner. | -| allowance | uint256 | Amount of tokens a `spender` want to operate with. | -| needed | uint256 | Amount required to decrease the allowance. | - -### ERC20PermitInvalidNonce - -```solidity -error ERC20PermitInvalidNonce(address account, uint256 nonce) -``` - -_Indicates the nonce used for an `account` is not the expected current nonce._ - -#### Parameters - -| Name | Type | Description | -| ---- | ---- | ----------- | -| account | address | Address whose nonce is being checked. | -| nonce | uint256 | Expected nonce for the given `account`. | - -### ERC2612ExpiredSignature - -```solidity -error ERC2612ExpiredSignature(uint256 deadline) -``` - -_Indicates the expiration of a permit to be used._ - -#### Parameters - -| Name | Type | Description | -| ---- | ---- | ----------- | -| deadline | uint256 | Expiration time limit in seconds. | - -### ERC2612InvalidSigner - -```solidity -error ERC2612InvalidSigner(address signer, address owner) -``` - -_Indicates the mismatched owner when validating the signature._ - -#### Parameters - -| Name | Type | Description | -| ---- | ---- | ----------- | -| signer | address | Address of the signer recovered. | -| owner | address | Address of the owner expected to match `signer`. | - diff --git a/docs/solidity-docgen/mock/ERC20/interfaces/IERC20Permit.md b/docs/solidity-docgen/mock/ERC20/interfaces/IERC20Permit.md deleted file mode 100644 index bf13c88..0000000 --- a/docs/solidity-docgen/mock/ERC20/interfaces/IERC20Permit.md +++ /dev/null @@ -1,69 +0,0 @@ -# Solidity API - -## IERC20Permit - -_Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in -https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. - -Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by -presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't -need to send a transaction, and thus is not required to hold Ether at all._ - -### nonces - -```solidity -function nonces(address owner) external view returns (uint256) -``` - -_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._ - -### DOMAIN_SEPARATOR - -```solidity -function DOMAIN_SEPARATOR() external view returns (bytes32) -``` - -_Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}._ - -### permit - -```solidity -function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external returns (bool) -``` - -_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]._ - -### permitTransfer - -```solidity -function permitTransfer(address from, address to, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external returns (bool) -``` - -_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._ - diff --git a/docs/solidity-docgen/mock/MockERC20.md b/docs/solidity-docgen/mock/MockERC20.md deleted file mode 100644 index 3a8e0f6..0000000 --- a/docs/solidity-docgen/mock/MockERC20.md +++ /dev/null @@ -1,15 +0,0 @@ -# Solidity API - -## MockERC20 - -### constructor - -```solidity -constructor() public -``` - -### mint - -```solidity -function mint(address to, uint256 amount) public -``` diff --git a/docs/solidity-docgen/mock/MockERC721.md b/docs/solidity-docgen/mock/MockERC721.md deleted file mode 100644 index 3e7f2de..0000000 --- a/docs/solidity-docgen/mock/MockERC721.md +++ /dev/null @@ -1,21 +0,0 @@ -# Solidity API - -## MockERC721 - -### totalSupply - -```solidity -uint256 totalSupply -``` - -### constructor - -```solidity -constructor() public -``` - -### mint - -```solidity -function mint(address to, uint256 id) public -``` From d9bdfa80c57d098838c5e5eb88873e1e3fb55f11 Mon Sep 17 00:00:00 2001 From: Rafael Sanches Date: Fri, 14 Jun 2024 20:10:53 -0300 Subject: [PATCH 88/91] git a --- hardhat.config.ts | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index 5e9334d..c1a8d8d 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -34,8 +34,28 @@ const config: HardhatUserConfig = { url: `${process.env.SEPOLIA_RPC_URL}`, accounts: [`${DEPLOYER_PRIVATE_KEY}`], }, - mumbai: { - url: `${process.env.MUMBAI_RPC_URL}`, + amoy: { + url: `${process.env.AMOY_RPC_URL}`, + accounts: [`${DEPLOYER_PRIVATE_KEY}`], + }, + opsepolia: { + url: `${process.env.OPSEPOLIA_RPC_URL}`, + accounts: [`${DEPLOYER_PRIVATE_KEY}`], + }, + fuji: { + url: `${process.env.FUJI_RPC_URL}`, + accounts: [`${DEPLOYER_PRIVATE_KEY}`], + }, + bnb_testnet: { + url: `${process.env.BNB_TESTNET_RPC_URL}`, + accounts: [`${DEPLOYER_PRIVATE_KEY}`], + }, + arbitrum_sepolia: { + url: `${process.env.ARBITRUM_SEPOLIA_RPC_URL}`, + accounts: [`${DEPLOYER_PRIVATE_KEY}`], + }, + base_sepolia: { + url: `${process.env.BASE_SEPOLIA_RPC_URL}`, accounts: [`${DEPLOYER_PRIVATE_KEY}`], }, /** From 85e39a8d13ef3511bbbf7eac5a640afa148ae48b Mon Sep 17 00:00:00 2001 From: 0xneves Date: Sun, 16 Jun 2024 18:25:29 +0700 Subject: [PATCH 89/91] docs: adding natspec param for core functions --- contracts/interfaces/ISwaplace.sol | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/contracts/interfaces/ISwaplace.sol b/contracts/interfaces/ISwaplace.sol index bc6f217..131232d 100644 --- a/contracts/interfaces/ISwaplace.sol +++ b/contracts/interfaces/ISwaplace.sol @@ -40,6 +40,8 @@ interface ISwaplace { * - `biding` and `asking` must not be empty. * * Emits a {SwapCreated} event. + * + * @param Swap is the Swap struct to be created. */ function createSwap( ISwap.Swap calldata Swap @@ -60,6 +62,9 @@ interface ISwaplace { * * NOTE: The expiry is set to 0, because if the Swap is expired it * will revert, preventing reentrancy attacks. + * + * @param swapId is the ID of the Swap to be accepted. + * @param receiver is the address that will receive the trading assets. */ function acceptSwap( uint256 swapId, @@ -78,6 +83,8 @@ interface ISwaplace { * - `expiry` must be bigger than timestamp. * * Emits a {SwapCanceled} event. + * + * @param swapId is the ID of the Swap to be canceled. */ function cancelSwap(uint256 swapId) external; @@ -87,6 +94,8 @@ interface ISwaplace { * 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. + * + * @param swapId is the ID of the Swap to be retrieved. */ function getSwap(uint256 swapId) external view returns (ISwap.Swap memory); } From 50d191452c01a22479595eed6b144517a0abbe80 Mon Sep 17 00:00:00 2001 From: Rafael de Souza Sanches Date: Fri, 21 Jun 2024 11:17:44 -0300 Subject: [PATCH 90/91] fix: removed sensitive info --- hardhat.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index c1a8d8d..c18e639 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -7,7 +7,7 @@ dotenv.config(); // Using a hardcoded solution to avoid GitHub actions issues const DEPLOYER_PRIVATE_KEY = process.env.DEPLOYER_PRIVATE_KEY || - "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"; + ""; const config: HardhatUserConfig = { solidity: { From 154f2271cfa83a8a375cff23c9cdef0f441efda9 Mon Sep 17 00:00:00 2001 From: RafaDSan Date: Tue, 25 Jun 2024 01:07:00 -0300 Subject: [PATCH 91/91] fix: added missing tables in the docs --- docs/solidity-docgen/interfaces/ISwaplace.md | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/docs/solidity-docgen/interfaces/ISwaplace.md b/docs/solidity-docgen/interfaces/ISwaplace.md index e2ee438..8356ef4 100644 --- a/docs/solidity-docgen/interfaces/ISwaplace.md +++ b/docs/solidity-docgen/interfaces/ISwaplace.md @@ -44,6 +44,12 @@ Requirements: Emits a {SwapCreated} event._ +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| Swap | struct ISwap.Swap | is the Swap struct to be created. | + ### acceptSwap ```solidity @@ -65,6 +71,13 @@ Emits a {SwapAccepted} event. NOTE: The expiry is set to 0, because if the Swap is expired it will revert, preventing reentrancy attacks._ +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| swapId | uint256 | is the ID of the Swap to be accepted. | +| receiver | address | is the address that will receive the trading assets. | + ### cancelSwap ```solidity @@ -83,6 +96,12 @@ Requirements: Emits a {SwapCanceled} event._ +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| swapId | uint256 | is the ID of the Swap to be canceled. | + ### getSwap ```solidity @@ -95,3 +114,9 @@ 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._ +#### Parameters + +| Name | Type | Description | +| ---- | ---- | ----------- | +| swapId | uint256 | is the ID of the Swap to be retrieved. | +