diff --git a/.env.sample b/.env.sample index 8981182..77d350c 100644 --- a/.env.sample +++ b/.env.sample @@ -29,6 +29,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/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) - ); - } } 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: { 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/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) => { 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) => { 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; 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];