hardhat#deployments TypeScript Examples

The following examples show how to use hardhat#deployments. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: greet.test.ts    From hardhat-deploy-ts-test with MIT License 7 votes vote down vote up
describe('Token', function () {
  beforeEach(async function () {
    await deployments.fixture();
  });

  it('getting correct greeting', async function () {
    const Greeter = await ethers.getContract('Greeter');
    assert.equal(await Greeter.greet(), 'hi');
  });

  it.skip('Fails: set new greeting', async function () {
    const Greeter = await ethers.getContract('Greeter');
    await Greeter.setGreeting('hi2');
    assert.equal(await Greeter.greet(), 'hi2');
  });
});
Example #2
Source File: PoolDeployer.test.ts    From trident with GNU General Public License v3.0 7 votes vote down vote up
describe("Pool Deployer", function () {
  before(async function () {
    await deployments.fixture(["MasterDeployer"]);
  });

  it("reverts when passing zero address for master deployer", async function () {
    const PoolFactory = await ethers.getContractFactory<PoolDeployerMock__factory>("PoolDeployerMock");
    await expect(PoolFactory.deploy("0x0000000000000000000000000000000000000000")).to.be.revertedWith(
      customError("ZeroAddress")
    );
  });

  it("reverts when deploying directly rather than master deployer", async function () {
    const PoolFactory = await ethers.getContractFactory<PoolDeployerMock__factory>("PoolDeployerMock");
    const masterDeployer = await ethers.getContract<MasterDeployer>("MasterDeployer");
    const poolFactory = await PoolFactory.deploy(masterDeployer.address);

    const deployData = ethers.utils.defaultAbiCoder.encode(
      ["address", "address"],
      ["0x0000000000000000000000000000000000000001", "0x0000000000000000000000000000000000000002"]
    );
    await expect(poolFactory.deployPool(deployData)).to.be.revertedWith(customError("UnauthorisedDeployer"));
  });

  it("reverts when deploying with invalid token order", async function () {
    const PoolFactory = await ethers.getContractFactory<PoolDeployerMock__factory>("PoolDeployerMock");
    const masterDeployer = await ethers.getContract<MasterDeployer>("MasterDeployer");
    const poolFactory = await PoolFactory.deploy(masterDeployer.address);
    await masterDeployer.addToWhitelist(poolFactory.address);
    const deployData = ethers.utils.defaultAbiCoder.encode(
      ["address", "address"],
      ["0x0000000000000000000000000000000000000002", "0x0000000000000000000000000000000000000001"]
    );
    await expect(masterDeployer.deployPool(poolFactory.address, deployData)).to.be.revertedWith(
      customError("InvalidTokenOrder")
    );
  });
});
Example #3
Source File: uninitializedConstantProductPool.ts    From trident with GNU General Public License v3.0 7 votes vote down vote up
uninitializedConstantProductPool = deployments.createFixture(async ({ deployments, ethers }, options) => {
  await deployments.fixture(["ConstantProductPoolFactory"]); // ensure you start from a fresh deployments

  const ERC20 = await ethers.getContractFactory<ERC20Mock__factory>("ERC20Mock");

  const token0 = await ERC20.deploy("Token 0", "TOKEN0", ethers.constants.MaxUint256);
  await token0.deployed();

  const token1 = await ERC20.deploy("Token 1", "TOKEN1", ethers.constants.MaxUint256);
  await token1.deployed();

  const masterDeployer = await ethers.getContract<MasterDeployer>("MasterDeployer");

  const constantProductPoolFactory = await ethers.getContract<ConstantProductPoolFactory>("ConstantProductPoolFactory");

  const deployData = ethers.utils.defaultAbiCoder.encode(
    ["address", "address", "uint256", "bool"],
    [token0.address, token1.address, 0, false]
  );

  const contractReceipt = await masterDeployer
    .deployPool(constantProductPoolFactory.address, deployData)
    .then((tx) => tx.wait());

  const Pool = await ethers.getContractFactory<ConstantProductPool__factory>("ConstantProductPool");

  const pool = Pool.attach(contractReceipt.events?.[0].args?.pool);

  return pool;
}, "uninitializedConstantProductPool")
Example #4
Source File: HoprStakingProxyForNetworkRegistry.test.ts    From hoprnet with GNU General Public License v3.0 7 votes vote down vote up
useFixtures = deployments.createFixture(async (_hre) => {
  const [_deployer, owner, ...signers] = await ethers.getSigners()
  const participants = signers.slice(3, 10) // 7 participants

  const ownerAddress = await owner.getAddress()
  const participantAddresses = await Promise.all(participants.map((h) => h.getAddress()))

  // mock staking contract
  const stakeV2Fake = await createFakeStakeV2Contract(participantAddresses)

  // deploy network registry
  const hoprStakingProxyForNetworkRegistry = (await (
    await ethers.getContractFactory('HoprStakingProxyForNetworkRegistry')
  ).deploy(stakeV2Fake.address, ownerAddress, INITIAL_MIN_STAKE)) as HoprStakingProxyForNetworkRegistry

  return {
    owner,
    participants,
    ownerAddress,
    participantAddresses,
    stakeV2Fake,
    hoprStakingProxyForNetworkRegistry
  }
})
Example #5
Source File: HoprDummyProxyForNetworkRegistry.test.ts    From hoprnet with GNU General Public License v3.0 7 votes vote down vote up
useFixtures = deployments.createFixture(async (_hre) => {
  const [_deployer, owner, ...signers] = await ethers.getSigners()
  const participants = signers.slice(3, 10) // 7 participants

  const ownerAddress = await owner.getAddress()
  const participantAddresses = await Promise.all(participants.map((h) => h.getAddress()))

  // deploy network registry
  const hoprDummyProxyForNetworkRegistry = (await (
    await ethers.getContractFactory('HoprDummyProxyForNetworkRegistry')
  ).deploy(ownerAddress)) as HoprDummyProxyForNetworkRegistry

  return {
    owner,
    participants,
    ownerAddress,
    participantAddresses,
    hoprDummyProxyForNetworkRegistry
  }
})
Example #6
Source File: HoprWrapper.test.ts    From hoprnet with GNU General Public License v3.0 7 votes vote down vote up
useFixtures = deployments.createFixture(async (hre) => {
  const [deployer, userA] = await ethers.getSigners()
  const network = await ethers.provider.getNetwork()

  // deploy ERC1820Registry required by ERC777 tokens
  await deployERC1820Registry(hre, deployer)

  // deploy xHOPR
  const xHOPR = (await (
    await ethers.getContractFactory('PermittableToken')
  ).deploy('xHOPR Token', 'xHOPR', 18, network.chainId)) as PermittableToken

  // deploy wxHOPR
  const wxHOPR = (await (await ethers.getContractFactory('HoprToken')).deploy()) as HoprToken
  // deploy wrapper
  const wrapper = (await (
    await ethers.getContractFactory('HoprWrapper')
  ).deploy(xHOPR.address, wxHOPR.address)) as HoprWrapper

  // allow wrapper to mint wxHOPR required for swapping
  await wxHOPR.grantRole(await wxHOPR.MINTER_ROLE(), wrapper.address)

  // mint some initial xHOPR for userA
  await xHOPR.mint(userA.address, 100)

  return {
    deployer,
    userA,
    xHOPR,
    wxHOPR,
    wrapper
  }
})
Example #7
Source File: HoprToken.test.ts    From hoprnet with GNU General Public License v3.0 7 votes vote down vote up
useFixtures = deployments.createFixture(async (hre) => {
  const [deployer, userA] = await ethers.getSigners()

  // deploy ERC1820Registry required by ERC777 token
  await deployERC1820Registry(hre, deployer)

  // deploy ChannelsMock
  const token = (await (await ethers.getContractFactory('HoprToken')).deploy()) as HoprToken

  // allow deployet to mint tokens
  await token.grantRole(await token.MINTER_ROLE(), deployer.address)

  return {
    deployer: deployer.address,
    token,
    userA
  }
})
Example #8
Source File: HoprNetworkRegistry.test.ts    From hoprnet with GNU General Public License v3.0 7 votes vote down vote up
useFixtures = deployments.createFixture(async (_hre) => {
  const [_deployer, owner, ...signers] = await ethers.getSigners()
  const participants = signers.slice(3, 10) // 7 participants

  const ownerAddress = await owner.getAddress()
  const participantAddresses = await Promise.all(participants.map((h) => h.getAddress()))

  // mock staking contract
  const registryFake = await createFakeRegistryContract(participantAddresses)

  // deploy network registry
  const hoprNetworkRegistry = (await (
    await ethers.getContractFactory('HoprNetworkRegistry')
  ).deploy(registryFake.address, ownerAddress)) as HoprNetworkRegistry

  return {
    owner,
    participants,
    ownerAddress,
    participantAddresses,
    registryFake,
    hoprNetworkRegistry
  }
})
Example #9
Source File: HoprDistributor.test.ts    From hoprnet with GNU General Public License v3.0 7 votes vote down vote up
useFixtures = deployments.createFixture(async (hre, ops: { startTime?: string; maxMintAmount?: string } = {}) => {
  const startTime = ops.startTime ?? (await getLatestBlockTimestamp())
  const maxMintAmount = ops.maxMintAmount ?? '500'
  const [owner] = await ethers.getSigners()

  await deployERC1820Registry(hre, owner)

  const token = (await (await ethers.getContractFactory('HoprToken')).deploy()) as HoprToken
  const distributor = (await (
    await ethers.getContractFactory('HoprDistributor')
  ).deploy(token.address, startTime, maxMintAmount)) as HoprDistributor

  await token.grantRole(await token.MINTER_ROLE(), distributor.address)

  const multiplier = (await distributor.MULTIPLIER()).toNumber()

  return {
    owner: owner.address,
    token,
    distributor,
    multiplier,
    startTime,
    maxMintAmount
  }
})
Example #10
Source File: ERC777Snapshot.test.ts    From hoprnet with GNU General Public License v3.0 7 votes vote down vote up
useFixtures = deployments.createFixture(async (hre) => {
  const [initialHolder, recipient, other] = await ethers.getSigners()

  await deployERC1820Registry(hre, initialHolder)

  const name = 'My Token'
  const symbol = 'MTKN'
  const initialSupply = '100'
  const token = (await (
    await hre.ethers.getContractFactory('ERC777SnapshotMock')
  ).deploy(name, symbol, initialHolder.address, initialSupply)) as ERC777SnapshotMock

  const initialMintBlock = await ethers.provider.getBlockNumber()

  return {
    initialHolder: initialHolder.address,
    recipient: recipient.address,
    other: other.address,
    token,
    initialSupply,
    initialMintBlock
  }
})
Example #11
Source File: diamond.test.ts    From hardhat-deploy-ts-test with MIT License 7 votes vote down vote up
describe('Diamond', function () {
  beforeEach(async function () {
    await deployments.fixture();
  });

  it('hello world', async function () {
    const DiamondExample = await ethers.getContract('DiamondExample');
    assert.equal(await DiamondExample.hello('world'), 'hello : world');
  });

  it('hello world fails after upgrade', async function () {
    await diamond.deploy('DiamondExample', {
      from: deployer,
      facets: ['ActionFacet', 'NewFacet', 'TestFacet'],
    });
    const DiamondExample = await ethers.getContract('DiamondExample');

    expect(() => DiamondExample.hello('world')).to.throw('DiamondExample.hello is not a function');

    assert.equal(await DiamondExample.hello2('world'), 'hello2 world');
    assert.equal((await DiamondExample.getNumber()).toString(), '0');
  });
});
Example #12
Source File: diamond.test.ts    From hardhat-deploy-ts-test with MIT License 6 votes vote down vote up
{diamond} = deployments
Example #13
Source File: 04_Guard.spec.ts    From zodiac with GNU Lesser General Public License v3.0 6 votes vote down vote up
describe("Guardable", async () => {
  const [user1, user2] = waffle.provider.getWallets();

  const setupTests = deployments.createFixture(async ({ deployments }) => {
    await deployments.fixture();
    const Avatar = await hre.ethers.getContractFactory("TestAvatar");
    const avatar = await Avatar.deploy();
    const iAvatar = await hre.ethers.getContractAt("IAvatar", avatar.address);
    const Module = await hre.ethers.getContractFactory("TestModule");
    const module = await Module.deploy(iAvatar.address, iAvatar.address);
    await avatar.enableModule(module.address);
    const Guard = await hre.ethers.getContractFactory("TestGuard");
    const guard = await Guard.deploy(module.address);
    return {
      iAvatar,
      guard,
      module,
    };
  });

  describe("setGuard", async () => {
    it("reverts if reverts if caller is not the owner", async () => {
      const { module } = await setupTests();
      await expect(
        module.connect(user2).setGuard(user2.address)
      ).to.be.revertedWith("Ownable: caller is not the owner");
    });

    it("reverts if guard does not implement ERC165", async () => {
      const { module } = await setupTests();
      await expect(module.setGuard(module.address)).to.be.reverted;
    });

    it("sets module and emits event", async () => {
      const { module, guard } = await setupTests();
      await expect(module.setGuard(guard.address))
        .to.emit(module, "ChangedGuard")
        .withArgs(guard.address);
    });
  });

  describe("getGuard", async () => {
    it("returns guard address", async () => {
      const { module } = await setupTests();
      await expect(await module.getGuard()).to.be.equals(AddressZero);
    });
  });
});
Example #14
Source File: ConstantProduct.ts    From trident with GNU General Public License v3.0 6 votes vote down vote up
export async function initialize() {
  if (accounts.length > 0) {
    return;
  }

  accounts = await ethers.getSigners();

  const ERC20 = await ethers.getContractFactory<ERC20Mock__factory>("ERC20Mock");

  tokens = await Promise.all(
    [...Array(10).keys()].map((n) => ERC20.deploy(`Token ${n}`, `TOKEN${n}`, getBigNumber(1000000)))
  );

  await deployments.fixture();
  bentoBox = await ethers.getContract<BentoBoxV1>("BentoBoxV1");
  masterDeployer = await ethers.getContract<MasterDeployer>("MasterDeployer");
  factory = await ethers.getContract<ConstantProductPoolFactory>("ConstantProductPoolFactory");
  router = await ethers.getContract<TridentRouter>("TridentRouter");

  // Approve BentoBox token deposits and deposit tokens in bentobox
  await Promise.all(
    tokens.map((token) =>
      token.approve(bentoBox.address, getBigNumber(1000000)).then(() => {
        bentoBox.deposit(token.address, accounts[0].address, accounts[0].address, getBigNumber(500000), 0);
      })
    )
  );

  // Approve Router to spend alice's BentoBox tokens
  await bentoBox.setMasterContractApproval(
    accounts[0].address,
    router.address,
    true,
    "0",
    "0x0000000000000000000000000000000000000000000000000000000000000000",
    "0x0000000000000000000000000000000000000000000000000000000000000000"
  );

  const Pool = await ethers.getContractFactory<ConstantProductPool__factory>("ConstantProductPool");

  // Create pools
  for (let i = 0; i < tokens.length - 1; i++) {
    // Pool deploy data
    let token0, token1;
    if (tokens[i].address < tokens[i + 1].address) {
      token0 = tokens[i];
      token1 = tokens[i + 1];
    } else {
      token0 = tokens[i + 1];
      token1 = tokens[i];
    }
    const deployData = ethers.utils.defaultAbiCoder.encode(
      ["address", "address", "uint256", "bool"],
      [token0.address, token1.address, 30, false]
    );
    const salt = ethers.utils.keccak256(deployData);
    const initCodeHash = ethers.utils.keccak256(Pool.bytecode);
    const poolAddress = ethers.utils.getCreate2Address(factory.address, salt, initCodeHash);
    pools.push(Pool.attach(poolAddress));
    await masterDeployer.deployPool(factory.address, deployData).then((tx) => tx.wait());
    const deployedPoolAddress = (await factory.getPools(token0.address, token1.address, 0, 1))[0];
    expect(poolAddress).eq(deployedPoolAddress);
  }

  // Add initial liquidity of 1k tokens each to every pool
  for (let i = 0; i < pools.length; i++) {
    await addLiquidity(i, getBigNumber(1000), getBigNumber(1000));
  }
}
Example #15
Source File: setMessage.ts    From gateway-sol with GNU General Public License v3.0 6 votes vote down vote up
{execute} = deployments
Example #16
Source File: TridentRouter.test.ts    From trident with GNU General Public License v3.0 5 votes vote down vote up
describe("Router", function () {
  before(async function () {
    await deployments.fixture(["TridentRouter"]);
  });

  beforeEach(async function () {
    //
  });

  describe("receive()", function () {
    it("Succeeds when msg.sender is WETH", async () => {
      const router = await ethers.getContract<TridentRouter>("TridentRouter");
      const weth9 = await ethers.getContract<WETH9>("WETH9");
      const deployer = await ethers.getNamedSigner("deployer");
      await expect(weth9.transfer(router.address, 1)).to.not.be.reverted;
      await expect(router.unwrapWETH(deployer.address)).to.not.be.reverted;
    });
    it("Reverts when msg.sender is not WETH", async () => {
      const router = await ethers.getContract<TridentRouter>("TridentRouter");
      const deployer = await ethers.getNamedSigner("deployer");
      await expect(
        deployer.sendTransaction({
          from: deployer.address,
          to: router.address,
          value: ethers.utils.parseEther("1"),
        })
      ).to.be.revertedWith("NotWethSender");
    });
  });

  describe("#exactInputSingle", function () {
    //
    it("Reverts when output is less than minimum", async () => {
      const router = await ethers.getContract<TridentRouter>("TridentRouter");

      const pool = await initializedConstantProductPool();

      const token0 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token0());

      const bento = await ethers.getContract<BentoBoxV1>("BentoBoxV1");

      const deployer = await ethers.getNamedSigner("deployer");

      await token0.approve(bento.address, "1000000000000000000");
      await bento.deposit(token0.address, deployer.address, deployer.address, "1000000000000000000", "0");

      await bento.whitelistMasterContract(router.address, true);

      await bento.setMasterContractApproval(
        deployer.address,
        router.address,
        true,
        "0",
        "0x0000000000000000000000000000000000000000000000000000000000000000",
        "0x0000000000000000000000000000000000000000000000000000000000000000"
      );

      const exactInputSingleParams = {
        amountIn: "1000000000000000000",
        amountOutMinimum: "1000000000000000000",
        pool: pool.address,
        tokenIn: token0.address,
        data: ethers.utils.defaultAbiCoder.encode(
          ["address", "address", "bool"],
          [token0.address, deployer.address, false]
        ), // (address tokenIn, address recipient, bool unwrapBento) = abi.decode(data, (address, address, bool));
      };

      await expect(router.exactInputSingle(exactInputSingleParams)).to.be.revertedWith(
        customError("TooLittleReceived")
      );
    });
  });

  describe("#exactInput", function () {
    //
  });

  describe("#exactInputLazy", function () {
    //
  });

  describe("#exactInputSingleWithNativeToken", function () {
    //
  });

  describe("#exactInputWithNativeToken", function () {
    //
  });

  describe("#complexPath", function () {
    //
  });

  describe("#addLiquidity", function () {
    it("Reverts when not enough liquidity is minted", async () => {
      const deployer = await ethers.getNamedSigner("deployer");

      const bentoBox = await ethers.getContract<BentoBoxV1>("BentoBoxV1");

      const router = await ethers.getContract<TridentRouter>("TridentRouter");

      await bentoBox.whitelistMasterContract(router.address, true);

      const pool = await initializedConstantProductPool();

      const token0 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token0());
      const token1 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token1());

      await token0.approve(bentoBox.address, 1);
      await token1.approve(bentoBox.address, 1);

      await bentoBox.setMasterContractApproval(
        deployer.address,
        router.address,
        true,
        "0",
        "0x0000000000000000000000000000000000000000000000000000000000000000",
        "0x0000000000000000000000000000000000000000000000000000000000000000"
      );

      const liquidityInput = [
        {
          token: token0.address,
          native: true,
          amount: 1,
        },
        {
          token: token1.address,
          native: true,
          amount: 1,
        },
      ];

      await expect(
        router.addLiquidity(
          liquidityInput,
          pool.address,
          1000,
          ethers.utils.defaultAbiCoder.encode(["address"], [deployer.address])
        )
      ).to.be.revertedWith("NotEnoughLiquidityMinted");
    });

    it("Reverts when update overflows", async () => {
      const deployer = await ethers.getNamedSigner("deployer");

      const bentoBox = await ethers.getContract<BentoBoxV1>("BentoBoxV1");

      const router = await ethers.getContract<TridentRouter>("TridentRouter");

      await bentoBox.whitelistMasterContract(router.address, true);

      const pool = await initializedConstantProductPool();

      const token0 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token0());
      const token1 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token1());

      await token0.approve(bentoBox.address, ethers.BigNumber.from(2).pow(112));
      await token1.approve(bentoBox.address, 1);

      await bentoBox.setMasterContractApproval(
        deployer.address,
        router.address,
        true,
        "0",
        "0x0000000000000000000000000000000000000000000000000000000000000000",
        "0x0000000000000000000000000000000000000000000000000000000000000000"
      );

      const liquidityInput = [
        {
          token: token0.address,
          native: true,
          amount: ethers.BigNumber.from(2).pow(112),
        },
        {
          token: token1.address,
          native: true,
          amount: 1,
        },
      ];

      await expect(
        router.addLiquidity(
          liquidityInput,
          pool.address,
          1000,
          ethers.utils.defaultAbiCoder.encode(["address"], [deployer.address])
        )
      ).to.be.revertedWith("Overflow()");
    });
  });

  describe("#burnLiquidity", function () {
    it("Reverts when an incorrect token order for minWithdrawals is sent", async () => {
      const deployer = await ethers.getNamedSigner("deployer");

      const router = await ethers.getContract<TridentRouter>("TridentRouter");

      const pool = await initializedConstantProductPool();

      const balance = await pool.balanceOf(deployer.address);

      await pool.approve(router.address, balance);

      const token0 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token0());

      const token1 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token1());

      const data = ethers.utils.defaultAbiCoder.encode(["address", "bool"], [deployer.address, false]);

      const minWithdrawals = [
        {
          token: token1.address,
          amount: "0",
        },
        {
          token: token0.address,
          amount: "0",
        },
      ];

      await expect(router.burnLiquidity(pool.address, balance, data, minWithdrawals)).to.be.revertedWith(
        "IncorrectSlippageParams"
      );
    });
    it("Reverts when output is less than minimum", async () => {
      const deployer = await ethers.getNamedSigner("deployer");

      const router = await ethers.getContract<TridentRouter>("TridentRouter");

      const pool = await initializedConstantProductPool();

      const balance = await pool.balanceOf(deployer.address);

      await pool.approve(router.address, balance);

      const token0 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token0());

      const token1 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token1());

      const data = ethers.utils.defaultAbiCoder.encode(["address", "bool"], [deployer.address, false]);

      const minWithdrawals = [
        {
          token: token0.address,
          amount: "1000000000000000000",
        },
        {
          token: token1.address,
          amount: "1000000000000000000",
        },
      ];

      await expect(router.burnLiquidity(pool.address, balance, data, minWithdrawals)).to.be.revertedWith(
        "TooLittleReceived"
      );
    });
  });

  describe("#burnLiquiditySingle", function () {
    it("Reverts when output is less than minimum", async () => {
      const deployer = await ethers.getNamedSigner("deployer");

      const router = await ethers.getContract<TridentRouter>("TridentRouter");

      const pool = await initializedConstantProductPool();

      const balance = await pool.balanceOf(deployer.address);

      await pool.approve(router.address, balance);

      console.log("Deployer pool balance", (await pool.balanceOf(deployer.address)).toString());

      const token0 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token0());

      const data = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "bool"],
        [token0.address, deployer.address, false]
      );

      // Burn whole balance, expect to get back initial 1000000000000000000 token0, but there wouldn't be enough
      await expect(router.burnLiquiditySingle(pool.address, balance, data, "1000000000000000000")).to.be.revertedWith(
        "TooLittleReceived"
      );
    });
  });

  describe("#sweep", function () {
    it("Allows sweep of bentobox erc20 token", async () => {
      const bentoBox = await ethers.getContract<BentoBoxV1>("BentoBoxV1");
      const router = await ethers.getContract<TridentRouter>("TridentRouter");
      const weth9 = await ethers.getContract<WETH9>("WETH9");
      const deployer = await ethers.getNamedSigner("deployer");
      await weth9.approve(bentoBox.address, 1);
      await bentoBox.deposit(weth9.address, deployer.address, router.address, 0, 1);
      await router.sweep(weth9.address, 1, deployer.address, true);
      expect(await bentoBox.balanceOf(weth9.address, deployer.address)).equal(1);
    });
    it("Allows sweep of native eth", async () => {
      const router = await ethers.getContract<TridentRouter>("TridentRouter");
      const carol = await ethers.getNamedSigner("carol");
      const balance = await carol.getBalance();
      // Gifting 1 unit and sweeping it back
      await router.sweep(ADDRESS_ZERO, 1, carol.address, false, { value: 1 });
      // Balance should be plus 1, since the deployer gifted 1 unit and carol sweeped it
      expect(await carol.getBalance()).equal(balance.add(1));
    });
    it("Allows sweeps of regular erc20 token", async () => {
      const router = await ethers.getContract<TridentRouter>("TridentRouter");
      const weth9 = await ethers.getContract<WETH9>("WETH9");
      const deployer = await ethers.getNamedSigner("deployer");
      const balance = await weth9.balanceOf(deployer.address);
      // Gifting 1 unit of WETH
      await weth9.transfer(router.address, 1);
      // Sweeping it back
      await router.sweep(weth9.address, 1, deployer.address, false);
      // Balance should remain the same
      expect(await weth9.balanceOf(deployer.address)).equal(balance);
    });
  });

  describe("#unwrapWETH", function () {
    it("Correctly unwraps weth", async () => {
      const router = await ethers.getContract<TridentRouter>("TridentRouter");
      const weth9 = await ethers.getContract<WETH9>("WETH9");
      await weth9.transfer(router.address, 1);
      const difference = await weth9.balanceOf(router.address);
      const oldBalance = await ethers.provider.getBalance(ethers.constants.AddressZero);
      await expect(router.unwrapWETH(ethers.constants.AddressZero)).to.not.be.reverted;
      const newBalance = await ethers.provider.getBalance(ethers.constants.AddressZero);
      expect(oldBalance.add(difference).eq(newBalance)).to.be.true;
    });
  });

  describe("#approveMasterContract", function () {
    it("Succeed setting master contract approval on bentobox", async () => {
      const deployer = await ethers.getNamedSigner("deployer");

      const chainId = Number(await getChainId());

      const bentoBox = await ethers.getContract<BentoBoxV1>("BentoBoxV1");

      const router = await ethers.getContract<TridentRouter>("TridentRouter");

      await bentoBox.whitelistMasterContract(router.address, true);

      const nonce = await bentoBox.nonces(deployer.address);

      const { v, r, s } = getSignedMasterContractApprovalData(
        bentoBox,
        deployer,
        "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
        router.address,
        true,
        nonce,
        chainId
      );

      await router.approveMasterContract(v, r, s);
    });
  });
});
Example #17
Source File: initializedConstantProductPool.ts    From trident with GNU General Public License v3.0 5 votes vote down vote up
initializedConstantProductPool = deployments.createFixture(
  async (
    {
      deployments,
      ethers: {
        getNamedSigners,
        constants: { MaxUint256 },
      },
    },
    options
  ) => {
    await deployments.fixture(["ConstantProductPoolFactory"], { keepExistingDeployments: true }); // ensure you start from a fresh deployments
    const { deployer } = await getNamedSigners();

    const ERC20 = await ethers.getContractFactory<ERC20Mock__factory>("ERC20Mock");

    const token0 = await ERC20.deploy("Token 0", "TOKEN0", ethers.constants.MaxUint256);
    await token0.deployed();

    const token1 = await ERC20.deploy("Token 1", "TOKEN1", ethers.constants.MaxUint256);
    await token1.deployed();

    const masterDeployer = await ethers.getContract<MasterDeployer>("MasterDeployer");

    const constantProductPoolFactory = await ethers.getContract<ConstantProductPoolFactory>(
      "ConstantProductPoolFactory"
    );

    const deployData = ethers.utils.defaultAbiCoder.encode(
      ["address", "address", "uint256", "bool"],
      [token0.address, token1.address, 0, false]
    );

    const contractReceipt = await masterDeployer
      .deployPool(constantProductPoolFactory.address, deployData)
      .then((tx) => tx.wait());

    const bento = await ethers.getContract<BentoBoxV1>("BentoBoxV1");

    await bento.whitelistMasterContract("0x0000000000000000000000000000000000000001", true);

    await token0.approve(bento.address, MaxUint256).then((tx) => tx.wait());

    await token1.approve(bento.address, MaxUint256).then((tx) => tx.wait());

    await bento
      .setMasterContractApproval(
        deployer.address,
        "0x0000000000000000000000000000000000000001",
        true,
        "0",
        "0x0000000000000000000000000000000000000000000000000000000000000000",
        "0x0000000000000000000000000000000000000000000000000000000000000000"
      )
      .then((tx) => tx.wait());

    await bento
      .deposit(token0.address, deployer.address, deployer.address, "1000000000000000000", 0)
      .then((tx) => tx.wait());

    await bento
      .deposit(token1.address, deployer.address, deployer.address, "1000000000000000000", 0)
      .then((tx) => tx.wait());

    await bento
      .transfer(token0.address, deployer.address, contractReceipt.events?.[0].args?.pool, "1000000000000000000")
      .then((tx) => tx.wait());

    await bento

      .transfer(token1.address, deployer.address, contractReceipt.events?.[0].args?.pool, "1000000000000000000")
      .then((tx) => tx.wait());

    const Pool = await ethers.getContractFactory<ConstantProductPool__factory>("ConstantProductPool");

    const pool = Pool.attach(contractReceipt.events?.[0].args?.pool);

    await pool.mint(ethers.utils.defaultAbiCoder.encode(["address"], [deployer.address])).then((tx) => tx.wait());

    return pool;
  },
  "initializedConstantProductPool"
)
Example #18
Source File: swapKiwiV1.test.ts    From swap.kiwi with GNU General Public License v3.0 5 votes vote down vote up
describe("SwapKiwiV1", async function () {
  let SwapKiwiV1: Deployment;
  let swapKiwi: SwapKiwiV1;
  let TestERC721: Deployment;
  let appUserNFT: TestERC721;
  let otherAppUserNFT: TestERC721;
  let signers: Signer[];
  let appUser: SwapKiwiV1;
  let otherAppUser: SwapKiwiV1;
  let appUserAddress: string;
  let otherAppUserAddress: string;
  const VALID_APP_FEE = ethers.utils.parseEther("0.1");

  before(async () => {
    signers = await ethers.getSigners();
    ({ SwapKiwiV1, TestERC721 } = await deployments.fixture());
    swapKiwi = await ethers.getContractAt(SwapKiwiV1.abi, SwapKiwiV1.address, signers[0]) as SwapKiwiV1;

    appUserNFT = await ethers.getContractAt(TestERC721.abi, TestERC721.address, signers[2]) as TestERC721;
    otherAppUserNFT = await ethers.getContractAt(TestERC721.abi, TestERC721.address, signers[3]) as TestERC721;

    appUser = new ethers.Contract(swapKiwi.address, SwapKiwiV1.abi, signers[2]) as SwapKiwiV1;
    otherAppUser = new ethers.Contract(swapKiwi.address, SwapKiwiV1.abi, signers[3]) as SwapKiwiV1;
    appUserAddress = await signers[2].getAddress();
    otherAppUserAddress = await signers[3].getAddress();
  });

  function getFilterName(eventName: string) {
    let filter: any;
    switch (eventName) {
      case "SwapExecuted":
        filter = swapKiwi.filters.SwapExecuted(null, null, null);
        break;
      case "SwapCanceled":
        filter = swapKiwi.filters.SwapCanceled(null, null);
        break;
      case "SwapProposed":
        filter = swapKiwi.filters.SwapProposed(null, null, null, null, null, null);
        break;
      case "SwapInitiated":
        filter = swapKiwi.filters.SwapInitiated(null, null, null, null, null, null);
      default: null
    }
    return filter;
  }

  async function getEventWithArgsFromLogs(txReceipt: TransactionReceipt, eventName: string): Promise<any | null> {
    if (txReceipt.logs) {
      const events = await swapKiwi.queryFilter(getFilterName(eventName), undefined);
      return events.map((e) => {
        if (e.event == eventName) {
          return {
            eventName: eventName,
            args: e.args
          }
        }
      }
      ).pop()
    }
    return null;
  }

  it("Should successfuly set app fee if caller is the owner", async function () {
    await swapKiwi.setAppFee(VALID_APP_FEE);
    expect((await swapKiwi.fee()).toString()).to.be.deep.equal(VALID_APP_FEE.toString());
  });

  it("Should fail to set app fee if caller is not owner", async function () {
    const nonOwnerContract = new ethers.Contract(SwapKiwiV1.address, SwapKiwiV1.abi, signers[6]) as SwapKiwiV1;

    await expect(nonOwnerContract.setAppFee(1000))
      .to.be.rejectedWith("Ownable: caller is not the owner");
  });

  it('Should fail to propose swap with invalid app fee', async function () {
    await expect(appUser.proposeSwap(otherAppUserAddress, [], [], {
      value: parseEther("0.01")
    })).to.be.rejectedWith(
      "SwapKiwi: Sent ETH amount needs to be more or equal application fee"
    );
  });

  it('Should fail to propose swap with different nft address and if length', async function () {
    await expect(appUser.proposeSwap(otherAppUserAddress, [], [13], {
      value: VALID_APP_FEE
    })).to.be.rejectedWith(
      "SwapKiwi: NFT and ID arrays have to be same length"
    );
  });

  it('Should succesfully deposit NFT into escrow contract and emit "SwapProposed" event', async function () {
    await appUserNFT.mint(appUserAddress, 25);
    await appUserNFT.approve(swapKiwi.address, 25);
    expect(await appUserNFT.ownerOf(25)).to.be.deep.equal(appUserAddress);

    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [25], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");

    // check if all values are emitted in event
    expect(logs.eventName).to.be.deep.equal("SwapProposed");
    expect(logs.args.from).to.be.deep.equal(appUserAddress);
    expect(logs.args.to).to.be.deep.equal(otherAppUserAddress);
    expect(logs.args.nftAddresses[0]).to.be.deep.equal(appUserNFT.address);
    expect(logs.args.nftIds[0].toString()).to.be.deep.equal("25");
    expect(await appUserNFT.ownerOf(25)).to.be.deep.equal(swapKiwi.address);
  });

  it('Should succesfully cancel swap by first user (after swap proposed) and emit "SwapCanceled" event', async function () {
    await appUserNFT.mint(appUserAddress, 140);
    await appUserNFT.approve(swapKiwi.address, 140);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [140], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    const cancelTx = await appUser.cancelSwap(swapIdFromLogs);
    const cancelTxReceipt = await cancelTx.wait(1);
    const cancelTxlogs = await getEventWithArgsFromLogs(cancelTxReceipt, "SwapCanceled");

    // check if all values are emitted in event
    expect(cancelTxlogs.eventName).to.be.deep.equal("SwapCanceled");
    expect(cancelTxlogs.args.canceledBy).to.be.deep.equal(appUserAddress);
    // expect that swap ID from "SwapCanceled" is same as swap ID from "swapProposed" event
    expect(cancelTxlogs.args.swapId.toString()).to.be.deep.equal(String(swapIdFromLogs));
    // check that NFT is returned to initial owner
    expect(await appUserNFT.ownerOf(140)).to.be.deep.equal(appUserAddress);
  });

  it('Should succesfully cancel swap by second user (after swap proposed) and emit "SwapCanceled" event', async function () {
    await appUserNFT.mint(appUserAddress, 141);
    await appUserNFT.approve(swapKiwi.address, 141);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [141], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    const cancelTx = await otherAppUser.cancelSwap(swapIdFromLogs);
    const cancelTxReceipt = await cancelTx.wait(1);
    const cancelTxlogs = await getEventWithArgsFromLogs(cancelTxReceipt, "SwapCanceled");

    // check if all values are emitted in event
    expect(cancelTxlogs.eventName).to.be.deep.equal("SwapCanceled");
    expect(cancelTxlogs.args.canceledBy).to.be.deep.equal(otherAppUserAddress);
    // expect that swap ID from "SwapCanceled" is same as swap ID from "swapProposed" event
    expect(cancelTxlogs.args.swapId.toString()).to.be.deep.equal(String(swapIdFromLogs));
    // check that NFT is returned to initial owner
    expect(await appUserNFT.ownerOf(141)).to.be.deep.equal(appUserAddress);
  });

  it('Should succesfully cancel swap by first user (after swap initiated) and emit "SwapCanceled" event', async function () {
    await appUserNFT.mint(appUserAddress, 120);
    await appUserNFT.approve(swapKiwi.address, 120);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [120], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await otherAppUserNFT.mint(otherAppUserAddress, 130);
    await otherAppUserNFT.mint(otherAppUserAddress, 131);
    await otherAppUserNFT.approve(swapKiwi.address, 130);
    await otherAppUserNFT.approve(swapKiwi.address, 131);
    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [otherAppUserNFT.address, otherAppUserNFT.address],
      [130, 131],
      {
        value: VALID_APP_FEE
      }
    );
    const initiateSwapTxReceipt = await initiateSwapTx.wait(1);
    const initiateSwapLogs = await getEventWithArgsFromLogs(initiateSwapTxReceipt, "SwapInitiated");
    // check if all values are emitted in "SwapInitiated" event
    expect(initiateSwapLogs.eventName).to.be.deep.equal("SwapInitiated");
    expect(initiateSwapLogs.args.from).to.be.deep.equal(otherAppUserAddress);
    expect(initiateSwapLogs.args.to).to.be.deep.equal(appUserAddress);

    const cancelTx = await otherAppUser.cancelSwap(swapIdFromLogs);
    const cancelTxReceipt = await cancelTx.wait(1);
    const cancelTxlogs = await getEventWithArgsFromLogs(cancelTxReceipt, "SwapCanceled");

    // check if all values are emitted in event
    expect(cancelTxlogs.eventName).to.be.deep.equal("SwapCanceled");
    expect(cancelTxlogs.args.canceledBy).to.be.deep.equal(otherAppUserAddress);
    // expect that swap ID from "SwapCanceled" is same as swap ID from "swapProposed" event
    expect(cancelTxlogs.args.swapId.toString()).to.be.deep.equal(String(swapIdFromLogs));
    // check that NFT is returned to initial owners
    expect(await appUserNFT.ownerOf(120)).to.be.deep.equal(appUserAddress);
    expect(await appUserNFT.ownerOf(130)).to.be.deep.equal(otherAppUserAddress);
    expect(await appUserNFT.ownerOf(131)).to.be.deep.equal(otherAppUserAddress);
  });

  it('Should succesfully cancel swap by second user (after swap initiated) and emit "SwapCanceled" event', async function () {
    await appUserNFT.mint(appUserAddress, 121);
    await appUserNFT.approve(swapKiwi.address, 121);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [121], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await otherAppUserNFT.mint(otherAppUserAddress, 135);
    await otherAppUserNFT.mint(otherAppUserAddress, 136);
    await otherAppUserNFT.approve(swapKiwi.address, 135);
    await otherAppUserNFT.approve(swapKiwi.address, 136);
    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [otherAppUserNFT.address, otherAppUserNFT.address],
      [135, 136],
      {
        value: VALID_APP_FEE
      }
    );
    const initiateSwapTxReceipt = await initiateSwapTx.wait(1);
    const initiateSwapLogs = await getEventWithArgsFromLogs(initiateSwapTxReceipt, "SwapInitiated");
    // check if all values are emitted in "SwapInitiated" event
    expect(initiateSwapLogs.eventName).to.be.deep.equal("SwapInitiated");
    expect(initiateSwapLogs.args.from).to.be.deep.equal(otherAppUserAddress);
    expect(initiateSwapLogs.args.to).to.be.deep.equal(appUserAddress);

    const cancelTx = await otherAppUser.cancelSwap(swapIdFromLogs);
    const cancelTxReceipt = await cancelTx.wait(1);
    const cancelTxlogs = await getEventWithArgsFromLogs(cancelTxReceipt, "SwapCanceled");

    // check if all values are emitted in event
    expect(cancelTxlogs.eventName).to.be.deep.equal("SwapCanceled");
    expect(cancelTxlogs.args.canceledBy).to.be.deep.equal(otherAppUserAddress);
    // expect that swap ID from "SwapCanceled" is same as swap ID from "swapProposed" event
    expect(cancelTxlogs.args.swapId.toString()).to.be.deep.equal(String(swapIdFromLogs));
    // check that NFT is returned to initial owners
    expect(await appUserNFT.ownerOf(121)).to.be.deep.equal(appUserAddress);
    expect(await appUserNFT.ownerOf(135)).to.be.deep.equal(otherAppUserAddress);
    expect(await appUserNFT.ownerOf(136)).to.be.deep.equal(otherAppUserAddress);
  });

  it('Should succesfully cancel swap created with ether value', async function () {
    const firstUserBalance = await appUser.signer.getBalance();
    const secondUserBalance = await otherAppUser.signer.getBalance();

    await appUserNFT.mint(appUserAddress, 430);
    await appUserNFT.approve(swapKiwi.address, 430);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [430], {
      value: VALID_APP_FEE.add(parseEther("20"))
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await otherAppUserNFT.mint(otherAppUserAddress, 431);
    await otherAppUserNFT.approve(swapKiwi.address, 431);
    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [otherAppUserNFT.address],
      [431],
      {
        value: VALID_APP_FEE.add(parseEther("10"))
      }
    );
    const initiateSwapTxReceipt = await initiateSwapTx.wait(1);
    await getEventWithArgsFromLogs(initiateSwapTxReceipt, "SwapInitiated");

    const cancelTx = await otherAppUser.cancelSwap(swapIdFromLogs);
    const cancelTxReceipt = await cancelTx.wait(1);
    await getEventWithArgsFromLogs(cancelTxReceipt, "SwapCanceled");

    expect(await appUserNFT.ownerOf(430)).to.be.deep.equal(appUserAddress);
    expect(await appUserNFT.ownerOf(431)).to.be.deep.equal(otherAppUserAddress);
    expect(firstUserBalance.sub(await appUser.signer.getBalance()).lt(parseEther("1"))).to.be.equal(true);
    expect(secondUserBalance.sub(await otherAppUser.signer.getBalance()).lt(parseEther("1"))).to.be.equal(true);
  });

  it('Should fail to initiate swap if swap canceled', async function () {
    await appUserNFT.mint(appUserAddress, 170);
    await appUserNFT.approve(swapKiwi.address, 170);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [170], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());
    const cancelTx = await appUser.cancelSwap(swapIdFromLogs);
    await cancelTx.wait(1);

    await otherAppUserNFT.mint(otherAppUserAddress, 301);
    await otherAppUserNFT.approve(swapKiwi.address, 301);
    await expect(otherAppUser.initiateSwap(swapIdFromLogs, [otherAppUserNFT.address], [301], {
      value: VALID_APP_FEE
    })).to.be.rejectedWith(
      `SwapKiwi: caller is not swap participator`
    );
  });

  it('Should fail to initiate swap with invalid app fee', async function () {
    const tx = await appUser.proposeSwap(otherAppUserAddress, [], [], {
      value: VALID_APP_FEE.add(parseEther("0.1"))
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await expect(otherAppUser.initiateSwap(swapIdFromLogs, [], [], {
      value: parseEther("0.01")
    })).to.be.rejectedWith(
      `SwapKiwi: Sent ETH amount needs to be more or equal application fee`
    );
  });

  it('Should fail to initiate swap twice', async function () {
    await appUserNFT.mint(appUserAddress, 189);
    await appUserNFT.approve(swapKiwi.address, 189);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [189], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());
    await otherAppUserNFT.mint(otherAppUserAddress, 302);
    await otherAppUserNFT.approve(swapKiwi.address, 302);
    await otherAppUser.initiateSwap(swapIdFromLogs, [otherAppUserNFT.address], [302], {
      value: VALID_APP_FEE
    })


    await otherAppUserNFT.mint(otherAppUserAddress, 303);
    await otherAppUserNFT.approve(swapKiwi.address, 303);
    await expect(otherAppUser.initiateSwap(swapIdFromLogs, [otherAppUserNFT.address], [303], {
      value: VALID_APP_FEE
    })).to.be.rejectedWith(
      "SwapKiwi: swap already initiated"
    );
  });

  it('Should fail to initiate swap twice if proposed only with ether', async function () {
    await appUserNFT.mint(appUserAddress, 1732);
    await appUserNFT.approve(swapKiwi.address, 1732);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [1732], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());
    await otherAppUser.initiateSwap(swapIdFromLogs, [], [], {
      value: VALID_APP_FEE.add(ethers.utils.parseEther("0.25"))
    })


    await otherAppUserNFT.mint(otherAppUserAddress, 1733);
    await otherAppUserNFT.approve(swapKiwi.address, 1733);
    await expect(otherAppUser.initiateSwap(swapIdFromLogs, [otherAppUserNFT.address], [1733], {
      value: VALID_APP_FEE
    })).to.be.rejectedWith(
      "SwapKiwi: swap already initiated"
    );
  });

  it('Should fail to cancel swap twice', async function () {
    await appUserNFT.mint(appUserAddress, 200);
    await appUserNFT.approve(swapKiwi.address, 200);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [200], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    const cancelTx = await appUser.cancelSwap(swapIdFromLogs);
    const cancelTxReceipt = await cancelTx.wait(1);
    const cancelTxlogs = await getEventWithArgsFromLogs(cancelTxReceipt, "SwapCanceled");

    // check if all values are emitted in event
    expect(cancelTxlogs.eventName).to.be.deep.equal("SwapCanceled");
    expect(cancelTxlogs.args.canceledBy).to.be.deep.equal(appUserAddress);
    // expect that swap ID from "SwapCanceled" is same as swap ID from "swapProposed" event
    expect(cancelTxlogs.args.swapId.toString()).to.be.deep.equal(String(swapIdFromLogs));
    // check that NFT is returned to initial owner
    expect(await appUserNFT.ownerOf(200)).to.be.deep.equal(appUserAddress);

    await expect(appUser.cancelSwap(swapIdFromLogs)).to.be.rejectedWith(
      "SwapKiwi: Can't cancel swap, must be swap participant"
    );
  });

  it("Should fail to cancel swap if user is not a swap participant", async function () {
    const nonSwapParticipant = new ethers.Contract(SwapKiwiV1.address, SwapKiwiV1.abi, signers[6]) as SwapKiwiV1;

    // first user NFT minting and swap deposit into SwapKiwi
    await appUserNFT.mint(appUserAddress, 70);
    await appUserNFT.approve(swapKiwi.address, 70);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [70], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await expect(nonSwapParticipant.cancelSwap(swapIdFromLogs)).to.be.rejectedWith(
      "SwapKiwi: Can't cancel swap, must be swap participant");
  });

  it("Should fail to accept swap if second user didn't add NFTs or ether", async function () {
    // first user NFT minting and swap deposit into SwapKiwi
    await appUserNFT.mint(appUserAddress, 2000);
    await appUserNFT.approve(swapKiwi.address, 2000);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [2000], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await expect(appUser.acceptSwap(swapIdFromLogs)).to.be.rejectedWith(
      "SwapKiwi: Can't accept swap, both participants didn't add NFTs");
  });

  it('Should fail to accept swap if not swap initiator', async function () {
    await appUserNFT.mint(appUserAddress, 2100);
    await appUserNFT.approve(swapKiwi.address, 2100);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [2100], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [],
      [],
      {
        value: VALID_APP_FEE.add(parseEther("50"))
      }
    );
    await initiateSwapTx.wait(1);

    await expect(otherAppUser.acceptSwap(swapIdFromLogs)).to.be.rejectedWith(
      "SwapKiwi: caller is not swap initiator"
    );
  });

  it('Should successfully execute NFT - NFT swap', async function () {
    // first user NFT minting and swap deposit into SwapKiwi
    await appUserNFT.mint(appUserAddress, 85);
    await appUserNFT.mint(appUserAddress, 86);
    await appUserNFT.approve(swapKiwi.address, 85);
    await appUserNFT.approve(swapKiwi.address, 86);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address, appUserNFT.address], [85, 86], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    // check that first user NFTs are deposited into SwapKiwi
    expect(await appUserNFT.ownerOf(85)).to.be.deep.equal(swapKiwi.address);
    expect(await appUserNFT.ownerOf(86)).to.be.deep.equal(swapKiwi.address);

    // second user NFT minting and swap deposit into SwapKiwi
    await otherAppUserNFT.mint(otherAppUserAddress, 87);
    await otherAppUserNFT.mint(otherAppUserAddress, 88);
    await otherAppUserNFT.approve(swapKiwi.address, 87);
    await otherAppUserNFT.approve(swapKiwi.address, 88);
    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [otherAppUserNFT.address, otherAppUserNFT.address],
      [87, 88],
      {
        value: VALID_APP_FEE
      }
    );
    const initiateSwapTxReceipt = await initiateSwapTx.wait(1);
    const initiateSwapLogs = await getEventWithArgsFromLogs(initiateSwapTxReceipt, "SwapInitiated");
    // check if all values are emitted in "SwapInitiated" event
    expect(initiateSwapLogs.eventName).to.be.deep.equal("SwapInitiated");
    expect(initiateSwapLogs.args.from).to.be.deep.equal(otherAppUserAddress);
    expect(initiateSwapLogs.args.to).to.be.deep.equal(appUserAddress);

    // check that second user NFTs are deposited into SwapKiwi
    expect(await otherAppUserNFT.ownerOf(87)).to.be.deep.equal(swapKiwi.address);
    expect(await otherAppUserNFT.ownerOf(88)).to.be.deep.equal(swapKiwi.address);

    const acceptSwapTx = await appUser.acceptSwap(swapIdFromLogs);
    const acceptSwapTxReceipt = await acceptSwapTx.wait(1);
    const acceptSwapLogs = await getEventWithArgsFromLogs(acceptSwapTxReceipt, "SwapExecuted");

    // check if all values are emitted in "SwapExecuted" event
    expect(acceptSwapLogs.eventName).to.be.deep.equal("SwapExecuted");
    expect(acceptSwapLogs.args.from).to.be.deep.equal(appUserAddress);
    expect(acceptSwapLogs.args.to).to.be.deep.equal(otherAppUserAddress);
    // check that NFTs are transfered from SwapKiwi to participants - same address because both have same signer

    expect(await otherAppUserNFT.ownerOf(85)).to.be.deep.equal(otherAppUserAddress);
    expect(await otherAppUserNFT.ownerOf(86)).to.be.deep.equal(otherAppUserAddress);
    expect(await appUserNFT.ownerOf(87)).to.be.deep.equal(appUserAddress);
    expect(await appUserNFT.ownerOf(88)).to.be.deep.equal(appUserAddress);
  });

  it('Should successfully execute NFT + ether - NFT + ether swap', async function () {
    const firstUserBalance = await appUser.signer.getBalance();
    const secondUserBalance = await otherAppUser.signer.getBalance();

    await appUserNFT.mint(appUserAddress, 375);
    await appUserNFT.approve(swapKiwi.address, 375);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [375], {
      value: VALID_APP_FEE.add(parseEther("50"))
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await otherAppUserNFT.mint(otherAppUserAddress, 376);
    await otherAppUserNFT.approve(swapKiwi.address, 376);
    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [otherAppUserNFT.address],
      [376],
      {
        value: VALID_APP_FEE.add(parseEther("25"))
      }
    );
    await initiateSwapTx.wait(1);

    const acceptSwapTx = await appUser.acceptSwap(swapIdFromLogs);
    await acceptSwapTx.wait(1);

    expect(await appUserNFT.ownerOf(375)).to.be.deep.equal(otherAppUserAddress);
    expect(await otherAppUserNFT.ownerOf(376)).to.be.deep.equal(appUserAddress);
    expect(firstUserBalance.sub((await appUser.signer.getBalance()).add(parseEther("25"))).lt(parseEther("1"))).to.be.equal(true);
    expect(secondUserBalance.sub((await otherAppUser.signer.getBalance()).sub(parseEther("25"))).lt(parseEther("1"))).to.be.equal(true);
  });

  it('Should successfully execute ether - NFT swap', async function () {
    const secondUserBalance = await otherAppUser.signer.getBalance();

    const tx = await appUser.proposeSwap(otherAppUserAddress, [], [], {
      value: VALID_APP_FEE.add(parseEther("50"))
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await otherAppUserNFT.mint(otherAppUserAddress, 1800);
    await otherAppUserNFT.approve(swapKiwi.address, 1800);
    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [otherAppUserNFT.address],
      [1800],
      {
        value: VALID_APP_FEE
      }
    );
    await initiateSwapTx.wait(1);

    const acceptSwapTx = await appUser.acceptSwap(swapIdFromLogs);
    await acceptSwapTx.wait(1);

    expect(await otherAppUserNFT.ownerOf(1800)).to.be.deep.equal(appUserAddress);
    expect(
      (
        await otherAppUser.signer.getBalance()
      ).sub(secondUserBalance).sub(parseEther("50")).lt(parseEther("1"))).to.be.equal(true);
  });

  it('Should successfully execute NFT - ether swap', async function () {
    const firstUserBalance = await appUser.signer.getBalance();

    await appUserNFT.mint(appUserAddress, 1822);
    await appUserNFT.approve(swapKiwi.address, 1822);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [1822], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [],
      [],
      {
        value: VALID_APP_FEE.add(parseEther("50"))
      }
    );
    await initiateSwapTx.wait(1);

    const acceptSwapTx = await appUser.acceptSwap(swapIdFromLogs);
    await acceptSwapTx.wait(1);

    expect(await appUserNFT.ownerOf(1822)).to.be.deep.equal(otherAppUserAddress);
    expect(
      (
        await appUser.signer.getBalance()
      ).sub(firstUserBalance).sub(parseEther("50")).lt(parseEther("1"))).to.be.equal(true);
  });

  it("Should successfully withdraw only collected fees", async function () {
    await swapKiwi.withdrawEther(await signers[7].getAddress());

    const tx1 = await appUser.proposeSwap(otherAppUserAddress, [], [], {
      value: VALID_APP_FEE.add(ethers.utils.parseEther("1"))
    });
    const txReceipt1 = await tx1.wait(1);
    const logs1 = await getEventWithArgsFromLogs(txReceipt1, "SwapProposed");
    const swapIdFromLogs1 = Number(logs1.args.swapId.toString());
    const initiateSwapTx1 = await otherAppUser.initiateSwap(
      swapIdFromLogs1,
      [],
      [],
      {
        value: VALID_APP_FEE.add(parseEther("5"))
      }
    );
    await initiateSwapTx1.wait(1);
    const acceptSwapTx1 = await appUser.acceptSwap(swapIdFromLogs1);
    await acceptSwapTx1.wait(1);
    const tx2 = await appUser.proposeSwap(otherAppUserAddress, [], [], {
      value: VALID_APP_FEE.add(ethers.utils.parseEther("1"))
    });
    const txReceipt2 = await tx2.wait(1);
    const logs2 = await getEventWithArgsFromLogs(txReceipt2, "SwapProposed");
    const swapIdFromLogs = Number(logs2.args.swapId.toString());
    const initiateSwapTx2 = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [],
      [],
      {
        value: VALID_APP_FEE.add(parseEther("5"))
      }
    );
    await initiateSwapTx2.wait(1);

    await swapKiwi.withdrawEther(appUserNFT.address)

    expect((await ethers.provider.getBalance(appUserNFT.address)).toString())
      .to.be.deep.equal(VALID_APP_FEE.mul(4).toString())
  });

  it("Should fail to withdraw collected fees if not owner", async function () {
    await expect(appUser.withdrawEther(appUser.address))
      .to.be.rejectedWith(
        "Ownable: caller is not the owner");
  });

  it("Should fail to withdraw collected fees if sent to zero address", async function () {
    await expect(swapKiwi.withdrawEther("0x0000000000000000000000000000000000000000"))
      .to.be.rejectedWith("SwapKiwi: transfer to the zero address");
  });
});
Example #19
Source File: ConstantProductPoolFactory.test.ts    From trident with GNU General Public License v3.0 5 votes vote down vote up
describe("Constant Product Pool Factory", function () {
  before(async () => {
    //
  });

  beforeEach(async () => {
    await deployments.fixture(["ConstantProductPoolFactory", "ConstantProductPoolFactoryHelper"]);
  });

  describe("#deployPool", function () {
    it("swaps tokens which are passed in the wrong order", async function () {
      const masterDeployer = await ethers.getContract<MasterDeployer>("MasterDeployer");

      const constantProductPoolFactory = await ethers.getContract<ConstantProductPoolFactory>(
        "ConstantProductPoolFactory"
      );

      const token1 = "0x0000000000000000000000000000000000000001";
      const token2 = "0x0000000000000000000000000000000000000002";

      const deployData = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "uint256", "bool"],
        [token2, token1, 30, false]
      );

      const tx = await (await masterDeployer.deployPool(constantProductPoolFactory.address, deployData)).wait();

      const constantProductPool = await ethers.getContractAt<ConstantProductPool>(
        "ConstantProductPool",
        tx.events?.[0]?.args?.pool
      );

      expect(await constantProductPool.token0()).to.equal(token1);
      expect(await constantProductPool.token1()).to.equal(token2);
    });

    it("getPoolsForTokens test", async function () {
      const token1 = "0x0000000000000000000000000000000000000001";
      const token2 = "0x0000000000000000000000000000000000000002";
      const token3 = "0x0000000000000000000000000000000000000003";
      const token4 = "0x0000000000000000000000000000000000000004";

      const masterDeployer = await ethers.getContract<MasterDeployer>("MasterDeployer");
      const constantProductPoolFactory = await ethers.getContract<ConstantProductPoolFactory>(
        "ConstantProductPoolFactory"
      );

      const constantProductPoolFactoryHelper = await ethers.getContract<ConstantProductPoolFactoryHelper>(
        "ConstantProductPoolFactoryHelper"
      );

      let deployData = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "uint256", "bool"],
        [token1, token2, 30, false]
      );
      await (await masterDeployer.deployPool(constantProductPoolFactory.address, deployData)).wait();

      deployData = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "uint256", "bool"],
        [token1, token2, 25, false]
      );
      await (await masterDeployer.deployPool(constantProductPoolFactory.address, deployData)).wait();

      deployData = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "uint256", "bool"],
        [token1, token2, 25, true]
      );
      await (await masterDeployer.deployPool(constantProductPoolFactory.address, deployData)).wait();

      deployData = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "uint256", "bool"],
        [token2, token3, 30, false]
      );
      await (await masterDeployer.deployPool(constantProductPoolFactory.address, deployData)).wait();

      const [res, length] = await constantProductPoolFactoryHelper.getPoolsForTokens(
        constantProductPoolFactory.address,
        [token1, token2, token3, token4]
      );
      expect(length).equal(4);
      expect(res.length).equal(4);
    });

    it("reverts when token0 is zero", async function () {
      const masterDeployer = await ethers.getContract<MasterDeployer>("MasterDeployer");

      const constantProductPoolFactory = await ethers.getContract<ConstantProductPoolFactory>(
        "ConstantProductPoolFactory"
      );

      const deployData = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "uint256", "bool"],
        ["0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000001", 30, false]
      );

      await expect(masterDeployer.deployPool(constantProductPoolFactory.address, deployData)).to.be.revertedWith(
        "ZeroAddress()"
      );
    });

    it("reverts when token1 is zero", async function () {
      const masterDeployer = await ethers.getContract<MasterDeployer>("MasterDeployer");

      const constantProductPoolFactory = await ethers.getContract<ConstantProductPoolFactory>(
        "ConstantProductPoolFactory"
      );

      const deployData = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "uint256", "bool"],
        ["0x0000000000000000000000000000000000000001", "0x0000000000000000000000000000000000000000", 30, false]
      );

      await expect(masterDeployer.deployPool(constantProductPoolFactory.address, deployData)).to.be.revertedWith(
        "ZeroAddress()"
      );
    });

    it("has pool count of 0", async function () {
      const constantProductPoolFactory = await ethers.getContract<ConstantProductPoolFactory>(
        "ConstantProductPoolFactory"
      );
      expect(
        await constantProductPoolFactory.poolsCount(
          "0x0000000000000000000000000000000000000001",
          "0x0000000000000000000000000000000000000002"
        )
      ).to.equal(0);
    });

    it("has pool count of 1 after pool deployed", async function () {
      const masterDeployer = await ethers.getContract<MasterDeployer>("MasterDeployer");

      const constantProductPoolFactory = await ethers.getContract<ConstantProductPoolFactory>(
        "ConstantProductPoolFactory"
      );

      const deployData = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "uint256", "bool"],
        ["0x0000000000000000000000000000000000000001", "0x0000000000000000000000000000000000000002", 30, false]
      );

      await masterDeployer.deployPool(constantProductPoolFactory.address, deployData);

      expect(
        await constantProductPoolFactory.poolsCount(
          "0x0000000000000000000000000000000000000001",
          "0x0000000000000000000000000000000000000002"
        )
      ).to.equal(1);
    });
  });

  describe("#configAddress", function () {
    //
  });
});
Example #20
Source File: ConstantProductPool.test.ts    From trident with GNU General Public License v3.0 5 votes vote down vote up
describe("Constant Product Pool", () => {
  before(async () => {
    console.log("Deploying ConstantProductPoolFactory fixture");
    await deployments.fixture(["ConstantProductPoolFactory"]);
    console.log("Deployed ConstantProductPoolFactory fixture");
  });

  beforeEach(async () => {
    //
  });

  describe("#instantiation", () => {
    it("reverts if token0 is zero", async () => {
      const cppFactory = await ethers.getContract<ConstantProductPoolFactory>("ConstantProductPoolFactory");
      const deployData = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "uint256", "bool"],
        ["0x0000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000", 30, false]
      );
      await expect(cppFactory.deployPool(deployData)).to.be.revertedWith("ZeroAddress()");
    });

    it("deploys if token1 is zero", async () => {
      const cppFactory = await ethers.getContract<ConstantProductPoolFactory>("ConstantProductPoolFactory");
      const deployData = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "uint256", "bool"],
        ["0x0000000000000000000000000000000000000001", "0x0000000000000000000000000000000000000000", 30, false]
      );
      await expect(cppFactory.deployPool(deployData)).to.be.revertedWith("ZeroAddress()");
    });

    it("reverts if token0 and token1 are identical", async () => {
      const cppFactory = await ethers.getContract<ConstantProductPoolFactory>("ConstantProductPoolFactory");
      const deployData = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "uint256", "bool"],
        ["0x0000000000000000000000000000000000000001", "0x0000000000000000000000000000000000000001", 30, false]
      );
      await expect(cppFactory.deployPool(deployData)).to.be.revertedWith("IdenticalAddress()");
    });
    it("reverts if swap fee more than the max fee", async () => {
      const cppFactory = await ethers.getContract<ConstantProductPoolFactory>("ConstantProductPoolFactory");
      const deployData = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "uint256", "bool"],
        ["0x0000000000000000000000000000000000000001", "0x0000000000000000000000000000000000000002", 10001, false]
      );
      await expect(cppFactory.deployPool(deployData)).to.be.revertedWith("InvalidSwapFee()");
    });
  });

  describe("#mint", function () {
    it("reverts if total supply is 0 and one of the token amounts are 0 - token 0", async () => {
      const pool = await uninitializedConstantProductPool();

      const bentoBox = await ethers.getContract<BentoBoxV1>("BentoBoxV1");

      const token0 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token0());

      const newLocal = "0x0000000000000000000000000000000000000003";

      await token0.transfer(bentoBox.address, 1000);

      await bentoBox.deposit(token0.address, bentoBox.address, pool.address, 1000, 0);

      const mintData = ethers.utils.defaultAbiCoder.encode(["address"], [newLocal]);
      await expect(pool.mint(mintData)).to.be.revertedWith("InvalidAmounts()");
    });

    it("reverts if total supply is 0 and one of the token amounts are 0 - token 1", async () => {
      const pool = await uninitializedConstantProductPool();

      const bentoBox = await ethers.getContract<BentoBoxV1>("BentoBoxV1");

      const token1 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token1());

      const newLocal = "0x0000000000000000000000000000000000000003";

      await token1.transfer(bentoBox.address, 1000);

      await bentoBox.deposit(token1.address, bentoBox.address, pool.address, 1000, 0);

      const mintData = ethers.utils.defaultAbiCoder.encode(["address"], [newLocal]);
      await expect(pool.mint(mintData)).to.be.revertedWith("InvalidAmounts()");
    });

    it("reverts if insufficient liquidity minted", async () => {
      const deployer = await ethers.getNamedSigner("deployer");
      const pool = await initializedConstantProductPool();
      const token0 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token0());
      const token1 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token1());
      await token0.transfer(pool.address, 1);
      await token1.transfer(pool.address, 1);
      const mintData = ethers.utils.defaultAbiCoder.encode(["address"], [deployer.address]);
      await expect(pool.mint(mintData)).to.be.revertedWith("InsufficientLiquidityMinted()");
    });
  });

  describe("#burn", function () {
    //
  });

  describe("#burnSingle", function () {
    //
  });

  describe("#swap", function () {
    it("reverts on uninitialized", async () => {
      const pool = await uninitializedConstantProductPool();
      const data = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "bool"],
        [await pool.token0(), "0x0000000000000000000000000000000000000000", false]
      );
      await expect(pool.swap(data)).to.be.revertedWith("PoolUninitialized()");
    });
  });

  describe("#flashSwap", function () {
    it("reverts on uninitialized", async () => {
      const pool = await uninitializedConstantProductPool();
      const data = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "bool", "uint256", "bytes"],
        [await pool.token0(), "0x0000000000000000000000000000000000000000", false, 0, "0x"]
      );
      await expect(pool.flashSwap(data)).to.be.revertedWith("PoolUninitialized()");
    });

    it("reverts on invalid input token", async () => {
      const pool = await initializedConstantProductPool();
      const ERC20 = await ethers.getContractFactory<ERC20Mock__factory>("ERC20Mock");
      const token2 = await ERC20.deploy("Token 2", "TOKEN2", ethers.constants.MaxUint256);
      await token2.deployed();

      const data = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "bool", "uint256", "bytes"],
        [token2.address, "0x0000000000000000000000000000000000000000", false, 0, "0x"]
      );

      await expect(pool.flashSwap(data)).to.be.revertedWith("InvalidInputToken()");
    });

    it("reverts on insuffiecient amount in token 0", async () => {
      const pool = await initializedConstantProductPool();
      const token0 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token0());
      const bento = await ethers.getContract<BentoBoxV1>("BentoBoxV1");

      const FlashSwapMock = await ethers.getContractFactory<FlashSwapMock__factory>("FlashSwapMock");
      const flashSwapMock = await FlashSwapMock.deploy(bento.address);
      await flashSwapMock.deployed();
      const flashSwapData = ethers.utils.defaultAbiCoder.encode(
        ["bool", "address", "bool"],
        [false, "0x0000000000000000000000000000000000000000", false]
      );
      const data = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "bool", "uint256", "bytes"],
        [token0.address, flashSwapMock.address, false, 1, flashSwapData]
      );

      await expect(flashSwapMock.testFlashSwap(pool.address, data)).to.be.revertedWith("InsufficientAmountIn()");
    });

    it("reverts on insuffiecient amount in token 1", async () => {
      const pool = await initializedConstantProductPool();
      const token1 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token1());
      const bento = await ethers.getContract<BentoBoxV1>("BentoBoxV1");

      const FlashSwapMock = await ethers.getContractFactory<FlashSwapMock__factory>("FlashSwapMock");
      const flashSwapMock = await FlashSwapMock.deploy(bento.address);
      await flashSwapMock.deployed();
      const flashSwapData = ethers.utils.defaultAbiCoder.encode(
        ["bool", "address", "bool"],
        [false, "0x0000000000000000000000000000000000000000", false]
      );
      const data = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "bool", "uint256", "bytes"],
        [token1.address, flashSwapMock.address, false, 1, flashSwapData]
      );

      await expect(flashSwapMock.testFlashSwap(pool.address, data)).to.be.revertedWith("InsufficientAmountIn()");
    });

    it("succeeds in flash swapping token 0 native", async () => {
      const pool = await initializedConstantProductPool();
      const token0 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token0());
      const token1 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token1());
      const bento = await ethers.getContract<BentoBoxV1>("BentoBoxV1");

      const FlashSwapMock = await ethers.getContractFactory<FlashSwapMock__factory>("FlashSwapMock");
      const flashSwapMock = await FlashSwapMock.deploy(bento.address);
      await flashSwapMock.deployed();
      await token0.transfer(flashSwapMock.address, 100);

      const flashSwapData = ethers.utils.defaultAbiCoder.encode(
        ["bool", "address", "bool"],
        [true, token0.address, false]
      );
      const data = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "bool", "uint256", "bytes"],
        [token0.address, flashSwapMock.address, true, 100, flashSwapData]
      );
      await flashSwapMock.testFlashSwap(pool.address, data);
      expect(await token1.balanceOf(flashSwapMock.address)).to.be.eq(99);
    });

    it("succeeds in flash swapping token 0 bento", async () => {
      const pool = await initializedConstantProductPool();
      const token1 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token1());
      const token0 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token0());
      const bento = await ethers.getContract<BentoBoxV1>("BentoBoxV1");

      const FlashSwapMock = await ethers.getContractFactory<FlashSwapMock__factory>("FlashSwapMock");
      const flashSwapMock = await FlashSwapMock.deploy(bento.address);
      await flashSwapMock.deployed();
      await token0.transfer(bento.address, 100);
      await bento.deposit(token0.address, bento.address, flashSwapMock.address, 100, 0);

      const flashSwapData = ethers.utils.defaultAbiCoder.encode(
        ["bool", "address", "bool"],
        [true, token0.address, true]
      );
      const data = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "bool", "uint256", "bytes"],
        [token0.address, flashSwapMock.address, false, 100, flashSwapData]
      );
      await flashSwapMock.testFlashSwap(pool.address, data);
      expect(await bento.balanceOf(token1.address, flashSwapMock.address)).to.be.eq(99);
    });

    it("succeeds in flash swapping token 1 native", async () => {
      const pool = await initializedConstantProductPool();
      const token0 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token0());
      const token1 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token1());
      const bento = await ethers.getContract<BentoBoxV1>("BentoBoxV1");

      const FlashSwapMock = await ethers.getContractFactory<FlashSwapMock__factory>("FlashSwapMock");
      const flashSwapMock = await FlashSwapMock.deploy(bento.address);
      await flashSwapMock.deployed();
      await token1.transfer(flashSwapMock.address, 100);

      const flashSwapData = ethers.utils.defaultAbiCoder.encode(
        ["bool", "address", "bool"],
        [true, token1.address, false]
      );
      const data = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "bool", "uint256", "bytes"],
        [token1.address, flashSwapMock.address, true, 100, flashSwapData]
      );

      await flashSwapMock.testFlashSwap(pool.address, data);
      expect(await token0.balanceOf(flashSwapMock.address)).to.be.eq(99);
    });

    it("succeeds in flash swapping token 1 bento", async () => {
      const pool = await initializedConstantProductPool();
      const token1 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token1());
      const token0 = await ethers.getContractAt<ERC20Mock>("ERC20Mock", await pool.token0());
      const bento = await ethers.getContract<BentoBoxV1>("BentoBoxV1");

      const FlashSwapMock = await ethers.getContractFactory<FlashSwapMock__factory>("FlashSwapMock");
      const flashSwapMock = await FlashSwapMock.deploy(bento.address);
      await flashSwapMock.deployed();
      await token1.transfer(bento.address, 100);
      await bento.deposit(token1.address, bento.address, flashSwapMock.address, 100, 0);

      const flashSwapData = ethers.utils.defaultAbiCoder.encode(
        ["bool", "address", "bool"],
        [true, token1.address, true]
      );
      const data = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "bool", "uint256", "bytes"],
        [token1.address, flashSwapMock.address, false, 100, flashSwapData]
      );
      await flashSwapMock.testFlashSwap(pool.address, data);
      expect(await bento.balanceOf(token0.address, flashSwapMock.address)).to.be.eq(99);
    });
  });

  describe("#poolIdentifier", function () {
    //
  });

  describe("#getAssets", function () {
    it("returns the assets the pool was deployed with, and in the correct order", async () => {
      const ConstantProductPool = await ethers.getContractFactory<ConstantProductPool__factory>("ConstantProductPool");
      const cppFactory = await ethers.getContract<ConstantProductPoolFactory>("ConstantProductPoolFactory");
      const masterDeployer = await ethers.getContract<MasterDeployer>("MasterDeployer");
      const deployData = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "uint256", "bool"],
        ["0x0000000000000000000000000000000000000002", "0x0000000000000000000000000000000000000001", 30, false]
      );
      await masterDeployer.deployPool(cppFactory.address, deployData);
      const addy = await cppFactory.calculatePoolAddress(
        "0x0000000000000000000000000000000000000001",
        "0x0000000000000000000000000000000000000002",
        30,
        false
      );
      const constantProductPool = ConstantProductPool.attach(addy);

      const assets = await constantProductPool.getAssets();

      await expect(assets[0], "0x0000000000000000000000000000000000000001");
      await expect(assets[1], "0x0000000000000000000000000000000000000002");
    });
  });

  describe("#updateBarParameters", () => {
    it("mutates bar fee if changed on master deployer", async () => {
      const pool = await initializedConstantProductPool();

      const masterDeployer = await ethers.getContract<MasterDeployer>("MasterDeployer");

      const { barFeeTo, bob } = await getNamedAccounts();

      expect(await pool.barFee()).equal(1667);

      expect(await pool.barFeeTo()).equal(barFeeTo);

      await masterDeployer.setBarFee(10).then((tx) => tx.wait());
      await masterDeployer.setBarFeeTo(bob).then((tx) => tx.wait());

      expect(await masterDeployer.barFee()).equal(10);
      expect(await masterDeployer.barFeeTo()).equal(bob);

      expect(await pool.barFee()).equal(1667);
      expect(await pool.barFeeTo()).equal(barFeeTo);

      await pool.updateBarParameters().then((tx) => tx.wait());

      expect(await pool.barFee()).equal(10);
      expect(await pool.barFeeTo()).equal(bob);

      // reset

      await masterDeployer.setBarFee(1667).then((tx) => tx.wait());
      await masterDeployer.setBarFeeTo(barFeeTo).then((tx) => tx.wait());

      expect(await masterDeployer.barFee()).equal(1667);
      expect(await masterDeployer.barFeeTo()).equal(barFeeTo);

      await pool.updateBarParameters().then((tx) => tx.wait());

      expect(await pool.barFee()).equal(1667);
      expect(await pool.barFeeTo()).equal(barFeeTo);
    });
  });

  describe("#getAmountOut", function () {
    it("returns 1000000000 given input of token0 in 1e18:1e18 pool, with bar fee 0 & swap fee 0", async () => {
      const pool = await initializedConstantProductPool();
      const reserves = await pool.getReserves();
      expect(
        await pool.getAmountOut(
          ethers.utils.defaultAbiCoder.encode(["address", "uint256"], [await pool.token0(), "1000000000"])
        )
      ).to.equal("999999999"); // 999999999
    });
    it("returns 999999999 given input of token1 in 1e18:1e18 pool, with bar fee 0 & swap fee 0", async () => {
      const pool = await initializedConstantProductPool();
      const reserves = await pool.getReserves();
      expect(
        await pool.getAmountOut(
          ethers.utils.defaultAbiCoder.encode(["address", "uint256"], [await pool.token1(), "1000000000"])
        )
      ).to.equal("999999999"); // 999999999
    });
    it("reverts if tokenIn is not equal to token0 and token1", async () => {
      const ConstantProductPool = await ethers.getContractFactory<ConstantProductPool__factory>("ConstantProductPool");
      const masterDeployer = await ethers.getContract<MasterDeployer>("MasterDeployer");
      const deployData = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "uint256", "bool"],
        ["0x0000000000000000000000000000000000000001", "0x0000000000000000000000000000000000000002", 30, false]
      );
      const cppFactory = await ethers.getContract<ConstantProductPoolFactory>("ConstantProductPoolFactory");
      await masterDeployer.deployPool(cppFactory.address, deployData);
      const addy = await cppFactory.calculatePoolAddress(
        "0x0000000000000000000000000000000000000001",
        "0x0000000000000000000000000000000000000002",
        30,
        false
      );
      const constantProductPool = ConstantProductPool.attach(addy);

      const data = ethers.utils.defaultAbiCoder.encode(
        ["address", "uint256"],
        ["0x0000000000000000000000000000000000000003", 0]
      );
      await expect(constantProductPool.getAmountOut(data)).to.be.revertedWith("InvalidInputToken()");
    });
  });

  describe("#getAmountIn", function () {
    it("returns 1000000002 given output of token0 in 1e18:1e18 pool, with bar fee 0 & swap fee 0", async () => {
      const pool = await initializedConstantProductPool();
      expect(
        await pool.getAmountIn(
          ethers.utils.defaultAbiCoder.encode(["address", "uint256"], [await pool.token0(), "1000000000"])
        )
      ).to.equal("1000000002"); // 1000000002
    });

    it("returns 1000000000 given output of token1 in 1e18:1e18 pool, with bar fee 0 & swap fee 0", async () => {
      const pool = await initializedConstantProductPool();
      expect(
        await pool.getAmountIn(
          ethers.utils.defaultAbiCoder.encode(["address", "uint256"], [await pool.token1(), "1000000000"])
        )
      ).to.equal("1000000002"); // 1000000002
    });
    it("reverts if tokenOut is not equal to token 1 and token0", async () => {
      const ConstantProductPool = await ethers.getContractFactory<ConstantProductPool__factory>("ConstantProductPool");
      const masterDeployer = await ethers.getContract<MasterDeployer>("MasterDeployer");
      const deployData = ethers.utils.defaultAbiCoder.encode(
        ["address", "address", "uint256", "bool"],
        ["0x0000000000000000000000000000000000000001", "0x0000000000000000000000000000000000000002", 30, false]
      );
      const cppFactory = await ethers.getContract<ConstantProductPoolFactory>("ConstantProductPoolFactory");
      await masterDeployer.deployPool(cppFactory.address, deployData);
      const addy = await cppFactory.calculatePoolAddress(
        "0x0000000000000000000000000000000000000001",
        "0x0000000000000000000000000000000000000002",
        30,
        false
      );
      const constantProductPool = ConstantProductPool.attach(addy);
      const data = ethers.utils.defaultAbiCoder.encode(
        ["address", "uint256"],
        ["0x0000000000000000000000000000000000000003", 0]
      );
      await expect(constantProductPool.getAmountIn(data)).to.be.revertedWith("InvalidOutputToken()");
    });
  });

  describe("#getNativeReserves", function () {
    it("returns expected values for initilisedConstantProductPool", async () => {
      const pool = await initializedConstantProductPool();
      const [_nativeReserve0, _nativeReserve1, _blockTimestampLast] = await pool.getNativeReserves();
      expect(_nativeReserve0).equal("1000000000000000000");
      expect(_nativeReserve1).equal("1000000000000000000");
      expect(_blockTimestampLast).equal(0);
    });
  });
});
Example #21
Source File: 001_deploy_gateways.ts    From gateway-sol with GNU General Public License v3.0 5 votes vote down vote up
deployGatewaySol = async function (
    hre: HardhatRuntimeEnvironment,
    config?: NetworkConfig,
    logger: ConsoleInterface = console
) {
    const { getNamedAccounts, ethers, network } = hre;

    logger.log(`Deploying to ${network.name}...`);

    const Ox = ethers.utils.getAddress;

    config = config || networks[network.name as "hardhat"];
    if (!config) {
        throw new Error(`No network configuration found for ${network.name}!`);
    }

    const { mintAuthority, chainName, create2SaltOverride } = config;
    const chainId: number = (await ethers.provider.getNetwork()).chainId;
    const { deployer } = await getNamedAccounts();

    if (Ox(mintAuthority) === Ox0) {
        throw new Error(`Invalid empty mintAuthority.`);
    }

    const create2 = setupCreate2(hre, create2SaltOverride, logger);
    const getExistingDeployment = setupGetExistingDeployment(hre);
    const waitForTx = setupWaitForTx(logger);

    // Deploy RenTimelock ////////////////////////////////////////////////
    logger.log(chalk.yellow("RenTimelock"));
    const renTimelock = await create2<RenTimelock__factory>("RenTimelock", [0, [deployer], [deployer]]);

    let governanceAddress: string;
    if (network.name === "hardhat") {
        governanceAddress = deployer;
    } else {
        governanceAddress = renTimelock.address;
    }

    // Deploy RenProxyAdmin ////////////////////////////////////////////////
    logger.log(chalk.yellow("RenProxyAdmin"));

    const renProxyAdmin =
        (await getExistingDeployment<RenProxyAdmin>("RenProxyAdmin")) ||
        (await create2<RenProxyAdmin__factory>("RenProxyAdmin", [governanceAddress]));
    const deployProxy = setupDeployProxy(hre, create2, renProxyAdmin, logger);

    // This should only be false on hardhat.
    const create2IsContract = (await ethers.provider.getCode(CREATE2_DEPLOYER)).replace(/^0x/, "").length > 0;

    // Deploy RenAssetProxyBeacon ////////////////////////////////////////////////
    logger.log(chalk.yellow("RenAsset beacon"));
    const renAssetImplementation = await create2<RenAssetV2__factory>("RenAssetV2", []);
    const existingRenAssetProxyBeacon = await getExistingDeployment<RenAssetProxyBeacon>("RenAssetProxyBeacon");
    const renAssetProxyBeacon =
        existingRenAssetProxyBeacon ||
        (await create2<RenAssetProxyBeacon__factory>("RenAssetProxyBeacon", [
            // Temporary value, gets overwritten in the next step. This is to
            // ensure that the address doesn't change between networks even when
            // the implementation changes.
            create2IsContract ? CREATE2_DEPLOYER : renAssetImplementation.address,
            deployer,
        ]));
    if (Ox(await renAssetProxyBeacon.implementation()) !== Ox(renAssetImplementation.address)) {
        logger.log(`Updating RenAsset implementation to ${Ox(renAssetImplementation.address)}`);
        await waitForTx(renAssetProxyBeacon.upgradeTo(renAssetImplementation.address));
    }

    // Deploy MintGatewayProxyBeacon /////////////////////////////////////////////
    logger.log(chalk.yellow("MintGateway beacon"));
    const mintGatewayImplementation = await create2("MintGatewayV3", []);
    const existingMintGatewayProxyBeacon = await getExistingDeployment<MintGatewayProxyBeacon>(
        "MintGatewayProxyBeacon"
    );
    const mintGatewayProxyBeacon =
        existingMintGatewayProxyBeacon ||
        (await create2<MintGatewayProxyBeacon__factory>("MintGatewayProxyBeacon", [
            // Temporary value, gets overwritten in the next step. This is to
            // ensure that the address doesn't change between networks even when
            // the implementation changes.
            create2IsContract ? CREATE2_DEPLOYER : mintGatewayImplementation.address,
            deployer,
        ]));
    if (Ox(await mintGatewayProxyBeacon.implementation()) !== Ox(mintGatewayImplementation.address)) {
        logger.log(`Updating MintGateway implementation to ${Ox(mintGatewayImplementation.address)}`);
        await waitForTx(mintGatewayProxyBeacon.upgradeTo(mintGatewayImplementation.address));
    }

    // Deploy LockGatewayProxyBeacon /////////////////////////////////////////////
    logger.log(chalk.yellow("LockGateway beacon"));
    const lockGatewayImplementation = await create2("LockGatewayV3", []);
    const existingLockGatewayProxyBeacon = await getExistingDeployment<LockGatewayProxyBeacon>(
        "LockGatewayProxyBeacon"
    );
    const lockGatewayProxyBeacon =
        existingLockGatewayProxyBeacon ||
        (await create2<LockGatewayProxyBeacon__factory>("LockGatewayProxyBeacon", [
            // Temporary value, gets overwritten in the next step. This is to
            // ensure that the address doesn't change between networks even when
            // the implementation changes.
            create2IsContract ? CREATE2_DEPLOYER : lockGatewayImplementation.address,
            deployer,
        ]));
    if (Ox(await lockGatewayProxyBeacon.implementation()) !== Ox(lockGatewayImplementation.address)) {
        logger.log(`Updating LockGateway implementation to ${Ox(lockGatewayImplementation.address)}`);
        await waitForTx(lockGatewayProxyBeacon.upgradeTo(lockGatewayImplementation.address));
    }

    logger.log(chalk.yellow("Signature Verifier"));
    const signatureVerifier = await deployProxy<RenVMSignatureVerifierV1__factory>(
        "RenVMSignatureVerifierV1",
        "RenVMSignatureVerifierProxy",
        {
            initializer: "__RenVMSignatureVerifier_init",
            constructorArgs: [chainName, mintAuthority, governanceAddress] as Parameters<
                RenVMSignatureVerifierV1["__RenVMSignatureVerifier_init"]
            >,
        },
        // isInitialized:
        async (signatureVerifier) => Ox(await signatureVerifier.getMintAuthority()) === Ox(mintAuthority)
    );
    const signatureVerifierOwner = Ox(await signatureVerifier.owner());
    if (signatureVerifierOwner !== Ox(governanceAddress)) {
        logger.log(
            `Transferring RenVMSignatureVerifier ownership to governance address. (Was ${signatureVerifierOwner}, changing to ${Ox(
                governanceAddress
            )}) Deployer: ${deployer}.`
        );
        await waitForTx(signatureVerifier.transferOwnership(governanceAddress));
    }

    logger.log(chalk.yellow("TransferWithLog"));
    const transferWithLog = await create2("TransferWithLog", []);

    // Deploy GatewayRegistry ////////////////////////////////////////////////////
    logger.log(chalk.yellow("GatewayRegistry"));
    const gatewayRegistry = await deployProxy<GatewayRegistryV2__factory>(
        "GatewayRegistryV2",
        "GatewayRegistryProxy",
        {
            initializer: "__GatewayRegistry_init",
            constructorArgs: [
                chainId,
                signatureVerifier.address,
                transferWithLog.address,
                renAssetProxyBeacon.address,
                mintGatewayProxyBeacon.address,
                lockGatewayProxyBeacon.address,
                governanceAddress,
                [deployer],
            ] as Parameters<GatewayRegistryV2["__GatewayRegistry_init"]>,
        },
        async (gatewayRegistry) => {
            try {
                await gatewayRegistry.getSignatureVerifier();
                return true;
            } catch (error: any) {
                return false;
            }
        }
    );
    const existingSignatureVerifier = Ox(await gatewayRegistry.getSignatureVerifier());
    if (existingSignatureVerifier !== Ox(signatureVerifier.address)) {
        logger.log(
            `Updating signature verifier in gateway registry. Was ${existingSignatureVerifier}, updating to ${Ox(
                signatureVerifier.address
            )}.`
        );
        await waitForTx(gatewayRegistry.updateSignatureVerifier(signatureVerifier.address));
    }

    const existingTransferWithLog = Ox(await gatewayRegistry.getTransferContract());
    if (existingTransferWithLog !== Ox(transferWithLog.address)) {
        logger.log(
            `Updating TransferWithLog in gateway registry. Was ${existingTransferWithLog}, updating to ${Ox(
                transferWithLog.address
            )}.`
        );
        await waitForTx(gatewayRegistry.updateTransferContract(transferWithLog.address));
    }

    if (Ox(await gatewayRegistry.getRenAssetProxyBeacon()) !== Ox(renAssetProxyBeacon.address)) {
        throw new Error(`GatewayRegistry's renAssetProxyBeacon address is not correct.`);
    }
    if (Ox(await gatewayRegistry.getMintGatewayProxyBeacon()) !== Ox(mintGatewayProxyBeacon.address)) {
        throw new Error(`GatewayRegistry's mintGatewayProxyBeacon address is not correct.`);
    }
    if (Ox(await gatewayRegistry.getLockGatewayProxyBeacon()) !== Ox(lockGatewayProxyBeacon.address)) {
        throw new Error(`GatewayRegistry's lockGatewayProxyBeacon address is not correct.`);
    }

    if (Ox(await renAssetProxyBeacon.getProxyDeployer()) !== Ox(gatewayRegistry.address)) {
        logger.log(`Granting deployer role to gateway registry in renAssetProxyBeacon.`);
        await waitForTx(renAssetProxyBeacon.updateProxyDeployer(gatewayRegistry.address));
    }
    if ((await renAssetProxyBeacon.owner()) !== governanceAddress) {
        logger.log(`Transferring renAssetProxyBeacon ownership to timelock.`);
        await waitForTx(renAssetProxyBeacon.transferOwnership(governanceAddress));
    }

    if (Ox(await mintGatewayProxyBeacon.getProxyDeployer()) !== Ox(gatewayRegistry.address)) {
        logger.log(`Granting deployer role to gateway registry in mintGatewayProxyBeacon.`);
        await waitForTx(mintGatewayProxyBeacon.updateProxyDeployer(gatewayRegistry.address));
    }
    if ((await mintGatewayProxyBeacon.owner()) !== governanceAddress) {
        logger.log(`Transferring mintGatewayProxyBeacon ownership to timelock.`);
        await waitForTx(mintGatewayProxyBeacon.transferOwnership(governanceAddress));
    }

    if (Ox(await lockGatewayProxyBeacon.getProxyDeployer()) !== Ox(gatewayRegistry.address)) {
        logger.log(`Granting deployer role to gateway registry in lockGatewayProxyBeacon.`);
        await waitForTx(lockGatewayProxyBeacon.updateProxyDeployer(gatewayRegistry.address));
    }
    if ((await lockGatewayProxyBeacon.owner()) !== governanceAddress) {
        logger.log(`Transferring lockGatewayProxyBeacon ownership to timelock.`);
        await waitForTx(lockGatewayProxyBeacon.transferOwnership(governanceAddress));
    }

    logger.log(`Deploying contract verification helper contract.`);
    const beaconProxy = await create2<BeaconProxy__factory>("BeaconProxy", [renAssetProxyBeacon!.address, []]);
    const renAssetProxy = await getContractAt(hre)<RenAssetV2>("RenAssetV2", beaconProxy.address);
    if ((await renAssetProxy.symbol()) === "") {
        logger.log(`Initializing contract verification helper contract.`);
        await waitForTx(renAssetProxy.__RenAsset_init(0, "1", "TESTDEPLOYMENT", "TESTDEPLOYMENT", 0, deployer));
    }
    const beaconDeployment = await deployments.get("BeaconProxy");

    logger.log(`Handling ${(config.mintGateways || []).length} mint assets.`);
    const prefix = config.tokenPrefix;
    for (const { symbol, gateway, token, decimals, version } of config.mintGateways) {
        logger.log(chalk.yellow(`Mint asset: ${symbol}`));
        const existingGateway = Ox(await gatewayRegistry.getMintGatewayBySymbol(symbol));
        const existingToken = Ox(await gatewayRegistry.getRenAssetBySymbol(symbol));
        if ((await gatewayRegistry.getGatewayBySymbol(symbol)) === Ox0) {
            const prefixedSymbol = `${prefix}${symbol}`;
            if (!token) {
                logger.log(
                    `Calling deployMintGatewayAndRenAsset(${symbol}, ${prefixedSymbol}, ${prefixedSymbol}, ${decimals}, '1')`
                );
                await waitForTx(
                    gatewayRegistry.deployMintGatewayAndRenAsset(
                        symbol,
                        prefixedSymbol,
                        prefixedSymbol,
                        decimals,
                        version || "1"
                    )
                );
            } else if (!gateway) {
                logger.log(`Calling deployMintGateway(${symbol}, ${token}, '1')`);
                await waitForTx(gatewayRegistry.deployMintGateway(symbol, token, "1"));
            } else {
                logger.log(`Calling addMintGateway(${symbol}, ${token}, ${gateway})`);
                await waitForTx(gatewayRegistry.addMintGateway(symbol, token, gateway));
            }
        } else {
            logger.log(`Skipping ${symbol} - ${existingGateway}, ${existingToken}!`);
        }

        const updatedGateway = existingGateway || Ox(await gatewayRegistry.getMintGatewayBySymbol(symbol));
        const updatedToken = existingToken || Ox(await gatewayRegistry.getRenAssetBySymbol(symbol));

        const gatewayLabel = `ren${symbol}_MintGateway_Proxy`;
        await deployments.save(gatewayLabel, {
            ...beaconDeployment,
            address: updatedGateway,
            metadata: beaconDeployment.metadata && beaconDeployment.metadata.replace("BeaconProxy", gatewayLabel),
        });

        const tokenLabel = `ren${symbol}_Proxy`;
        await deployments.save(tokenLabel, {
            ...beaconDeployment,
            address: updatedToken,
            metadata: beaconDeployment.metadata && beaconDeployment.metadata.replace("BeaconProxy", tokenLabel),
        });

        // Update signature verifier.
        const gatewayInstance = await getContractAt(hre)<MintGatewayV3>("MintGatewayV3", updatedGateway);
        const existingSignatureVerifier = Ox(await gatewayInstance.getSignatureVerifier());
        if (existingSignatureVerifier !== Ox(signatureVerifier.address)) {
            logger.log(
                `Updating signature verifier in the ${symbol} mint gateway. Was ${existingSignatureVerifier}, updating to ${Ox(
                    signatureVerifier.address
                )}.`
            );
            await waitForTx(gatewayInstance.updateSignatureVerifier(signatureVerifier.address));
        }

        // Update selector hash, by updating symbol.
        const expectedSelectorHash = keccak256(
            Buffer.concat([Buffer.from(symbol), Buffer.from("/to"), Buffer.from(chainName)])
        );
        if (expectedSelectorHash !== (await gatewayInstance.getSelectorHash())) {
            await gatewayInstance.updateAsset(symbol);
        }
    }

    // Test Tokens are deployed when a particular lock-asset doesn't exist on a
    // testnet.
    logger.log(`Handling ${(config.lockGateways || []).length} lock assets.`);
    for (const { symbol, gateway, token, decimals } of config.lockGateways || []) {
        logger.log(chalk.yellow(`Lock asset: ${symbol}`));
        const existingGateway = Ox(await gatewayRegistry.getLockGatewayBySymbol(symbol));
        const existingToken = Ox(await gatewayRegistry.getLockAssetBySymbol(symbol));
        const totalSupply = typeof token === "object" ? token.totalSupply : undefined;
        let deployedToken = typeof token === "string" ? token : "";
        if (existingGateway === Ox0) {
            // Check token symbol and decimals
            if (token && typeof token === "string") {
                const erc20 = await ethers.getContractAt<ERC20>("ERC20", token);
                const erc20Symbol = await erc20.symbol();
                if (erc20Symbol !== symbol && symbol !== "FTT") {
                    console.error(`Expected ${token.slice(0, 10)}...'s symbol to be ${symbol}, got ${erc20Symbol}`);
                }
                const erc20Decimals = await erc20.decimals();
                if (erc20Decimals !== decimals) {
                    console.error(
                        `Expected ${token.slice(0, 10)}...'s decimals to be ${decimals}, got ${erc20Decimals}`
                    );
                }
            }

            if (typeof token !== "string") {
                logger.log(`Deploying TestToken(${symbol}, ${decimals}, ${totalSupply})`);
                const supply = new BigNumber(
                    totalSupply !== undefined ? totalSupply.replace(/,/g, "") : 1000000
                ).shiftedBy(decimals !== undefined ? decimals : 18);
                const deployedTokenInstance = await create2<TestToken__factory>("TestToken", [
                    symbol,
                    symbol,
                    decimals,
                    supply.toFixed(),
                    deployer,
                ]);
                deployedToken = deployedTokenInstance.address;
            }

            if (!gateway) {
                logger.log(`Calling deployLockGateway(${symbol}, ${deployedToken}, '1')`);
                await waitForTx(gatewayRegistry.deployLockGateway(symbol, deployedToken, "1"));
            } else {
                logger.log(`Calling addLockGateway(${symbol}, ${deployedToken}, ${gateway})`);
                await waitForTx(gatewayRegistry.addLockGateway(symbol, deployedToken, gateway));
            }
        } else {
            logger.log(`Skipping ${symbol} - ${existingGateway}, ${existingToken}!`);
        }

        const updatedGateway = existingGateway || Ox(await gatewayRegistry.getLockGatewayBySymbol(symbol));
        const gatewayLabel = `ren${symbol}_LockGateway_Proxy`;
        await deployments.save(gatewayLabel, {
            ...beaconDeployment,
            address: updatedGateway,
            metadata: beaconDeployment.metadata && beaconDeployment.metadata.replace("BeaconProxy", gatewayLabel),
        });

        // Update signature verifier.
        const gatewayInstance = await getContractAt(hre)<LockGatewayV3>("LockGatewayV3", updatedGateway);
        const existingSignatureVerifier = Ox(await gatewayInstance.getSignatureVerifier());
        if (existingSignatureVerifier !== Ox(signatureVerifier.address)) {
            logger.log(
                `Updating signature verifier in the ${symbol} lock gateway. Was ${existingSignatureVerifier}, updating to ${Ox(
                    signatureVerifier.address
                )}.`
            );
            await waitForTx(gatewayInstance.updateSignatureVerifier(signatureVerifier.address));
        }

        // Update selector hash, by updating symbol.
        const expectedSelectorHash = keccak256(
            Buffer.concat([Buffer.from(symbol), Buffer.from("/to"), Buffer.from(chainName)])
        );
        if (expectedSelectorHash !== (await gatewayInstance.getSelectorHash())) {
            await gatewayInstance.updateAsset(symbol);
        }
    }

    // await gatewayRegistry.deployMintGatewayAndRenAsset(
    //   'BTC',
    //   'renBTC',
    //   'renBTC',
    //   8,
    //   '1'
    // );
    // await gatewayRegistry.deployMintGatewayAndRenAsset(
    //   'ZEC',
    //   'renZEC',
    //   'renZEC',
    //   8,
    //   '1'
    // );
    // await gatewayRegistry.deployMintGatewayAndRenAsset(
    //   'BCH',
    //   'renBCH',
    //   'renBCH',
    //   8,
    //   '1'
    // );
    // await gatewayRegistry.deployMintGatewayAndRenAsset(
    //   'DGB',
    //   'renDGB',
    //   'renDGB',
    //   8,
    //   '1'
    // );
    // await gatewayRegistry.deployMintGatewayAndRenAsset(
    //   'FIL',
    //   'renFIL',
    //   'renFIL',
    //   18,
    //   '1'
    // );
    // await gatewayRegistry.deployMintGatewayAndRenAsset(
    //   'LUNA',
    //   'renLUNA',
    //   'renLUNA',
    //   6,
    //   '1'
    // );
    // await gatewayRegistry.deployMintGatewayAndRenAsset(
    //   'DOGE',
    //   'renDOGE',
    //   'renDOGE',
    //   8,
    //   '1'
    // );

    // await gatewayRegistry.deployMintGatewayAndRenAsset(
    //   'ETH',
    //   'renETH',
    //   'renETH',
    //   18,
    //   '1'
    // );

    // await gatewayRegistry.deployMintGatewayAndRenAsset(
    //   'DAI',
    //   'renDAI',
    //   'renDAI',
    //   18,
    //   '1'
    // );

    // await usdc.initialize('USDC', 'USDC');
    // await gatewayRegistry.deployLockGateway('DAI', usdc.address, '1');

    // await usdc.mint(deployer, '100', {from: deployer});

    // const usdcLockGatewayAddress = await gatewayRegistry.getLockGatewayBySymbol(
    //   'USDC'
    // );
    // const usdcGateway = await ethers.getContractAt<LockGatewayV3>(
    //   'LockGatewayV3',
    //   usdcLockGatewayAddress,
    //   deployer
    // );

    // await usdc.approve(usdcGateway.address, '10', {from: deployer});
    // await usdcGateway.lock('8123', 'Bitcoin', [], '10', {from: deployer});

    logger.log("mint gateway symbols:", await gatewayRegistry.getMintGatewaySymbols(0, 0));
    const lockSymbols = await gatewayRegistry.getLockGatewaySymbols(0, 0);
    logger.log("lock gateway symbols:", lockSymbols);
    for (const lockSymbol of lockSymbols) {
        const lockToken = await gatewayRegistry.getLockAssetBySymbol(lockSymbol);
        logger.log(`${lockSymbol}: ${lockToken}`);
    }

    const basicBridge = await create2<BasicBridge__factory>("BasicBridge", [gatewayRegistry.address]);

    return {
        gatewayRegistry,
        basicBridge,
        signatureVerifier,
    };
}
Example #22
Source File: swapKiwiWithOnlyERC721.test.ts    From swap.kiwi with GNU General Public License v3.0 5 votes vote down vote up
describe("SwapKiwi-With only ERC721 to compare SwapKiwiV1", async function () {
  let SwapKiwi: Deployment;
  let swapKiwi: SwapKiwi;
  let TestERC721: Deployment;
  let appUserNFT: TestERC721;
  let otherAppUserNFT: TestERC721;
  let signers: Signer[];
  let appUser: SwapKiwi;
  let otherAppUser: SwapKiwi;
  let appUserAddress: string;
  let otherAppUserAddress: string;
  const VALID_APP_FEE = ethers.utils.parseEther("0.1");

  before(async () => {
    signers = await ethers.getSigners();
    ({ SwapKiwi, TestERC721 } = await deployments.fixture());
    swapKiwi = await ethers.getContractAt(SwapKiwi.abi, SwapKiwi.address, signers[0]) as SwapKiwi;

    appUserNFT = await ethers.getContractAt(TestERC721.abi, TestERC721.address, signers[2]) as TestERC721;
    otherAppUserNFT = await ethers.getContractAt(TestERC721.abi, TestERC721.address, signers[3]) as TestERC721;

    appUser = new ethers.Contract(swapKiwi.address, SwapKiwi.abi, signers[2]) as SwapKiwi;
    otherAppUser = new ethers.Contract(swapKiwi.address, SwapKiwi.abi, signers[3]) as SwapKiwi;
    appUserAddress = await signers[2].getAddress();
    otherAppUserAddress = await signers[3].getAddress();
  });

  function getFilterName(eventName: string) {
    let filter: any;
    switch (eventName) {
      case "SwapExecuted":
        filter = swapKiwi.filters.SwapExecuted(null, null, null);
        break;
      case "SwapCanceled":
        filter = swapKiwi.filters.SwapCanceled(null, null);
        break;
      case "SwapProposed":
        filter = swapKiwi.filters.SwapProposed(null, null, null, null, null, null, null);
        break;
      case "SwapInitiated":
        filter = swapKiwi.filters.SwapInitiated(null, null, null, null, null, null, null);
      default: null
    }
    return filter;
  }

  async function getEventWithArgsFromLogs(txReceipt: TransactionReceipt, eventName: string): Promise<any | null> {
    if (txReceipt.logs) {
      const events = await swapKiwi.queryFilter(getFilterName(eventName), undefined);
      return events.map((e) => {
        if (e.event == eventName) {
          return {
            eventName: eventName,
            args: e.args
          }
        }
      }
      ).pop()
    }
    return null;
  }

  it("Should successfuly set app fee if caller is the owner", async function () {
    await swapKiwi.setAppFee(VALID_APP_FEE);
    expect((await swapKiwi.fee()).toString()).to.be.deep.equal(VALID_APP_FEE.toString());
  });

  it("Should fail to set app fee if caller is not owner", async function () {
    const nonOwnerContract = new ethers.Contract(SwapKiwi.address, SwapKiwi.abi, signers[6]) as SwapKiwi;

    await expect(nonOwnerContract.setAppFee(1000))
      .to.be.rejectedWith("Ownable: caller is not the owner");
  });

  it('Should fail to propose swap with invalid app fee', async function () {
    await expect(appUser.proposeSwap(otherAppUserAddress, [], [], [], {
      value: parseEther("0.01")
    })).to.be.rejectedWith(
      "SwapKiwi: Sent ETH amount needs to be more or equal application fee"
    );
  });

  it('Should fail to propose swap with different nft address and if length', async function () {
    await expect(appUser.proposeSwap(otherAppUserAddress, [], [13], [], {
      value: VALID_APP_FEE
    })).to.be.rejectedWith(
      "SwapKiwi: NFT and ID arrays have to be same length"
    );
  });

  it('Should succesfully deposit NFT into escrow contract and emit "SwapProposed" event', async function () {
    await appUserNFT.mint(appUserAddress, 25);
    await appUserNFT.approve(swapKiwi.address, 25);
    expect(await appUserNFT.ownerOf(25)).to.be.deep.equal(appUserAddress);

    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [25], [0], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");

    // check if all values are emitted in event
    expect(logs.eventName).to.be.deep.equal("SwapProposed");
    expect(logs.args.from).to.be.deep.equal(appUserAddress);
    expect(logs.args.to).to.be.deep.equal(otherAppUserAddress);
    expect(logs.args.nftAddresses[0]).to.be.deep.equal(appUserNFT.address);
    expect(logs.args.nftIds[0].toString()).to.be.deep.equal("25");
    expect(await appUserNFT.ownerOf(25)).to.be.deep.equal(swapKiwi.address);
  });

  it('Should succesfully cancel swap by first user (after swap proposed) and emit "SwapCanceled" event', async function () {
    await appUserNFT.mint(appUserAddress, 140);
    await appUserNFT.approve(swapKiwi.address, 140);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [140], [0], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    const cancelTx = await appUser.cancelSwap(swapIdFromLogs);
    const cancelTxReceipt = await cancelTx.wait(1);
    const cancelTxlogs = await getEventWithArgsFromLogs(cancelTxReceipt, "SwapCanceled");

    // check if all values are emitted in event
    expect(cancelTxlogs.eventName).to.be.deep.equal("SwapCanceled");
    expect(cancelTxlogs.args.canceledBy).to.be.deep.equal(appUserAddress);
    // expect that swap ID from "SwapCanceled" is same as swap ID from "swapProposed" event
    expect(cancelTxlogs.args.swapId.toString()).to.be.deep.equal(String(swapIdFromLogs));
    // check that NFT is returned to initial owner
    expect(await appUserNFT.ownerOf(140)).to.be.deep.equal(appUserAddress);
  });

  it('Should succesfully cancel swap by second user (after swap proposed) and emit "SwapCanceled" event', async function () {
    await appUserNFT.mint(appUserAddress, 141);
    await appUserNFT.approve(swapKiwi.address, 141);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [141], [0], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    const cancelTx = await otherAppUser.cancelSwap(swapIdFromLogs);
    const cancelTxReceipt = await cancelTx.wait(1);
    const cancelTxlogs = await getEventWithArgsFromLogs(cancelTxReceipt, "SwapCanceled");

    // check if all values are emitted in event
    expect(cancelTxlogs.eventName).to.be.deep.equal("SwapCanceled");
    expect(cancelTxlogs.args.canceledBy).to.be.deep.equal(otherAppUserAddress);
    // expect that swap ID from "SwapCanceled" is same as swap ID from "swapProposed" event
    expect(cancelTxlogs.args.swapId.toString()).to.be.deep.equal(String(swapIdFromLogs));
    // check that NFT is returned to initial owner
    expect(await appUserNFT.ownerOf(141)).to.be.deep.equal(appUserAddress);
  });

  it('Should succesfully cancel swap by first user (after swap initiated) and emit "SwapCanceled" event', async function () {
    await appUserNFT.mint(appUserAddress, 120);
    await appUserNFT.approve(swapKiwi.address, 120);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [120], [0], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await otherAppUserNFT.mint(otherAppUserAddress, 130);
    await otherAppUserNFT.mint(otherAppUserAddress, 131);
    await otherAppUserNFT.approve(swapKiwi.address, 130);
    await otherAppUserNFT.approve(swapKiwi.address, 131);
    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [otherAppUserNFT.address, otherAppUserNFT.address],
      [130, 131],
      [0, 0],
      {
        value: VALID_APP_FEE
      }
    );
    const initiateSwapTxReceipt = await initiateSwapTx.wait(1);
    const initiateSwapLogs = await getEventWithArgsFromLogs(initiateSwapTxReceipt, "SwapInitiated");
    // check if all values are emitted in "SwapInitiated" event
    expect(initiateSwapLogs.eventName).to.be.deep.equal("SwapInitiated");
    expect(initiateSwapLogs.args.from).to.be.deep.equal(otherAppUserAddress);
    expect(initiateSwapLogs.args.to).to.be.deep.equal(appUserAddress);

    const cancelTx = await otherAppUser.cancelSwap(swapIdFromLogs);
    const cancelTxReceipt = await cancelTx.wait(1);
    const cancelTxlogs = await getEventWithArgsFromLogs(cancelTxReceipt, "SwapCanceled");

    // check if all values are emitted in event
    expect(cancelTxlogs.eventName).to.be.deep.equal("SwapCanceled");
    expect(cancelTxlogs.args.canceledBy).to.be.deep.equal(otherAppUserAddress);
    // expect that swap ID from "SwapCanceled" is same as swap ID from "swapProposed" event
    expect(cancelTxlogs.args.swapId.toString()).to.be.deep.equal(String(swapIdFromLogs));
    // check that NFT is returned to initial owners
    expect(await appUserNFT.ownerOf(120)).to.be.deep.equal(appUserAddress);
    expect(await appUserNFT.ownerOf(130)).to.be.deep.equal(otherAppUserAddress);
    expect(await appUserNFT.ownerOf(131)).to.be.deep.equal(otherAppUserAddress);
  });

  it('Should succesfully cancel swap by second user (after swap initiated) and emit "SwapCanceled" event', async function () {
    await appUserNFT.mint(appUserAddress, 121);
    await appUserNFT.approve(swapKiwi.address, 121);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [121], [0], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await otherAppUserNFT.mint(otherAppUserAddress, 135);
    await otherAppUserNFT.mint(otherAppUserAddress, 136);
    await otherAppUserNFT.approve(swapKiwi.address, 135);
    await otherAppUserNFT.approve(swapKiwi.address, 136);
    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [otherAppUserNFT.address, otherAppUserNFT.address],
      [135, 136],
      [0, 0],
      {
        value: VALID_APP_FEE
      }
    );
    const initiateSwapTxReceipt = await initiateSwapTx.wait(1);
    const initiateSwapLogs = await getEventWithArgsFromLogs(initiateSwapTxReceipt, "SwapInitiated");
    // check if all values are emitted in "SwapInitiated" event
    expect(initiateSwapLogs.eventName).to.be.deep.equal("SwapInitiated");
    expect(initiateSwapLogs.args.from).to.be.deep.equal(otherAppUserAddress);
    expect(initiateSwapLogs.args.to).to.be.deep.equal(appUserAddress);

    const cancelTx = await otherAppUser.cancelSwap(swapIdFromLogs);
    const cancelTxReceipt = await cancelTx.wait(1);
    const cancelTxlogs = await getEventWithArgsFromLogs(cancelTxReceipt, "SwapCanceled");

    // check if all values are emitted in event
    expect(cancelTxlogs.eventName).to.be.deep.equal("SwapCanceled");
    expect(cancelTxlogs.args.canceledBy).to.be.deep.equal(otherAppUserAddress);
    // expect that swap ID from "SwapCanceled" is same as swap ID from "swapProposed" event
    expect(cancelTxlogs.args.swapId.toString()).to.be.deep.equal(String(swapIdFromLogs));
    // check that NFT is returned to initial owners
    expect(await appUserNFT.ownerOf(121)).to.be.deep.equal(appUserAddress);
    expect(await appUserNFT.ownerOf(135)).to.be.deep.equal(otherAppUserAddress);
    expect(await appUserNFT.ownerOf(136)).to.be.deep.equal(otherAppUserAddress);
  });

  it('Should succesfully cancel swap created with ether value', async function () {
    const firstUserBalance = await appUser.signer.getBalance();
    const secondUserBalance = await otherAppUser.signer.getBalance();

    await appUserNFT.mint(appUserAddress, 430);
    await appUserNFT.approve(swapKiwi.address, 430);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [430], [0], {
      value: VALID_APP_FEE.add(parseEther("20"))
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await otherAppUserNFT.mint(otherAppUserAddress, 431);
    await otherAppUserNFT.approve(swapKiwi.address, 431);
    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [otherAppUserNFT.address],
      [431],
      [0],
      {
        value: VALID_APP_FEE.add(parseEther("10"))
      }
    );
    const initiateSwapTxReceipt = await initiateSwapTx.wait(1);
    await getEventWithArgsFromLogs(initiateSwapTxReceipt, "SwapInitiated");

    const cancelTx = await otherAppUser.cancelSwap(swapIdFromLogs);
    const cancelTxReceipt = await cancelTx.wait(1);
    await getEventWithArgsFromLogs(cancelTxReceipt, "SwapCanceled");

    expect(await appUserNFT.ownerOf(430)).to.be.deep.equal(appUserAddress);
    expect(await appUserNFT.ownerOf(431)).to.be.deep.equal(otherAppUserAddress);
    expect(firstUserBalance.sub(await appUser.signer.getBalance()).lt(parseEther("1"))).to.be.equal(true);
    expect(secondUserBalance.sub(await otherAppUser.signer.getBalance()).lt(parseEther("1"))).to.be.equal(true);
  });

  it('Should fail to initiate swap if swap canceled', async function () {
    await appUserNFT.mint(appUserAddress, 170);
    await appUserNFT.approve(swapKiwi.address, 170);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [170], [0], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());
    const cancelTx = await appUser.cancelSwap(swapIdFromLogs);
    await cancelTx.wait(1);

    await otherAppUserNFT.mint(otherAppUserAddress, 301);
    await otherAppUserNFT.approve(swapKiwi.address, 301);
    await expect(otherAppUser.initiateSwap(swapIdFromLogs, [otherAppUserNFT.address], [301], [0], {
      value: VALID_APP_FEE
    })).to.be.rejectedWith(
      `SwapKiwi: caller is not swap participator`
    );
  });

  it('Should fail to initiate swap with invalid app fee', async function () {
    const tx = await appUser.proposeSwap(otherAppUserAddress, [], [], [], {
      value: VALID_APP_FEE.add(parseEther("0.1"))
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await expect(otherAppUser.initiateSwap(swapIdFromLogs, [], [], [], {
      value: parseEther("0.01")
    })).to.be.rejectedWith(
      `SwapKiwi: Sent ETH amount needs to be more or equal application fee`
    );
  });

  it('Should fail to initiate swap twice', async function () {
    await appUserNFT.mint(appUserAddress, 189);
    await appUserNFT.approve(swapKiwi.address, 189);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [189], [0], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());
    await otherAppUserNFT.mint(otherAppUserAddress, 302);
    await otherAppUserNFT.approve(swapKiwi.address, 302);
    await otherAppUser.initiateSwap(swapIdFromLogs, [otherAppUserNFT.address], [302], [0], {
      value: VALID_APP_FEE
    })


    await otherAppUserNFT.mint(otherAppUserAddress, 303);
    await otherAppUserNFT.approve(swapKiwi.address, 303);
    await expect(otherAppUser.initiateSwap(swapIdFromLogs, [otherAppUserNFT.address], [303], [0], {
      value: VALID_APP_FEE
    })).to.be.rejectedWith(
      "SwapKiwi: swap already initiated"
    );
  });

  it('Should fail to initiate swap twice if proposed only with ether', async function () {
    await appUserNFT.mint(appUserAddress, 1732);
    await appUserNFT.approve(swapKiwi.address, 1732);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [1732], [0], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());
    await otherAppUser.initiateSwap(swapIdFromLogs, [], [], [], {
      value: VALID_APP_FEE.add(ethers.utils.parseEther("0.25"))
    })


    await otherAppUserNFT.mint(otherAppUserAddress, 1733);
    await otherAppUserNFT.approve(swapKiwi.address, 1733);
    await expect(otherAppUser.initiateSwap(swapIdFromLogs, [otherAppUserNFT.address], [1733], [0], {
      value: VALID_APP_FEE
    })).to.be.rejectedWith(
      "SwapKiwi: swap already initiated"
    );
  });

  it('Should fail to cancel swap twice', async function () {
    await appUserNFT.mint(appUserAddress, 200);
    await appUserNFT.approve(swapKiwi.address, 200);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [200], [0], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    const cancelTx = await appUser.cancelSwap(swapIdFromLogs);
    const cancelTxReceipt = await cancelTx.wait(1);
    const cancelTxlogs = await getEventWithArgsFromLogs(cancelTxReceipt, "SwapCanceled");

    // check if all values are emitted in event
    expect(cancelTxlogs.eventName).to.be.deep.equal("SwapCanceled");
    expect(cancelTxlogs.args.canceledBy).to.be.deep.equal(appUserAddress);
    // expect that swap ID from "SwapCanceled" is same as swap ID from "swapProposed" event
    expect(cancelTxlogs.args.swapId.toString()).to.be.deep.equal(String(swapIdFromLogs));
    // check that NFT is returned to initial owner
    expect(await appUserNFT.ownerOf(200)).to.be.deep.equal(appUserAddress);

    await expect(appUser.cancelSwap(swapIdFromLogs)).to.be.rejectedWith(
      "SwapKiwi: Can't cancel swap, must be swap participant"
    );
  });

  it("Should fail to cancel swap if user is not a swap participant", async function () {
    const nonSwapParticipant = new ethers.Contract(SwapKiwi.address, SwapKiwi.abi, signers[6]) as SwapKiwi;

    // first user NFT minting and swap deposit into SwapKiwi
    await appUserNFT.mint(appUserAddress, 70);
    await appUserNFT.approve(swapKiwi.address, 70);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [70], [0], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await expect(nonSwapParticipant.cancelSwap(swapIdFromLogs)).to.be.rejectedWith(
      "SwapKiwi: Can't cancel swap, must be swap participant");
  });

  it("Should fail to accept swap if second user didn't add NFTs or ether", async function () {
    // first user NFT minting and swap deposit into SwapKiwi
    await appUserNFT.mint(appUserAddress, 2000);
    await appUserNFT.approve(swapKiwi.address, 2000);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [2000], [0], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await expect(appUser.acceptSwap(swapIdFromLogs)).to.be.rejectedWith(
      "SwapKiwi: Can't accept swap, both participants didn't add NFTs");
  });

  it('Should fail to accept swap if not swap initiator', async function () {
    await appUserNFT.mint(appUserAddress, 2100);
    await appUserNFT.approve(swapKiwi.address, 2100);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [2100], [0], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [],
      [],
      [],
      {
        value: VALID_APP_FEE.add(parseEther("50"))
      }
    );
    await initiateSwapTx.wait(1);

    await expect(otherAppUser.acceptSwap(swapIdFromLogs)).to.be.rejectedWith(
      "SwapKiwi: caller is not swap initiator"
    );
  });

  it('Should successfully execute NFT - NFT swap', async function () {
    // first user NFT minting and swap deposit into SwapKiwi
    await appUserNFT.mint(appUserAddress, 85);
    await appUserNFT.mint(appUserAddress, 86);
    await appUserNFT.approve(swapKiwi.address, 85);
    await appUserNFT.approve(swapKiwi.address, 86);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address, appUserNFT.address], [85, 86], [0, 0], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    // check that first user NFTs are deposited into SwapKiwi
    expect(await appUserNFT.ownerOf(85)).to.be.deep.equal(swapKiwi.address);
    expect(await appUserNFT.ownerOf(86)).to.be.deep.equal(swapKiwi.address);

    // second user NFT minting and swap deposit into SwapKiwi
    await otherAppUserNFT.mint(otherAppUserAddress, 87);
    await otherAppUserNFT.mint(otherAppUserAddress, 88);
    await otherAppUserNFT.approve(swapKiwi.address, 87);
    await otherAppUserNFT.approve(swapKiwi.address, 88);
    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [otherAppUserNFT.address, otherAppUserNFT.address],
      [87, 88],
      [0, 0],
      {
        value: VALID_APP_FEE
      }
    );
    const initiateSwapTxReceipt = await initiateSwapTx.wait(1);
    const initiateSwapLogs = await getEventWithArgsFromLogs(initiateSwapTxReceipt, "SwapInitiated");
    // check if all values are emitted in "SwapInitiated" event
    expect(initiateSwapLogs.eventName).to.be.deep.equal("SwapInitiated");
    expect(initiateSwapLogs.args.from).to.be.deep.equal(otherAppUserAddress);
    expect(initiateSwapLogs.args.to).to.be.deep.equal(appUserAddress);

    // check that second user NFTs are deposited into SwapKiwi
    expect(await otherAppUserNFT.ownerOf(87)).to.be.deep.equal(swapKiwi.address);
    expect(await otherAppUserNFT.ownerOf(88)).to.be.deep.equal(swapKiwi.address);

    const acceptSwapTx = await appUser.acceptSwap(swapIdFromLogs);
    const acceptSwapTxReceipt = await acceptSwapTx.wait(1);
    const acceptSwapLogs = await getEventWithArgsFromLogs(acceptSwapTxReceipt, "SwapExecuted");

    // check if all values are emitted in "SwapExecuted" event
    expect(acceptSwapLogs.eventName).to.be.deep.equal("SwapExecuted");
    expect(acceptSwapLogs.args.from).to.be.deep.equal(appUserAddress);
    expect(acceptSwapLogs.args.to).to.be.deep.equal(otherAppUserAddress);
    // check that NFTs are transfered from SwapKiwi to participants - same address because both have same signer

    expect(await otherAppUserNFT.ownerOf(85)).to.be.deep.equal(otherAppUserAddress);
    expect(await otherAppUserNFT.ownerOf(86)).to.be.deep.equal(otherAppUserAddress);
    expect(await appUserNFT.ownerOf(87)).to.be.deep.equal(appUserAddress);
    expect(await appUserNFT.ownerOf(88)).to.be.deep.equal(appUserAddress);
  });

  it('Should successfully execute NFT + ether - NFT + ether swap', async function () {
    const firstUserBalance = await appUser.signer.getBalance();
    const secondUserBalance = await otherAppUser.signer.getBalance();

    await appUserNFT.mint(appUserAddress, 375);
    await appUserNFT.approve(swapKiwi.address, 375);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [375], [0], {
      value: VALID_APP_FEE.add(parseEther("50"))
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await otherAppUserNFT.mint(otherAppUserAddress, 376);
    await otherAppUserNFT.approve(swapKiwi.address, 376);
    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [otherAppUserNFT.address],
      [376],
      [0],
      {
        value: VALID_APP_FEE.add(parseEther("25"))
      }
    );
    await initiateSwapTx.wait(1);

    const acceptSwapTx = await appUser.acceptSwap(swapIdFromLogs);
    await acceptSwapTx.wait(1);

    expect(await appUserNFT.ownerOf(375)).to.be.deep.equal(otherAppUserAddress);
    expect(await otherAppUserNFT.ownerOf(376)).to.be.deep.equal(appUserAddress);
    expect(firstUserBalance.sub((await appUser.signer.getBalance()).add(parseEther("25"))).lt(parseEther("1"))).to.be.equal(true);
    expect(secondUserBalance.sub((await otherAppUser.signer.getBalance()).sub(parseEther("25"))).lt(parseEther("1"))).to.be.equal(true);
  });

  it('Should successfully execute ether - NFT swap', async function () {
    const secondUserBalance = await otherAppUser.signer.getBalance();

    const tx = await appUser.proposeSwap(otherAppUserAddress, [], [], [], {
      value: VALID_APP_FEE.add(parseEther("50"))
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await otherAppUserNFT.mint(otherAppUserAddress, 1800);
    await otherAppUserNFT.approve(swapKiwi.address, 1800);
    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [otherAppUserNFT.address],
      [1800],
      [0],
      {
        value: VALID_APP_FEE
      }
    );
    await initiateSwapTx.wait(1);

    const acceptSwapTx = await appUser.acceptSwap(swapIdFromLogs);
    await acceptSwapTx.wait(1);

    expect(await otherAppUserNFT.ownerOf(1800)).to.be.deep.equal(appUserAddress);
    expect(
      (
        await otherAppUser.signer.getBalance()
      ).sub(secondUserBalance).sub(parseEther("50")).lt(parseEther("1"))).to.be.equal(true);
  });

  it('Should successfully execute NFT - ether swap', async function () {
    const firstUserBalance = await appUser.signer.getBalance();

    await appUserNFT.mint(appUserAddress, 1822);
    await appUserNFT.approve(swapKiwi.address, 1822);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserNFT.address], [1822], [0], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [],
      [],
      [],
      {
        value: VALID_APP_FEE.add(parseEther("50"))
      }
    );
    await initiateSwapTx.wait(1);

    const acceptSwapTx = await appUser.acceptSwap(swapIdFromLogs);
    await acceptSwapTx.wait(1);

    expect(await appUserNFT.ownerOf(1822)).to.be.deep.equal(otherAppUserAddress);
    expect(
      (
        await appUser.signer.getBalance()
      ).sub(firstUserBalance).sub(parseEther("50")).lt(parseEther("1"))).to.be.equal(true);
  });

  it("Should successfully withdraw only collected fees", async function () {
    await swapKiwi.withdrawEther(await signers[7].getAddress());

    const tx1 = await appUser.proposeSwap(otherAppUserAddress, [], [], [], {
      value: VALID_APP_FEE.add(ethers.utils.parseEther("1"))
    });
    const txReceipt1 = await tx1.wait(1);
    const logs1 = await getEventWithArgsFromLogs(txReceipt1, "SwapProposed");
    const swapIdFromLogs1 = Number(logs1.args.swapId.toString());
    const initiateSwapTx1 = await otherAppUser.initiateSwap(
      swapIdFromLogs1,
      [],
      [],
      [],
      {
        value: VALID_APP_FEE.add(parseEther("5"))
      }
    );
    await initiateSwapTx1.wait(1);
    const acceptSwapTx1 = await appUser.acceptSwap(swapIdFromLogs1);
    await acceptSwapTx1.wait(1);
    const tx2 = await appUser.proposeSwap(otherAppUserAddress, [], [], [], {
      value: VALID_APP_FEE.add(ethers.utils.parseEther("1"))
    });
    const txReceipt2 = await tx2.wait(1);
    const logs2 = await getEventWithArgsFromLogs(txReceipt2, "SwapProposed");
    const swapIdFromLogs = Number(logs2.args.swapId.toString());
    const initiateSwapTx2 = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [],
      [],
      [],
      {
        value: VALID_APP_FEE.add(parseEther("5"))
      }
    );
    await initiateSwapTx2.wait(1);

    await swapKiwi.withdrawEther(appUserNFT.address)

    expect((await ethers.provider.getBalance(appUserNFT.address)).toString())
      .to.be.deep.equal(VALID_APP_FEE.mul(4).toString())
  });

  it("Should fail to withdraw collected fees if not owner", async function () {
    await expect(appUser.withdrawEther(appUser.address))
      .to.be.rejectedWith(
        "Ownable: caller is not the owner");
  });

  it("Should fail to withdraw collected fees if sent to zero address", async function () {
    await expect(swapKiwi.withdrawEther("0x0000000000000000000000000000000000000000"))
      .to.be.rejectedWith("SwapKiwi: transfer to the zero address");
  });
});
Example #23
Source File: 01_IAvatar.spec.ts    From zodiac with GNU Lesser General Public License v3.0 5 votes vote down vote up
describe("IAvatar", async () => {
  const [user1, user2] = waffle.provider.getWallets();

  const setupTests = deployments.createFixture(async ({ deployments }) => {
    await deployments.fixture();
    const Avatar = await hre.ethers.getContractFactory("TestAvatar");
    const avatar = await Avatar.deploy();
    const iAvatar = await hre.ethers.getContractAt("IAvatar", avatar.address);
    const tx = {
      to: avatar.address,

      value: 0,
      data: "0x",
      operation: 0,
      avatarTxGas: 0,
      baseGas: 0,
      gasPrice: 0,
      gasToken: AddressZero,
      refundReceiver: AddressZero,
      signatures: "0x",
    };
    return {
      iAvatar,
      tx,
    };
  });

  describe("enableModule", async () => {
    it("allow to enable a module", async () => {
      const { iAvatar } = await setupTests();
      await expect(await iAvatar.isModuleEnabled(user1.address)).to.be.equals(
        false
      );
      let transaction = await iAvatar.enableModule(user1.address);
      let receipt = await transaction.wait();
      await expect(await iAvatar.isModuleEnabled(user1.address)).to.be.equals(
        true
      );
    });
  });

  describe("disableModule", async () => {
    it("allow to disable a module", async () => {
      const { iAvatar } = await setupTests();
      await expect(await iAvatar.isModuleEnabled(user1.address)).to.be.equals(
        false
      );
      let transaction = await iAvatar.enableModule(user1.address);
      let receipt = await transaction.wait();
      await expect(await iAvatar.isModuleEnabled(user1.address)).to.be.equals(
        true
      );
      transaction = await iAvatar.disableModule(AddressZero, user1.address);
      receipt = await transaction.wait();
      await expect(await iAvatar.isModuleEnabled(user1.address)).to.be.equals(
        false
      );
    });
  });

  describe("execTransactionFromModule", async () => {
    it("revert if module is not enabled", async () => {
      const { iAvatar, tx } = await setupTests();
      await expect(
        iAvatar.execTransactionFromModule(
          tx.to,
          tx.value,
          tx.data,
          tx.operation
        )
      ).to.be.revertedWith("Not authorized");
    });

    it("allow to execute module transaction", async () => {
      const { iAvatar, tx } = await setupTests();
      await iAvatar.enableModule(user1.address);
      await expect(
        iAvatar.execTransactionFromModule(
          tx.to,
          tx.value,
          tx.data,
          tx.operation
        )
      );
    });
  });

  describe("execTransactionFromModuleReturnData", async () => {
    it("revert if module is not enabled", async () => {
      const { iAvatar, tx } = await setupTests();
      await expect(
        iAvatar.execTransactionFromModuleReturnData(
          tx.to,
          tx.value,
          tx.data,
          tx.operation
        )
      ).to.be.revertedWith("Not authorized");
    });

    it("allow to execute module transaction and return data", async () => {
      const { iAvatar, tx } = await setupTests();
      await iAvatar.enableModule(user1.address);
      await expect(
        iAvatar.execTransactionFromModuleReturnData(
          tx.to,
          tx.value,
          tx.data,
          tx.operation
        )
      );
    });
  });

  describe("isModuleEnabled", async () => {
    it("returns false if module has not been enabled", async () => {
      const { iAvatar } = await setupTests();
      await expect(await iAvatar.isModuleEnabled(user1.address)).to.be.equals(
        false
      );
    });

    it("returns true if module has been enabled", async () => {
      const { iAvatar } = await setupTests();
      await expect(await iAvatar.isModuleEnabled(user1.address)).to.be.equals(
        false
      );
      let transaction = await iAvatar.enableModule(user1.address);
      let receipt = await transaction.wait();
      await expect(await iAvatar.isModuleEnabled(user1.address)).to.be.equals(
        true
      );
    });
  });

  describe("getModulesPaginated", async () => {
    it("returns array of enabled modules", async () => {
      const { iAvatar } = await setupTests();
      let transaction = await iAvatar.enableModule(user1.address);
      let array, next;
      [array, next] = await iAvatar.getModulesPaginated(user1.address, 1);
      await expect(array.toString()).to.be.equals([user1.address].toString());
      await expect(next).to.be.equals(user1.address);
    });
  });
});
Example #24
Source File: swapKiwi.test.ts    From swap.kiwi with GNU General Public License v3.0 5 votes vote down vote up
describe("SwapKiwi", async function () {
  let SwapKiwi: Deployment;
  let swapKiwi: SwapKiwi;
  let TestERC721: Deployment;
  let appUserERC721: TestERC721;
  let otherAppUserERC721: TestERC721;
  let TestERC1155: Deployment;
  let appUserERC1155: TestERC1155;
  let otherAppUserERC1155: TestERC1155;
  let SwapParticipant: Deployment;
  let initiatorParticipant: SwapParticipant;
  let secondUserParticipant: SwapParticipant;
  let signers: Signer[];
  let appUser: SwapKiwi;
  let otherAppUser: SwapKiwi;
  let appUserAddress: string;
  let otherAppUserAddress: string;
  const VALID_APP_FEE = ethers.utils.parseEther("0.1");

  before(async () => {
    signers = await ethers.getSigners();
    ({ SwapKiwi, TestERC721, TestERC1155, SwapParticipant } = await deployments.fixture());

    swapKiwi = await ethers.getContractAt(SwapKiwi.abi, SwapKiwi.address, signers[0]) as SwapKiwi;

    appUserERC721 = await ethers.getContractAt(TestERC721.abi, TestERC721.address, signers[2]) as TestERC721;
    otherAppUserERC721 = await ethers.getContractAt(TestERC721.abi, TestERC721.address, signers[3]) as TestERC721;

    appUserERC1155 = await ethers.getContractAt(TestERC1155.abi, TestERC1155.address, signers[2]) as TestERC1155;
    otherAppUserERC1155 = await ethers.getContractAt(TestERC1155.abi, TestERC1155.address, signers[3]) as TestERC1155;

    initiatorParticipant = await ethers.getContractAt(SwapParticipant.abi, SwapParticipant.address, signers[2]) as SwapParticipant;
    secondUserParticipant = await ethers.getContractAt(SwapParticipant.abi, SwapParticipant.address, signers[3]) as SwapParticipant;

    appUser = new ethers.Contract(swapKiwi.address, SwapKiwi.abi, signers[2]) as SwapKiwi;
    otherAppUser = new ethers.Contract(swapKiwi.address, SwapKiwi.abi, signers[3]) as SwapKiwi;
    appUserAddress = await signers[2].getAddress();
    otherAppUserAddress = await signers[3].getAddress();
  });

  function getFilterName(eventName: string) {
    let filter: any;
    switch (eventName) {
      case "SwapExecuted":
        filter = swapKiwi.filters.SwapExecuted(null, null, null);
        break;
      case "SwapCanceled":
        filter = swapKiwi.filters.SwapCanceled(null, null);
        break;
      case "SwapProposed":
        filter = swapKiwi.filters.SwapProposed(null, null, null, null, null, null, null);
        break;
      case "SwapInitiated":
        filter = swapKiwi.filters.SwapInitiated(null, null, null, null, null, null, null);
      default: null
    }
    return filter;
  }

  async function getEventWithArgsFromLogs(txReceipt: TransactionReceipt, eventName: string): Promise<any | null> {
    if (txReceipt.logs) {
      const events = await swapKiwi.queryFilter(getFilterName(eventName), undefined);
      return events.map((e) => {
        if (e.event == eventName) {
          return {
            eventName: eventName,
            args: e.args
          }
        }
      }
      ).pop()
    }
    return null;
  }

  it("Should successfuly set app fee if caller is the owner", async function () {
    await swapKiwi.setAppFee(VALID_APP_FEE);
    expect((await swapKiwi.fee()).toString()).to.be.deep.equal(VALID_APP_FEE.toString());
  });

  it("Should fail to set app fee if caller is not owner", async function () {
    const nonOwnerContract = new ethers.Contract(SwapKiwi.address, SwapKiwi.abi, signers[6]) as SwapKiwi;

    await expect(nonOwnerContract.setAppFee(1000))
      .to.be.rejectedWith("Ownable: caller is not the owner");
  });

  it('Should fail to propose swap with invalid app fee', async function () {
    await expect(appUser.proposeSwap(otherAppUserAddress, [], [], [], {
      value: parseEther("0.01")
    })).to.be.rejectedWith(
      "SwapKiwi: Sent ETH amount needs to be more or equal application fee"
    );
  });

  it('Should fail to propose swap with different nft address and id length', async function () {
    await expect(appUser.proposeSwap(otherAppUserAddress, [], [13], [], {
      value: VALID_APP_FEE
    })).to.be.rejectedWith(
      "SwapKiwi: NFT and ID arrays have to be same length"
    );
  });

  it('Should fail to propose swap with different nft address and amount length', async function () {
    await expect(appUser.proposeSwap(otherAppUserAddress, [], [], [5], {
      value: VALID_APP_FEE
    })).to.be.rejectedWith(
      "SwapKiwi: NFT and AMOUNT arrays have to be same length"
    );
  });

  it('Should succesfully deposit NFT into escrow contract and emit "SwapProposed" event', async function () {
    const initiatorTokenIds = [25, 25];
    const initiatorTokenAmounts = [0, 50];

    await appUserERC721.mint(appUserAddress, initiatorTokenIds[0]);
    await appUserERC721.approve(swapKiwi.address, initiatorTokenIds[0]);
    await appUserERC1155.mint(appUserAddress, initiatorTokenIds[1], initiatorTokenAmounts[1]);
    await appUserERC1155.setApprovalForAll(swapKiwi.address, true);

    expect(await appUserERC721.ownerOf(initiatorTokenIds[0])).to.be.deep.equal(appUserAddress);
    
    const erc1155_balance_beforeDeposit =  await appUserERC1155.balanceOf(appUserAddress, initiatorTokenIds[1]);
    expect(erc1155_balance_beforeDeposit.toNumber()).to.be.deep.equal(initiatorTokenAmounts[1]);

    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserERC721.address, appUserERC1155.address], initiatorTokenIds, initiatorTokenAmounts, {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");

    const erc1155_balance_AfterDeposit =  await appUserERC1155.balanceOf(appUserAddress, initiatorTokenIds[1]);
    const erc1155_balance_SwapKiwi = await appUserERC1155.balanceOf(swapKiwi.address, initiatorTokenIds[1]);

    // check if all values are emitted in event
    expect(logs.eventName).to.be.deep.equal("SwapProposed");
    expect(logs.args.from).to.be.deep.equal(appUserAddress);
    expect(logs.args.to).to.be.deep.equal(otherAppUserAddress);
    expect(logs.args.nftAddresses[0]).to.be.deep.equal(appUserERC721.address);
    expect(logs.args.nftIds[0].toString()).to.be.deep.equal("25");
    expect(logs.args.nftIds[1].toString()).to.be.deep.equal("25");
    expect(logs.args.nftAmounts[0].toString()).to.be.deep.equal("0");
    expect(logs.args.nftAmounts[1].toString()).to.be.deep.equal("50");
    expect(await appUserERC721.ownerOf(25)).to.be.deep.equal(swapKiwi.address);
    expect(erc1155_balance_AfterDeposit.toNumber()).to.be.deep.equal(0);
    expect(erc1155_balance_SwapKiwi.toNumber()).to.be.deep.equal(initiatorTokenAmounts[1]);
  });

  it('Should succesfully cancel swap by first user (after swap proposed) and emit "SwapCanceled" event', async function () {
    await appUserERC721.mint(appUserAddress, 140);
    await appUserERC721.approve(swapKiwi.address, 140);
    await appUserERC1155.mint(appUserAddress, 30, 50);
    await appUserERC1155.setApprovalForAll(swapKiwi.address, true);

    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserERC721.address, appUserERC1155.address], [140, 30], [0, 50], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    const user_erc1155BalanceBeforeCancel = await appUserERC1155.balanceOf(appUserAddress, 30);
    expect(user_erc1155BalanceBeforeCancel.toNumber()).to.be.deep.equal(0);
    const cancelTx = await appUser.cancelSwap(swapIdFromLogs);
    const cancelTxReceipt = await cancelTx.wait(1);
    const cancelTxlogs = await getEventWithArgsFromLogs(cancelTxReceipt, "SwapCanceled");

    const user_erc1155BalanceAfterCancel = await appUserERC1155.balanceOf(appUserAddress, 30);
    // check if all values are emitted in event
    expect(cancelTxlogs.eventName).to.be.deep.equal("SwapCanceled");
    expect(cancelTxlogs.args.canceledBy).to.be.deep.equal(appUserAddress);
    // expect that swap ID from "SwapCanceled" is same as swap ID from "swapProposed" event
    expect(cancelTxlogs.args.swapId.toString()).to.be.deep.equal(String(swapIdFromLogs));
    // check that ERC721 and ERC1155 are returned to initial owner
    expect(await appUserERC721.ownerOf(140)).to.be.deep.equal(appUserAddress);
    expect(user_erc1155BalanceAfterCancel.toNumber()).to.be.deep.equal(50);
  });

  it('Should succesfully cancel swap by second user (after swap proposed) and emit "SwapCanceled" event', async function () {
    const initiatorTokenIds = [141, 725];
    const initiatorTokenAmounts = [0, 50];

    await appUserERC721.mint(appUserAddress, initiatorTokenIds[0]);
    await appUserERC721.approve(swapKiwi.address, initiatorTokenIds[0]);
    await appUserERC1155.mint(appUserAddress, initiatorTokenIds[1], initiatorTokenAmounts[1]);
    await appUserERC1155.setApprovalForAll(swapKiwi.address, true);

    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserERC721.address, appUserERC1155.address], initiatorTokenIds, initiatorTokenAmounts, {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    const user_erc1155BalanceBeforeCancel = await appUserERC1155.balanceOf(appUserAddress, initiatorTokenIds[1]);
    expect(user_erc1155BalanceBeforeCancel.toNumber()).to.be.deep.equal(0);

    const cancelTx = await otherAppUser.cancelSwap(swapIdFromLogs);
    const cancelTxReceipt = await cancelTx.wait(1);
    const cancelTxlogs = await getEventWithArgsFromLogs(cancelTxReceipt, "SwapCanceled");

    const user_erc1155BalanceAfterCancel = await appUserERC1155.balanceOf(appUserAddress, initiatorTokenIds[1]);

    // check if all values are emitted in event
    expect(cancelTxlogs.eventName).to.be.deep.equal("SwapCanceled");
    expect(cancelTxlogs.args.canceledBy).to.be.deep.equal(otherAppUserAddress);
    // expect that swap ID from "SwapCanceled" is same as swap ID from "swapProposed" event
    expect(cancelTxlogs.args.swapId.toString()).to.be.deep.equal(String(swapIdFromLogs));
    // check that ERC721 and ERC1155 are returned to initial owner
    expect(await appUserERC721.ownerOf(initiatorTokenIds[0])).to.be.deep.equal(appUserAddress);
    expect(user_erc1155BalanceAfterCancel.toNumber()).to.be.deep.equal(initiatorTokenAmounts[1]);
  });

  it('Should succesfully cancel swap by first user (after swap initiated) and emit "SwapCanceled" event', async function () {
    const initiatorTokenIds = [120, 923];
    const initiatorTokenAmounts = [0, 25];

    await appUserERC721.mint(appUserAddress, initiatorTokenIds[0]);
    await appUserERC721.approve(swapKiwi.address, initiatorTokenIds[0]);
    await appUserERC1155.mint(appUserAddress, initiatorTokenIds[1], initiatorTokenAmounts[1]);
    await appUserERC1155.setApprovalForAll(swapKiwi.address, true);

    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserERC721.address, appUserERC1155.address], initiatorTokenIds, initiatorTokenAmounts, {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    const secondUserTokenIds = [130, 131, 47];
    const secondUserTokenAmounts = [0, 0, 50];

    await otherAppUserERC721.mint(otherAppUserAddress, secondUserTokenIds[0]);
    await otherAppUserERC721.mint(otherAppUserAddress, secondUserTokenIds[1]);
    await otherAppUserERC721.approve(swapKiwi.address, secondUserTokenIds[0]);
    await otherAppUserERC721.approve(swapKiwi.address, secondUserTokenIds[1]);
    await otherAppUserERC1155.mint(otherAppUserAddress, secondUserTokenIds[2], secondUserTokenAmounts[2]);
    await otherAppUserERC1155.setApprovalForAll(swapKiwi.address, true);

    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [otherAppUserERC721.address, otherAppUserERC721.address, otherAppUserERC1155.address],
      secondUserTokenIds,
      secondUserTokenAmounts,
      {
        value: VALID_APP_FEE
      }
    );

    const initiateSwapTxReceipt = await initiateSwapTx.wait(1);
    const initiateSwapLogs = await getEventWithArgsFromLogs(initiateSwapTxReceipt, "SwapInitiated");

    const initiatorERC1155_923_balance = await appUserERC1155.balanceOf(appUserAddress, initiatorTokenIds[1]);
    const secondUserERC1155_47_balance = await otherAppUserERC1155.balanceOf(otherAppUserAddress, secondUserTokenIds[2]);
    
    expect(initiatorERC1155_923_balance.toNumber()).to.be.deep.equal(0);
    expect(secondUserERC1155_47_balance.toNumber()).to.be.deep.equal(0);

    // check if all values are emitted in "SwapInitiated" event
    expect(initiateSwapLogs.eventName).to.be.deep.equal("SwapInitiated");
    expect(initiateSwapLogs.args.from).to.be.deep.equal(otherAppUserAddress);
    expect(initiateSwapLogs.args.to).to.be.deep.equal(appUserAddress);

    const cancelTx = await otherAppUser.cancelSwap(swapIdFromLogs);
    const cancelTxReceipt = await cancelTx.wait(1);
    const cancelTxlogs = await getEventWithArgsFromLogs(cancelTxReceipt, "SwapCanceled");

    const initiatorERC1155_923_balanceAfterCancel = await appUserERC1155.balanceOf(appUserAddress, initiatorTokenIds[1]);
    const secondUserERC1155_47_balanceAfterCancel = await otherAppUserERC1155.balanceOf(otherAppUserAddress, secondUserTokenIds[2]);

    expect(initiatorERC1155_923_balanceAfterCancel.toNumber()).to.be.deep.equal(initiatorTokenAmounts[1]);
    expect(secondUserERC1155_47_balanceAfterCancel.toNumber()).to.be.deep.equal(secondUserTokenAmounts[2]);

    // check if all values are emitted in event
    expect(cancelTxlogs.eventName).to.be.deep.equal("SwapCanceled");
    expect(cancelTxlogs.args.canceledBy).to.be.deep.equal(otherAppUserAddress);
    // expect that swap ID from "SwapCanceled" is same as swap ID from "swapProposed" event
    expect(cancelTxlogs.args.swapId.toString()).to.be.deep.equal(String(swapIdFromLogs));
    // check that NFT is returned to initial owners
    expect(await appUserERC721.ownerOf(120)).to.be.deep.equal(appUserAddress);
    expect(await appUserERC721.ownerOf(130)).to.be.deep.equal(otherAppUserAddress);
    expect(await appUserERC721.ownerOf(131)).to.be.deep.equal(otherAppUserAddress);
  });

  it('Should succesfully cancel swap by second user (after swap initiated) and emit "SwapCanceled" event', async function () {
    const initiatorTokenIds = [121, 47];
    const initiatorTokenAmounts = [0, 25];

    await appUserERC721.mint(appUserAddress, initiatorTokenIds[0]);
    await appUserERC721.approve(swapKiwi.address, initiatorTokenIds[0]);
    await appUserERC1155.mint(appUserAddress, initiatorTokenIds[1], initiatorTokenAmounts[1]);
    await appUserERC1155.setApprovalForAll(swapKiwi.address, true);

    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserERC721.address, appUserERC1155.address], initiatorTokenIds, initiatorTokenAmounts, {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    const secondUserTokenIds = [135, 136, 71];
    const secondUserTokenAmounts = [0, 0, 25];

    await otherAppUserERC721.mint(otherAppUserAddress, secondUserTokenIds[0]);
    await otherAppUserERC721.mint(otherAppUserAddress, secondUserTokenIds[1]);
    await otherAppUserERC721.approve(swapKiwi.address, secondUserTokenIds[0]);
    await otherAppUserERC721.approve(swapKiwi.address, secondUserTokenIds[1]);
    await otherAppUserERC1155.mint(otherAppUserAddress, secondUserTokenIds[2], secondUserTokenAmounts[2]);
    await otherAppUserERC1155.setApprovalForAll(swapKiwi.address, true);

    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [otherAppUserERC721.address, otherAppUserERC721.address, otherAppUserERC1155.address],
      secondUserTokenIds,
      secondUserTokenAmounts,
      {
        value: VALID_APP_FEE
      }
    );
    const initiateSwapTxReceipt = await initiateSwapTx.wait(1);
    const initiateSwapLogs = await getEventWithArgsFromLogs(initiateSwapTxReceipt, "SwapInitiated");

    const initiatorERC1155_47_balance = await appUserERC1155.balanceOf(appUserAddress, initiatorTokenIds[1]);
    const secondUserERC1155_71_balance = await otherAppUserERC1155.balanceOf(otherAppUserAddress, secondUserTokenIds[2]);
    
    expect(initiatorERC1155_47_balance.toNumber()).to.be.deep.equal(0);
    expect(secondUserERC1155_71_balance.toNumber()).to.be.deep.equal(0);

    // check if all values are emitted in "SwapInitiated" event
    expect(initiateSwapLogs.eventName).to.be.deep.equal("SwapInitiated");
    expect(initiateSwapLogs.args.from).to.be.deep.equal(otherAppUserAddress);
    expect(initiateSwapLogs.args.to).to.be.deep.equal(appUserAddress);

    const cancelTx = await otherAppUser.cancelSwap(swapIdFromLogs);
    const cancelTxReceipt = await cancelTx.wait(1);
    const cancelTxlogs = await getEventWithArgsFromLogs(cancelTxReceipt, "SwapCanceled");

    const initiatorERC1155_47_balanceAfterCancel = await appUserERC1155.balanceOf(appUserAddress, initiatorTokenIds[1]);
    const secondUserERC1155_71_balanceAfterCancel = await otherAppUserERC1155.balanceOf(otherAppUserAddress, secondUserTokenIds[2]);

    // check if all values are emitted in event
    expect(cancelTxlogs.eventName).to.be.deep.equal("SwapCanceled");
    expect(cancelTxlogs.args.canceledBy).to.be.deep.equal(otherAppUserAddress);
    // expect that swap ID from "SwapCanceled" is same as swap ID from "swapProposed" event
    expect(cancelTxlogs.args.swapId.toString()).to.be.deep.equal(String(swapIdFromLogs));
    // check that NFT is returned to initial owners
    expect(await appUserERC721.ownerOf(121)).to.be.deep.equal(appUserAddress);
    expect(await appUserERC721.ownerOf(135)).to.be.deep.equal(otherAppUserAddress);
    expect(await appUserERC721.ownerOf(136)).to.be.deep.equal(otherAppUserAddress);
    expect(initiatorERC1155_47_balanceAfterCancel.toNumber()).to.be.deep.equal(secondUserTokenAmounts[2]);
    expect(secondUserERC1155_71_balanceAfterCancel.toNumber()).to.be.deep.equal(initiatorTokenAmounts[1]);
  });

  it('Should succesfully cancel swap created with ether value', async function () {
    const firstUserBalance = await appUser.signer.getBalance();
    const secondUserBalance = await otherAppUser.signer.getBalance();

    const initiatorTokenIds = [430, 431];
    const initiatorTokenAmounts = [0, 25];

    await appUserERC721.mint(appUserAddress, 430);
    await appUserERC721.approve(swapKiwi.address, 430);

    await appUserERC1155.mint(appUserAddress, initiatorTokenIds[1], initiatorTokenAmounts[1]);
    await appUserERC1155.setApprovalForAll(swapKiwi.address, true);

    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserERC721.address, appUserERC1155.address], initiatorTokenIds, initiatorTokenAmounts, {
      value: VALID_APP_FEE.add(parseEther("20"))
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await otherAppUserERC721.mint(otherAppUserAddress, 431);
    await otherAppUserERC721.approve(swapKiwi.address, 431);

    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [otherAppUserERC721.address],
      [431], [0],
      {
        value: VALID_APP_FEE.add(parseEther("10"))
      }
    );
    const initiateSwapTxReceipt = await initiateSwapTx.wait(1);
    await getEventWithArgsFromLogs(initiateSwapTxReceipt, "SwapInitiated");

    const cancelTx = await otherAppUser.cancelSwap(swapIdFromLogs);
    const cancelTxReceipt = await cancelTx.wait(1);
    await getEventWithArgsFromLogs(cancelTxReceipt, "SwapCanceled");

    const erc1155_431_initiatorBalance = await appUserERC1155.balanceOf(appUserAddress, initiatorTokenIds[1]);

    expect(await appUserERC721.ownerOf(430)).to.be.deep.equal(appUserAddress);
    expect(await appUserERC721.ownerOf(431)).to.be.deep.equal(otherAppUserAddress);
    expect(erc1155_431_initiatorBalance.toNumber()).to.be.deep.equal(initiatorTokenAmounts[1]);
    expect(firstUserBalance.sub(await appUser.signer.getBalance()).lt(parseEther("1"))).to.be.equal(true);
    expect(secondUserBalance.sub(await otherAppUser.signer.getBalance()).lt(parseEther("1"))).to.be.equal(true);
  });

  it('Should fail to initiate swap if swap canceled', async function () {
    const initiatorTokenIds = [170, 171];
    const initiatorTokenAmounts = [0, 90];

    await appUserERC721.mint(appUserAddress, initiatorTokenIds[0]);
    await appUserERC721.approve(swapKiwi.address, initiatorTokenIds[0]);

    await appUserERC1155.mint(appUserAddress, initiatorTokenIds[1], initiatorTokenAmounts[1]);
    await appUserERC1155.setApprovalForAll(swapKiwi.address, true);

    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserERC721.address, appUserERC1155.address], initiatorTokenIds, initiatorTokenAmounts, {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());
    const cancelTx = await appUser.cancelSwap(swapIdFromLogs);
    await cancelTx.wait(1);

    await otherAppUserERC721.mint(otherAppUserAddress, 301);
    await otherAppUserERC721.approve(swapKiwi.address, 301);
    await expect(otherAppUser.initiateSwap(swapIdFromLogs, [otherAppUserERC721.address], [301], [0], {
      value: VALID_APP_FEE
    })).to.be.rejectedWith(
      `SwapKiwi: caller is not swap participator`
    );
  });

  it('Should fail to initiate swap with invalid app fee', async function () {
    const tx = await appUser.proposeSwap(otherAppUserAddress, [], [], [], {
      value: VALID_APP_FEE.add(parseEther("0.1"))
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await expect(otherAppUser.initiateSwap(swapIdFromLogs, [], [], [], {
      value: parseEther("0.01")
    })).to.be.rejectedWith(
      `SwapKiwi: Sent ETH amount needs to be more or equal application fee`
    );
  });

  it('Should fail to initiate swap twice', async function () {
    const initiatorTokenIds = [189, 190];
    const initiatorTokenAmounts = [0, 75];

    await appUserERC721.mint(appUserAddress, initiatorTokenIds[0]);
    await appUserERC721.approve(swapKiwi.address, initiatorTokenIds[0]);

    await appUserERC1155.mint(appUserAddress, initiatorTokenIds[1], initiatorTokenAmounts[1]);
    await appUserERC1155.setApprovalForAll(swapKiwi.address, true);

    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserERC721.address, appUserERC1155.address], initiatorTokenIds, initiatorTokenAmounts, {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());
    await otherAppUserERC721.mint(otherAppUserAddress, 302);
    await otherAppUserERC721.approve(swapKiwi.address, 302);
    await otherAppUser.initiateSwap(swapIdFromLogs, [otherAppUserERC721.address], [302], [0], {
      value: VALID_APP_FEE
    })

    await otherAppUserERC721.mint(otherAppUserAddress, 303);
    await otherAppUserERC721.approve(swapKiwi.address, 303);
    await expect(otherAppUser.initiateSwap(swapIdFromLogs, [otherAppUserERC721.address], [303], [0], {
      value: VALID_APP_FEE
    })).to.be.rejectedWith(
      "SwapKiwi: swap already initiated"
    );
  });

  it('Should fail to initiate swap twice if proposed only with ether', async function () {
    const initiatorTokenIds = [1732, 1733];
    const initiatorTokenAmounts = [0, 100];

    await appUserERC721.mint(appUserAddress, 1732);
    await appUserERC721.approve(swapKiwi.address, 1732);

    await appUserERC1155.mint(appUserAddress, initiatorTokenIds[1], initiatorTokenAmounts[1]);
    await appUserERC1155.setApprovalForAll(swapKiwi.address, true);

    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserERC721.address, appUserERC1155.address], initiatorTokenIds, initiatorTokenAmounts, {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());
    await otherAppUser.initiateSwap(swapIdFromLogs, [], [], [], {
      value: VALID_APP_FEE.add(ethers.utils.parseEther("0.25"))
    })


    await otherAppUserERC721.mint(otherAppUserAddress, 1733);
    await otherAppUserERC721.approve(swapKiwi.address, 1733);
    await expect(otherAppUser.initiateSwap(swapIdFromLogs, [otherAppUserERC721.address], [1733], [0], {
      value: VALID_APP_FEE
    })).to.be.rejectedWith(
      "SwapKiwi: swap already initiated"
    );
  });

  it('Should fail to cancel swap twice', async function () {
    const initiatorTokenIds = [200, 201];
    const initiatorTokenAmounts = [0, 2];

    await appUserERC721.mint(appUserAddress, initiatorTokenIds[0]);
    await appUserERC721.approve(swapKiwi.address, initiatorTokenIds[0]);

    await appUserERC1155.mint(appUserAddress, initiatorTokenIds[1], initiatorTokenAmounts[1]);
    await appUserERC1155.setApprovalForAll(swapKiwi.address, true);

    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserERC721.address, appUserERC1155.address], initiatorTokenIds, initiatorTokenAmounts, {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    const cancelTx = await appUser.cancelSwap(swapIdFromLogs);
    const cancelTxReceipt = await cancelTx.wait(1);
    const cancelTxlogs = await getEventWithArgsFromLogs(cancelTxReceipt, "SwapCanceled");

    // check if all values are emitted in event
    expect(cancelTxlogs.eventName).to.be.deep.equal("SwapCanceled");
    expect(cancelTxlogs.args.canceledBy).to.be.deep.equal(appUserAddress);
    // expect that swap ID from "SwapCanceled" is same as swap ID from "swapProposed" event
    expect(cancelTxlogs.args.swapId.toString()).to.be.deep.equal(String(swapIdFromLogs));
    // check that NFT is returned to initial owner
    expect(await appUserERC721.ownerOf(200)).to.be.deep.equal(appUserAddress);

    await expect(appUser.cancelSwap(swapIdFromLogs)).to.be.rejectedWith(
      "SwapKiwi: Can't cancel swap, must be swap participant"
    );
  });

  it("Should fail to cancel swap if user is not a swap participant", async function () {
    const nonSwapParticipant = new ethers.Contract(SwapKiwi.address, SwapKiwi.abi, signers[6]) as SwapKiwi;

    const initiatorTokenIds = [70, 71];
    const initiatorTokenAmounts = [0, 75];

    // first user NFT minting and swap deposit into SwapKiwi
    await appUserERC721.mint(appUserAddress, initiatorTokenIds[0]);
    await appUserERC721.approve(swapKiwi.address, initiatorTokenIds[0]);

    await appUserERC1155.mint(appUserAddress, initiatorTokenIds[1], initiatorTokenAmounts[1]);
    await appUserERC1155.setApprovalForAll(swapKiwi.address, true);

    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserERC721.address, appUserERC1155.address], initiatorTokenIds, initiatorTokenAmounts, {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await expect(nonSwapParticipant.cancelSwap(swapIdFromLogs)).to.be.rejectedWith(
      "SwapKiwi: Can't cancel swap, must be swap participant");
  });

  it("Should fail to accept swap if second user didn't add NFTs or ether", async function () {
    const initiatorTokenIds = [2000, 2001];
    const initiatorTokenAmounts = [0, 10];

    // first user NFT minting and swap deposit into SwapKiwi
    await appUserERC721.mint(appUserAddress, initiatorTokenIds[0]);
    await appUserERC721.approve(swapKiwi.address, initiatorTokenIds[0]);

    await appUserERC1155.mint(appUserAddress, initiatorTokenIds[1], initiatorTokenAmounts[1]);
    await appUserERC1155.setApprovalForAll(swapKiwi.address, true);

    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserERC721.address, appUserERC1155.address], initiatorTokenIds, initiatorTokenAmounts, {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await expect(appUser.acceptSwap(swapIdFromLogs)).to.be.rejectedWith(
      "SwapKiwi: Can't accept swap, both participants didn't add NFTs");
  });

  it('Should fail to accept swap if not swap initiator', async function () {
    await appUserERC721.mint(appUserAddress, 2100);
    await appUserERC721.approve(swapKiwi.address, 2100);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserERC721.address], [2100], [0], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [],
      [],
      [],
      {
        value: VALID_APP_FEE.add(parseEther("50"))
      }
    );
    await initiateSwapTx.wait(1);

    await expect(otherAppUser.acceptSwap(swapIdFromLogs)).to.be.rejectedWith(
      "SwapKiwi: caller is not swap initiator"
    );
  });

  it('Should successfully execute NFT - NFT swap', async function () {
    // first user NFT minting and swap deposit into SwapKiwi
    const initiatorNftIds = [85, 86, 55];
    const initiatorNftAmounts = [0, 0, 10];
    const secondUserNftIds = [87, 88, 56];
    const secondUserNftAmounts = [0, 0, 15];

    await appUserERC721.mint(appUserAddress, initiatorNftIds[0]);
    await appUserERC721.mint(appUserAddress, initiatorNftIds[1]);
    await appUserERC721.approve(swapKiwi.address, initiatorNftIds[0]);
    await appUserERC721.approve(swapKiwi.address, initiatorNftIds[1]);
    await appUserERC1155.mint(appUserAddress, initiatorNftIds[2], initiatorNftAmounts[2]);
    await appUserERC1155.setApprovalForAll(swapKiwi.address, true);

    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserERC721.address, appUserERC721.address, appUserERC1155.address], initiatorNftIds, initiatorNftAmounts, {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    const swap_erc1155_55_balance = await appUserERC1155.balanceOf(swapKiwi.address, 55);

    // check that first user NFTs are deposited into SwapKiwi
    expect(await appUserERC721.ownerOf(85)).to.be.deep.equal(swapKiwi.address);
    expect(await appUserERC721.ownerOf(86)).to.be.deep.equal(swapKiwi.address);
    expect(swap_erc1155_55_balance.toNumber()).to.be.deep.equal(initiatorNftAmounts[2]);

    // second user NFT minting and swap deposit into SwapKiwi
    await otherAppUserERC721.mint(otherAppUserAddress, secondUserNftIds[0]);
    await otherAppUserERC721.mint(otherAppUserAddress, secondUserNftIds[1]);
    await otherAppUserERC721.approve(swapKiwi.address, secondUserNftIds[0]);
    await otherAppUserERC721.approve(swapKiwi.address, secondUserNftIds[1]);
    await otherAppUserERC1155.mint(otherAppUserAddress, secondUserNftIds[2], secondUserNftAmounts[2]);
    await otherAppUserERC1155.setApprovalForAll(swapKiwi.address, true);

    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [otherAppUserERC721.address, otherAppUserERC721.address, otherAppUserERC1155.address],
      secondUserNftIds,
      secondUserNftAmounts,
      {
        value: VALID_APP_FEE
      }
    );
    const initiateSwapTxReceipt = await initiateSwapTx.wait(1);
    const initiateSwapLogs = await getEventWithArgsFromLogs(initiateSwapTxReceipt, "SwapInitiated");
    
    const swap_erc1155_56_balance = await otherAppUserERC1155.balanceOf(swapKiwi.address, secondUserNftIds[2]);

    // check if all values are emitted in "SwapInitiated" event
    expect(initiateSwapLogs.eventName).to.be.deep.equal("SwapInitiated");
    expect(initiateSwapLogs.args.from).to.be.deep.equal(otherAppUserAddress);
    expect(initiateSwapLogs.args.to).to.be.deep.equal(appUserAddress);
    
    // check that second user NFTs are deposited into SwapKiwi
    expect(await otherAppUserERC721.ownerOf(87)).to.be.deep.equal(swapKiwi.address);
    expect(await otherAppUserERC721.ownerOf(88)).to.be.deep.equal(swapKiwi.address);
    expect(swap_erc1155_56_balance.toNumber()).to.be.deep.equal(secondUserNftAmounts[2]);

    const acceptSwapTx = await appUser.acceptSwap(swapIdFromLogs);
    const acceptSwapTxReceipt = await acceptSwapTx.wait(1);
    const acceptSwapLogs = await getEventWithArgsFromLogs(acceptSwapTxReceipt, "SwapExecuted");

    // check if all values are emitted in "SwapExecuted" event
    expect(acceptSwapLogs.eventName).to.be.deep.equal("SwapExecuted");
    expect(acceptSwapLogs.args.from).to.be.deep.equal(appUserAddress);
    expect(acceptSwapLogs.args.to).to.be.deep.equal(otherAppUserAddress);
    // check that NFTs are transfered from SwapKiwi to participants - same address because both have same signer

    expect(await otherAppUserERC721.ownerOf(85)).to.be.deep.equal(otherAppUserAddress);
    expect(await otherAppUserERC721.ownerOf(86)).to.be.deep.equal(otherAppUserAddress);
    expect(await appUserERC721.ownerOf(87)).to.be.deep.equal(appUserAddress);
    expect(await appUserERC721.ownerOf(88)).to.be.deep.equal(appUserAddress);

    const initiator_erc1155_56_balance = await otherAppUserERC1155.balanceOf(appUserAddress, secondUserNftIds[2]);
    expect(initiator_erc1155_56_balance.toNumber()).to.be.deep.equal(secondUserNftAmounts[2]);

    const secondUser_erc1155_55_balance = await otherAppUserERC1155.balanceOf(otherAppUserAddress, initiatorNftIds[2]);
    expect(secondUser_erc1155_55_balance.toNumber()).to.be.deep.equal(initiatorNftAmounts[2]);
  });

  it('Should successfully execute NFT + ether - NFT + ether swap', async function () {
    const firstUserBalance = await appUser.signer.getBalance();
    const secondUserBalance = await otherAppUser.signer.getBalance();

    const initiatorTokenIds = [375, 1376, 1377];
    const initiatorTokenAmounts = [0, 10, 20];

    await appUserERC1155.mint(appUserAddress, initiatorTokenIds[1], initiatorTokenAmounts[1]);
    await appUserERC1155.mint(appUserAddress, initiatorTokenIds[2], initiatorTokenAmounts[2]);

    await appUserERC1155.setApprovalForAll(swapKiwi.address, true);

    await appUserERC721.mint(appUserAddress, initiatorTokenIds[0]);
    await appUserERC721.approve(swapKiwi.address, initiatorTokenIds[0]);
    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserERC721.address, appUserERC1155.address, appUserERC1155.address], initiatorTokenIds, initiatorTokenAmounts, {
      value: VALID_APP_FEE.add(parseEther("50"))
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    const secondUserTokenIds = [376, 2376, 2377];
    const secondUserTokenAmounts = [0, 50, 60];

    await otherAppUserERC721.mint(otherAppUserAddress, secondUserTokenIds[0]);
    await otherAppUserERC721.approve(swapKiwi.address, secondUserTokenIds[0]);

    await otherAppUserERC1155.mint(otherAppUserAddress, secondUserTokenIds[1], secondUserTokenAmounts[1]);
    await otherAppUserERC1155.mint(otherAppUserAddress, secondUserTokenIds[2], secondUserTokenAmounts[2]);

    await otherAppUserERC1155.setApprovalForAll(swapKiwi.address, true);

    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [otherAppUserERC721.address, otherAppUserERC1155.address, otherAppUserERC1155.address],
      secondUserTokenIds,
      secondUserTokenAmounts,
      {
        value: VALID_APP_FEE.add(parseEther("25"))
      }
    );
    await initiateSwapTx.wait(1);

    const acceptSwapTx = await appUser.acceptSwap(swapIdFromLogs);
    await acceptSwapTx.wait(1);

    const erc1155_2376_initiatorBalance = await otherAppUserERC1155.balanceOf(appUserAddress, secondUserTokenIds[1]);
    const erc1155_2377_initiatorBalance = await otherAppUserERC1155.balanceOf(appUserAddress, secondUserTokenIds[2]);

    const erc1155_1376_secondUserBalance = await appUserERC1155.balanceOf(otherAppUserAddress, initiatorTokenIds[1]);
    const erc1155_1377_secondUserBalance = await appUserERC1155.balanceOf(otherAppUserAddress, initiatorTokenIds[2]);

    expect(await appUserERC721.ownerOf(375)).to.be.deep.equal(otherAppUserAddress);
    expect(await otherAppUserERC721.ownerOf(376)).to.be.deep.equal(appUserAddress);
    expect(erc1155_2376_initiatorBalance.toNumber()).to.be.deep.equal(secondUserTokenAmounts[1]);
    expect(erc1155_2377_initiatorBalance.toNumber()).to.be.deep.equal(secondUserTokenAmounts[2]);
    expect(erc1155_1376_secondUserBalance.toNumber()).to.be.deep.equal(initiatorTokenAmounts[1]);
    expect(erc1155_1377_secondUserBalance.toNumber()).to.be.deep.equal(initiatorTokenAmounts[2]);
    expect(firstUserBalance.sub((await appUser.signer.getBalance()).add(parseEther("25"))).lt(parseEther("1"))).to.be.equal(true);
    expect(secondUserBalance.sub((await otherAppUser.signer.getBalance()).sub(parseEther("25"))).lt(parseEther("1"))).to.be.equal(true);
  });

  it('Should successfully execute ether - NFT swap', async function () {
    const secondUserBalance = await otherAppUser.signer.getBalance();

    const tx = await appUser.proposeSwap(otherAppUserAddress, [], [], [], {
      value: VALID_APP_FEE.add(parseEther("50"))
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    const secondUserTokenIds = [1800, 1801, 1802];
    const secondUserTokenAmounts = [0, 50, 60];

    await otherAppUserERC721.mint(otherAppUserAddress, secondUserTokenIds[0]);
    await otherAppUserERC721.approve(swapKiwi.address, secondUserTokenIds[0]);

    await otherAppUserERC1155.mint(otherAppUserAddress, secondUserTokenIds[1], secondUserTokenAmounts[1]);
    await otherAppUserERC1155.mint(otherAppUserAddress, secondUserTokenIds[2], secondUserTokenAmounts[2]);

    await otherAppUserERC1155.setApprovalForAll(swapKiwi.address, true);

    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [otherAppUserERC721.address, otherAppUserERC1155.address, otherAppUserERC1155.address],
      secondUserTokenIds,
      secondUserTokenAmounts,
      {
        value: VALID_APP_FEE
      }
    );
    await initiateSwapTx.wait(1);

    const acceptSwapTx = await appUser.acceptSwap(swapIdFromLogs);
    await acceptSwapTx.wait(1);

    const erc1155_1801_initiatorBalance = await otherAppUserERC1155.balanceOf(appUserAddress, secondUserTokenIds[1]);
    const erc1155_1802_initiatorBalance = await otherAppUserERC1155.balanceOf(appUserAddress, secondUserTokenIds[2]);

    expect(await otherAppUserERC721.ownerOf(1800)).to.be.deep.equal(appUserAddress);
    expect(erc1155_1801_initiatorBalance.toNumber()).to.be.deep.equal(secondUserTokenAmounts[1]);
    expect(erc1155_1802_initiatorBalance.toNumber()).to.be.deep.equal(secondUserTokenAmounts[2]);
    expect(
      (
        await otherAppUser.signer.getBalance()
      ).sub(secondUserBalance).sub(parseEther("50")).lt(parseEther("1"))).to.be.equal(true);
  });

  it('Should successfully execute NFT - ether swap', async function () {
    const firstUserBalance = await appUser.signer.getBalance();

    const initiatorTokenIds = [1822, 1823, 1825];
    const initiatorTokenAmounts = [0, 10, 20];

    await appUserERC721.mint(appUserAddress, initiatorTokenIds[0]);
    await appUserERC721.approve(swapKiwi.address, initiatorTokenIds[0]);

    await appUserERC1155.mint(appUserAddress, initiatorTokenIds[1], initiatorTokenAmounts[1]);
    await appUserERC1155.mint(appUserAddress, initiatorTokenIds[2], initiatorTokenAmounts[2]);

    await appUserERC1155.setApprovalForAll(swapKiwi.address, true);

    const tx = await appUser.proposeSwap(otherAppUserAddress, [appUserERC721.address, appUserERC1155.address, appUserERC1155.address], initiatorTokenIds, initiatorTokenAmounts, {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    const initiateSwapTx = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [],
      [],
      [],
      {
        value: VALID_APP_FEE.add(parseEther("50"))
      }
    );
    await initiateSwapTx.wait(1);

    const acceptSwapTx = await appUser.acceptSwap(swapIdFromLogs);
    await acceptSwapTx.wait(1);

    const erc1155_1823_otherUserBalance = await appUserERC1155.balanceOf(otherAppUserAddress, initiatorTokenIds[1]);
    const erc1155_1825_otherUserBalance = await appUserERC1155.balanceOf(otherAppUserAddress, initiatorTokenIds[2]);

    expect(await appUserERC721.ownerOf(initiatorTokenIds[0])).to.be.deep.equal(otherAppUserAddress);
    expect(erc1155_1823_otherUserBalance.toNumber()).to.be.deep.equal(initiatorTokenAmounts[1]);
    expect(erc1155_1825_otherUserBalance.toNumber()).to.be.deep.equal(initiatorTokenAmounts[2]);
    expect(
      (
        await appUser.signer.getBalance()
      ).sub(firstUserBalance).sub(parseEther("50")).lt(parseEther("1"))).to.be.equal(true);
  });

  it("Should successfully withdraw only collected fees", async function () {
    await swapKiwi.withdrawEther(await signers[7].getAddress());

    const tx1 = await appUser.proposeSwap(otherAppUserAddress, [], [], [], {
      value: VALID_APP_FEE.add(ethers.utils.parseEther("1"))
    });
    const txReceipt1 = await tx1.wait(1);
    const logs1 = await getEventWithArgsFromLogs(txReceipt1, "SwapProposed");
    const swapIdFromLogs1 = Number(logs1.args.swapId.toString());
    const initiateSwapTx1 = await otherAppUser.initiateSwap(
      swapIdFromLogs1,
      [],
      [],
      [],
      {
        value: VALID_APP_FEE.add(parseEther("5"))
      }
    );
    await initiateSwapTx1.wait(1);
    const acceptSwapTx1 = await appUser.acceptSwap(swapIdFromLogs1);
    await acceptSwapTx1.wait(1);
    const tx2 = await appUser.proposeSwap(otherAppUserAddress, [], [], [], {
      value: VALID_APP_FEE.add(ethers.utils.parseEther("1"))
    });
    const txReceipt2 = await tx2.wait(1);
    const logs2 = await getEventWithArgsFromLogs(txReceipt2, "SwapProposed");
    const swapIdFromLogs = Number(logs2.args.swapId.toString());
    const initiateSwapTx2 = await otherAppUser.initiateSwap(
      swapIdFromLogs,
      [],
      [],
      [],
      {
        value: VALID_APP_FEE.add(parseEther("5"))
      }
    );
    await initiateSwapTx2.wait(1);

    await swapKiwi.withdrawEther(appUserERC721.address)

    expect((await ethers.provider.getBalance(appUserERC721.address)).toString())
      .to.be.deep.equal(VALID_APP_FEE.mul(4).toString())
  });

  it("Should fail to withdraw collected fees if not owner", async function () {
    await expect(appUser.withdrawEther(appUser.address))
      .to.be.rejectedWith(
        "Ownable: caller is not the owner");
  });

  it("Should fail to withdraw collected fees if sent to zero address", async function () {
    await expect(swapKiwi.withdrawEther("0x0000000000000000000000000000000000000000"))
      .to.be.rejectedWith("SwapKiwi: transfer to the zero address");
  });

  it("The initiator should get their parts even if the common cancelling(part) is failed. And then the second user can get its part through a single side cancel", async function () {
    const tokenIds = [1827, 1828];
    const tokenAmounts = [10, 20];

    const secondUserBalance = await secondUserParticipant.signer.getBalance();

    await appUserERC1155.mint(appUserAddress, tokenIds[0], tokenAmounts[0]);
    await otherAppUserERC1155.mint(secondUserParticipant.address, tokenIds[1], tokenAmounts[1]);

    await appUserERC1155.setApprovalForAll(swapKiwi.address, true);
    await secondUserParticipant.setSwap(swapKiwi.address);

    const tx = await appUser.proposeSwap(secondUserParticipant.address, [appUserERC1155.address], [tokenIds[0]], [tokenAmounts[0]], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await secondUserParticipant.initiateSwap(swapIdFromLogs, [otherAppUserERC1155.address], [tokenIds[1]], [tokenAmounts[1]], {
      value: VALID_APP_FEE.add(parseEther("50"))
    });

    await secondUserParticipant.setCounter(10);

    await appUser.cancelSwap(swapIdFromLogs);

    const initiator_erc1155BalanceAfterCancel = await appUserERC1155.balanceOf(appUserAddress, tokenIds[0]);
    // check that ERC721 and ERC1155 are returned to initial owner
    expect(initiator_erc1155BalanceAfterCancel.toNumber()).to.be.deep.equal(tokenAmounts[0]);
  
    await secondUserParticipant.setCounter(0);
    await secondUserParticipant.cancelSwapBySecondUser(swapIdFromLogs);
    const secondUser_erc1155BalanceAfterCancel = await otherAppUserERC1155.balanceOf(secondUserParticipant.address, tokenIds[1]);
    expect(secondUser_erc1155BalanceAfterCancel.toNumber()).to.be.deep.equal(tokenAmounts[1]);
    expect(secondUserBalance.sub(await secondUserParticipant.signer.getBalance()).lt(parseEther("1"))).to.be.equal(true);
  });

  it("The initiator should get their part even if the common cancelling(full) is failed. And then the second user can get its part through a single side cancel", async function () {
    const tokenIds = [11827, 11828];
    const tokenAmounts = [10, 20];

    const secondUserBalance = await secondUserParticipant.signer.getBalance();

    await appUserERC1155.mint(initiatorParticipant.address, tokenIds[0], tokenAmounts[0]);
    await otherAppUserERC1155.mint(secondUserParticipant.address, tokenIds[1], tokenAmounts[1]);

    await initiatorParticipant.setSwap(swapKiwi.address);
    await secondUserParticipant.setSwap(swapKiwi.address);

    const tx = await initiatorParticipant.proposeSwap(secondUserParticipant.address, [appUserERC1155.address], [tokenIds[0]], [tokenAmounts[0]], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await secondUserParticipant.initiateSwap(swapIdFromLogs, [otherAppUserERC1155.address], [tokenIds[1]], [tokenAmounts[1]], {
      value: VALID_APP_FEE.add(parseEther("50"))
    });

    await initiatorParticipant.setCounter(10);
    await expect(initiatorParticipant.cancelSwap(swapIdFromLogs))
      .to.be.rejectedWith("The malicious onERC1155Received contract");

    const initiator_erc1155BalanceAfterCancel = await appUserERC1155.balanceOf(initiatorParticipant.address, tokenIds[0]);
    // check that ERC721 and ERC1155 are returned to initial owner
    expect(initiator_erc1155BalanceAfterCancel.toNumber()).to.be.deep.equal(0);
  
    await secondUserParticipant.setCounter(0);
    await secondUserParticipant.cancelSwapBySecondUser(swapIdFromLogs);
    const secondUser_erc1155BalanceAfterCancel = await otherAppUserERC1155.balanceOf(secondUserParticipant.address, tokenIds[1]);
    expect(secondUser_erc1155BalanceAfterCancel.toNumber()).to.be.deep.equal(tokenAmounts[1]);
    expect(secondUserBalance.sub(await secondUserParticipant.signer.getBalance()).lt(parseEther("1"))).to.be.equal(true);
  });

  it("The second user can get its part through a single side cancel. After that, the initiator can get its part through the common cancel", async function () {
    const tokenIds = [1227, 1228];
    const tokenAmounts = [10, 20];

    const secondUserBalance = await secondUserParticipant.signer.getBalance();

    await appUserERC1155.mint(appUserAddress, tokenIds[0], tokenAmounts[0]);
    await otherAppUserERC1155.mint(secondUserParticipant.address, tokenIds[1], tokenAmounts[1]);

    await appUserERC1155.setApprovalForAll(swapKiwi.address, true);
    await secondUserParticipant.setSwap(swapKiwi.address);

    const tx = await appUser.proposeSwap(secondUserParticipant.address, [appUserERC1155.address], [tokenIds[0]], [tokenAmounts[0]], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await secondUserParticipant.initiateSwap(swapIdFromLogs, [otherAppUserERC1155.address], [tokenIds[1]], [tokenAmounts[1]], {
      value: VALID_APP_FEE.add(parseEther("50"))
    });

    await secondUserParticipant.cancelSwapBySecondUser(swapIdFromLogs);
    const secondUser_erc1155BalanceAfterCancel = await otherAppUserERC1155.balanceOf(secondUserParticipant.address, tokenIds[1]);
    expect(secondUser_erc1155BalanceAfterCancel.toNumber()).to.be.deep.equal(tokenAmounts[1]);
    expect(secondUserBalance.sub(await secondUserParticipant.signer.getBalance()).lt(parseEther("1"))).to.be.equal(true);

    await appUser.cancelSwap(swapIdFromLogs);
    const initiator_erc1155BalanceAfterCancel = await appUserERC1155.balanceOf(appUserAddress, tokenIds[0]);
    // check that ERC721 and ERC1155 are returned to initial owner
    expect(initiator_erc1155BalanceAfterCancel.toNumber()).to.be.deep.equal(tokenAmounts[0]);
  });

  it("After the second user does a single side cancel, the swap should be failed", async function () {
    const tokenIds = [1027, 1028];
    const tokenAmounts = [10, 20];

    const secondUserBalance = await secondUserParticipant.signer.getBalance();

    await appUserERC1155.mint(appUserAddress, tokenIds[0], tokenAmounts[0]);
    await otherAppUserERC1155.mint(secondUserParticipant.address, tokenIds[1], tokenAmounts[1]);

    await appUserERC1155.setApprovalForAll(swapKiwi.address, true);
    await secondUserParticipant.setSwap(swapKiwi.address);

    const tx = await appUser.proposeSwap(secondUserParticipant.address, [appUserERC1155.address], [tokenIds[0]], [tokenAmounts[0]], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await secondUserParticipant.initiateSwap(swapIdFromLogs, [otherAppUserERC1155.address], [tokenIds[1]], [tokenAmounts[1]], {
      value: VALID_APP_FEE.add(parseEther("50"))
    });

    await secondUserParticipant.cancelSwapBySecondUser(swapIdFromLogs);
    const secondUser_erc1155BalanceAfterCancel = await otherAppUserERC1155.balanceOf(secondUserParticipant.address, tokenIds[1]);
    expect(secondUser_erc1155BalanceAfterCancel.toNumber()).to.be.deep.equal(tokenAmounts[1]);
    expect(secondUserBalance.sub(await secondUserParticipant.signer.getBalance()).lt(parseEther("1"))).to.be.equal(true);

    await expect(appUser.acceptSwap(swapIdFromLogs))
      .to.be.rejectedWith("SwapKiwi: Can't accept swap, both participants didn't add NFTs");
  });

  it("etherLocked amount should not be changed if eth transfer to the second user is failed when common cancel", async function () {
    const tokenIds = [18827, 18828];
    const tokenAmounts = [10, 20];

    const secondUserBalance = await secondUserParticipant.signer.getBalance();

    await appUserERC1155.mint(appUserAddress, tokenIds[0], tokenAmounts[0]);
    await otherAppUserERC1155.mint(secondUserParticipant.address, tokenIds[1], tokenAmounts[1]);

    await appUserERC1155.setApprovalForAll(swapKiwi.address, true);
    await secondUserParticipant.setSwap(swapKiwi.address);

    const tx = await appUser.proposeSwap(secondUserParticipant.address, [appUserERC1155.address], [tokenIds[0]], [tokenAmounts[0]], {
      value: VALID_APP_FEE
    });
    const txReceipt = await tx.wait(1);
    const logs = await getEventWithArgsFromLogs(txReceipt, "SwapProposed");
    const swapIdFromLogs = Number(logs.args.swapId.toString());

    await secondUserParticipant.initiateSwap(swapIdFromLogs, [otherAppUserERC1155.address], [tokenIds[1]], [tokenAmounts[1]], {
      value: VALID_APP_FEE.add(parseEther("50"))
    });

    const locked = await appUser.etherLocked();

    await secondUserParticipant.setCounter2(10);
    await appUser.cancelSwap(swapIdFromLogs);

    const lockedAfterFailed = await appUser.etherLocked();

    expect(lockedAfterFailed.toString()).to.be.deep.equal(locked.toString());
    const secondUser_erc1155BalanceAfterCancel = await otherAppUserERC1155.balanceOf(secondUserParticipant.address, tokenIds[1]);
    expect(secondUser_erc1155BalanceAfterCancel.toNumber()).to.be.deep.equal(tokenAmounts[1]);
    expect(secondUserBalance.sub(await secondUserParticipant.signer.getBalance()).gt(parseEther("50"))).to.be.equal(true);
  });
});
Example #25
Source File: HoprChannels.spec.ts    From hoprnet with GNU General Public License v3.0 5 votes vote down vote up
useFixtures = deployments.createFixture(
  async (_, ops: { skipAnnounceForAccountA: boolean; skipAnnounceForAccountB: boolean }) => {
    const deployer = ethers.provider.getSigner()

    const deployerPubKey = await recoverPublicKeyFromSigner(deployer)
    const accountA = new ethers.Wallet(ACCOUNT_A.privateKey).connect(ethers.provider)
    const accountAPubKey = PublicKey.fromPrivKeyString(accountA.privateKey)
    const accountB = new ethers.Wallet(ACCOUNT_B.privateKey).connect(ethers.provider)
    const accountBPubKey = PublicKey.fromPrivKeyString(accountB.privateKey)

    // run migrations
    const contracts = await deployments.fixture()
    const token = (await ethers.getContractFactory('HoprToken')).attach(contracts['HoprToken'].address) as HoprToken

    const channels = (await ethers.getContractFactory('HoprChannels')).attach(
      contracts['HoprChannels'].address
    ) as HoprChannels

    const mockChannels = (await (
      await ethers.getContractFactory('ChannelsMock', deployer)
    ).deploy(token.address, 0)) as ChannelsMock

    // create deployer the minter
    const minterRole = await token.MINTER_ROLE()
    await token.connect(deployer).grantRole(minterRole, await deployer.getAddress())

    const fundEther = async (addr: string, amount: BigNumberish) =>
      await deployer.sendTransaction({ to: addr, value: amount })

    const fund = async (addr: string, amount: BigNumberish) =>
      await token.connect(deployer).mint(addr, amount + '', ethers.constants.HashZero, ethers.constants.HashZero)

    const approve = async (account: Wallet, amount: BigNumberish) =>
      await token.connect(account).approve(channels.address, amount)

    const fundAndApprove = async (account: Wallet, amount: BigNumberish) => {
      await fund(account.address, amount)
      await approve(account, amount)
    }

    const TICKET_AB_WIN = await createTicket(
      {
        recipient: ACCOUNT_B.address,
        proofOfRelaySecret: PROOF_OF_RELAY_SECRET_0,
        ticketEpoch: '0',
        ticketIndex: '1',
        amount: '10',
        winProb: WIN_PROB_100.toString(),
        channelEpoch: '1'
      },
      ACCOUNT_A,
      SECRET_1
    )
    await fundEther(accountA.address, ethers.utils.parseEther(ChannelStatus.Open + ''))
    await fundEther(accountB.address, ethers.utils.parseEther(ChannelStatus.Open + ''))

    // announce
    if (!ops?.skipAnnounceForAccountA) {
      await channels
        .connect(accountA)
        .announce(accountAPubKey.toUncompressedPubKeyHex(), [], { gasLimit: BigNumber.from('100000') })
    }
    if (!ops?.skipAnnounceForAccountB) {
      await channels
        .connect(accountB)
        .announce(accountBPubKey.toUncompressedPubKeyHex(), [], { gasLimit: BigNumber.from('100000') })
    }

    return {
      token,
      channels,
      deployer,
      deployerPubKey,
      accountA,
      accountAPubKey,
      accountB,
      accountBPubKey,
      fund,
      approve,
      mockChannels,
      fundAndApprove,
      TICKET_AB_WIN
    }
  }
)
Example #26
Source File: 04_Guard.spec.ts    From zodiac with GNU Lesser General Public License v3.0 5 votes vote down vote up
describe("BaseGuard", async () => {
  const [user1, user2] = waffle.provider.getWallets();
  const txHash =
    "0x0000000000000000000000000000000000000000000000000000000000000001";

  const setupTests = deployments.createFixture(async ({ deployments }) => {
    await deployments.fixture();
    const Avatar = await hre.ethers.getContractFactory("TestAvatar");
    const avatar = await Avatar.deploy();
    const iAvatar = await hre.ethers.getContractAt("IAvatar", avatar.address);
    const Module = await hre.ethers.getContractFactory("TestModule");
    const module = await Module.deploy(iAvatar.address, iAvatar.address);
    await avatar.enableModule(module.address);
    const Guard = await hre.ethers.getContractFactory("TestGuard");
    const guard = await Guard.deploy(module.address);
    const tx = {
      to: avatar.address,
      value: 0,
      data: "0x",
      operation: 0,
      avatarTxGas: 0,
      baseGas: 0,
      gasPrice: 0,
      gasToken: AddressZero,
      refundReceiver: AddressZero,
      signatures: "0x",
    };
    return {
      iAvatar,
      guard,
      module,
      tx,
    };
  });

  describe("checkTransaction", async () => {
    it("reverts if test fails", async () => {
      const { guard, tx } = await setupTests();
      await expect(
        guard.checkTransaction(
          tx.to,
          1337,
          tx.data,
          tx.operation,
          tx.avatarTxGas,
          tx.baseGas,
          tx.gasPrice,
          tx.gasToken,
          tx.refundReceiver,
          tx.signatures,
          user1.address
        )
      ).to.be.revertedWith("Cannot send 1337");
    });
    it("checks transaction", async () => {
      const { guard, tx } = await setupTests();
      await expect(
        guard.checkTransaction(
          tx.to,
          tx.value,
          tx.data,
          tx.operation,
          tx.avatarTxGas,
          tx.baseGas,
          tx.gasPrice,
          tx.gasToken,
          tx.refundReceiver,
          tx.signatures,
          user1.address
        )
      )
        .to.emit(guard, "PreChecked")
        .withArgs(true);
    });
  });

  describe("checkAfterExecution", async () => {
    it("reverts if test fails", async () => {
      const { module, guard, tx } = await setupTests();
      await expect(guard.checkAfterExecution(txHash, true)).to.be.revertedWith(
        "Module cannot remove its own guard."
      );
    });
    it("checks state after execution", async () => {
      const { module, guard, tx } = await setupTests();
      await expect(module.setGuard(guard.address))
        .to.emit(module, "ChangedGuard")
        .withArgs(guard.address);
      await expect(guard.checkAfterExecution(txHash, true))
        .to.emit(guard, "PostChecked")
        .withArgs(true);
    });
  });
});
Example #27
Source File: 03_Modifier.spec.ts    From zodiac with GNU Lesser General Public License v3.0 5 votes vote down vote up
describe("Modifier", async () => {
  const [user1, user2] = waffle.provider.getWallets();
  const SENTINEL_MODULES = "0x0000000000000000000000000000000000000001";

  const setupTests = deployments.createFixture(async ({ deployments }) => {
    await deployments.fixture();
    const Avatar = await hre.ethers.getContractFactory("TestAvatar");
    const avatar = await Avatar.deploy();
    const iAvatar = await hre.ethers.getContractAt("IAvatar", avatar.address);
    const Modifier = await hre.ethers.getContractFactory("TestModifier");
    const modifier = await Modifier.deploy(iAvatar.address, iAvatar.address);
    await iAvatar.enableModule(modifier.address);
    // await modifier.enableModule(user1.address);
    const tx = {
      to: avatar.address,
      value: 0,
      data: "0x",
      operation: 0,
      avatarTxGas: 0,
      baseGas: 0,
      gasPrice: 0,
      gasToken: AddressZero,
      refundReceiver: AddressZero,
      signatures: "0x",
    };
    return {
      iAvatar,
      modifier,
      tx,
    };
  });

  describe("enableModule", async () => {
    it("reverts if caller is not the owner", async () => {
      const { modifier } = await setupTests();
      await expect(
        modifier.connect(user2).enableModule(user2.address)
      ).to.be.revertedWith("Ownable: caller is not the owner");
    });

    it("reverts if module is zero address", async () => {
      const { modifier } = await setupTests();
      await expect(modifier.enableModule(AddressZero)).to.be.revertedWith(
        "reverted with custom error 'InvalidModule(\"0x0000000000000000000000000000000000000000\")'"
      );
    });

    it("reverts if module is SENTINEL_MODULES", async () => {
      const { iAvatar, modifier } = await setupTests();
      await expect(modifier.enableModule(SENTINEL_MODULES)).to.be.revertedWith(
        "reverted with custom error 'InvalidModule(\"0x0000000000000000000000000000000000000001\")'"
      );
    });

    it("reverts if module is already enabled", async () => {
      const { modifier } = await setupTests();
      await expect(modifier.enableModule(user1.address))
        .to.emit(modifier, "EnabledModule")
        .withArgs(user1.address);
      await expect(modifier.enableModule(user1.address))
        .to.emit(modifier, "EnabledModule")
        .withArgs(user1.address);
      await expect(modifier.enableModule(user1.address)).to.be.revertedWith(
        "reverted with custom error 'AlreadyEnabledModule(\"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266\")'"
      );
    });

    it("enables a module", async () => {
      const { modifier } = await setupTests();
      await expect(modifier.enableModule(user1.address))
        .to.emit(modifier, "EnabledModule")
        .withArgs(user1.address);
    });
  });

  describe("disableModule", async () => {
    it("reverts if caller is not the owner", async () => {
      const { modifier } = await setupTests();
      await expect(
        modifier.connect(user2).disableModule(SENTINEL_MODULES, user2.address)
      ).to.be.revertedWith("Ownable: caller is not the owner");
    });

    it("reverts if module is zero address", async () => {
      const { modifier } = await setupTests();
      await expect(
        modifier.disableModule(SENTINEL_MODULES, AddressZero)
      ).to.be.revertedWith("reverted with custom error 'InvalidModule(\"0x0000000000000000000000000000000000000000\")'");
    });

    it("reverts if module is SENTINEL_MODULES", async () => {
      const { modifier } = await setupTests();
      await expect(
        modifier.disableModule(SENTINEL_MODULES, SENTINEL_MODULES)
      ).to.be.revertedWith("reverted with custom error 'InvalidModule(\"0x0000000000000000000000000000000000000001\")'");
    });

    it("reverts if module is already disabled", async () => {
      const { modifier } = await setupTests();
      await expect(modifier.enableModule(user1.address))
        .to.emit(modifier, "EnabledModule")
        .withArgs(user1.address);
      await expect(modifier.disableModule(SENTINEL_MODULES, user1.address))
        .to.emit(modifier, "DisabledModule")
        .withArgs(user1.address);
      await expect(
        modifier.disableModule(SENTINEL_MODULES, user1.address)
      ).to.be.revertedWith("reverted with custom error 'AlreadyDisabledModule(\"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266\")'");
    });

    it("disables a module", async () => {
      const { modifier } = await setupTests();
      await expect(modifier.enableModule(user1.address))
        .to.emit(modifier, "EnabledModule")
        .withArgs(user1.address);
      await expect(modifier.disableModule(SENTINEL_MODULES, user1.address))
        .to.emit(modifier, "DisabledModule")
        .withArgs(user1.address);
    });
  });

  describe("isModuleEnabled", async () => {
    it("returns false if SENTINEL_MODULES is provided", async () => {
      const { modifier } = await setupTests();
      await expect(
        await modifier.isModuleEnabled(SENTINEL_MODULES)
      ).to.be.equals(false);
    });

    it("returns false if AddressZero is provided", async () => {
      const { modifier } = await setupTests();
      await expect(await modifier.isModuleEnabled(AddressZero)).to.be.equals(
        false
      );
    });

    it("returns false if module is not enabled", async () => {
      const { modifier } = await setupTests();
      await expect(await modifier.isModuleEnabled(user1.address)).to.be.equals(
        false
      );
    });

    it("returns true if module is enabled", async () => {
      const { modifier } = await setupTests();
      // delete once you figure out why you need to do this twice
      await expect(await modifier.enableModule(user1.address))
        .to.emit(modifier, "EnabledModule")
        .withArgs(user1.address);

      await expect(await modifier.enableModule(user2.address))
        .to.emit(modifier, "EnabledModule")
        .withArgs(user2.address);
      await expect(await modifier.isModuleEnabled(user2.address)).to.be.equals(
        true
      );
    });
  });

  describe("getModulesPaginated", async () => {
    it("returns empty array if no modules are enabled.", async () => {
      const { modifier } = await setupTests();
      let tx = await modifier.getModulesPaginated(SENTINEL_MODULES, 3);
      tx = tx.toString();
      await expect(tx).to.be.equals(
        [[], "0x0000000000000000000000000000000000000000"].toString()
      );
    });

    it("returns one module if one module is enabled", async () => {
      const { modifier } = await setupTests();
      await modifier.enableModule(user1.address);
      let tx = await modifier.getModulesPaginated(SENTINEL_MODULES, 3);
      tx = tx.toString();
      await expect(tx).to.be.equals(
        [
          [user1.address],
          "0x0000000000000000000000000000000000000000",
        ].toString()
      );
    });

    it("returns two modules if two modules are enabled", async () => {
      const { modifier } = await setupTests();
      await expect(modifier.enableModule(user1.address))
        .to.emit(modifier, "EnabledModule")
        .withArgs(user1.address);
      // delete once you figure out why you need to do this twice
      await expect(modifier.enableModule(user2.address))
        .to.emit(modifier, "EnabledModule")
        .withArgs(user2.address);
      let tx = await modifier.getModulesPaginated(SENTINEL_MODULES, 3);
      tx = tx.toString();
      await expect(tx).to.be.equals(
        [
          user2.address,
          user1.address,
          "0x0000000000000000000000000000000000000000",
        ].toString()
      );
    });
  });

  describe("execTransactionFromModule", async () => {
    it("reverts if module is not enabled", async () => {
      const { iAvatar, modifier, tx } = await setupTests();
      await expect(
        modifier.execTransactionFromModule(
          tx.to,
          tx.value,
          tx.data,
          tx.operation
        )
      ).to.be.revertedWith("reverted with custom error 'NotAuthorized(\"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266\")'");
    });

    it("execute a transaction.", async () => {
      const { iAvatar, modifier, tx } = await setupTests();
      await expect(await modifier.enableModule(user1.address))
        .to.emit(modifier, "EnabledModule")
        .withArgs(user1.address);
      // delete once you figure out why you need to do this twice
      await expect(await modifier.enableModule(user1.address))
        .to.emit(modifier, "EnabledModule")
        .withArgs(user1.address);

      await expect(
        modifier.execTransactionFromModule(
          tx.to,
          tx.value,
          tx.data,
          tx.operation
        )
      ).to.emit(modifier, "executed");
    });
  });

  describe("execTransactionFromModuleReturnData", async () => {
    it("reverts if module is not enabled", async () => {
      const { iAvatar, modifier, tx } = await setupTests();
      await expect(
        modifier.execTransactionFromModuleReturnData(
          tx.to,
          tx.value,
          tx.data,
          tx.operation
        )
      ).to.be.revertedWith("reverted with custom error 'NotAuthorized(\"0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266\")'");
    });

    it("execute a transaction.", async () => {
      const { iAvatar, modifier, tx } = await setupTests();
      await expect(await modifier.enableModule(user1.address))
        .to.emit(modifier, "EnabledModule")
        .withArgs(user1.address);
      // delete once you figure out why you need to do this twice
      await expect(await modifier.enableModule(user1.address))
        .to.emit(modifier, "EnabledModule")
        .withArgs(user1.address);

      await expect(
        modifier.execTransactionFromModuleReturnData(
          tx.to,
          tx.value,
          tx.data,
          tx.operation
        )
      ).to.emit(modifier, "executedAndReturnedData");
    });
  });
});
Example #28
Source File: 02_Module.spec.ts    From zodiac with GNU Lesser General Public License v3.0 5 votes vote down vote up
describe("Module", async () => {
  const [user1, user2] = waffle.provider.getWallets();

  const setupTests = deployments.createFixture(async ({ deployments }) => {
    await deployments.fixture();
    const Avatar = await hre.ethers.getContractFactory("TestAvatar");
    const avatar = await Avatar.deploy();
    const iAvatar = await hre.ethers.getContractAt("IAvatar", avatar.address);
    const Module = await hre.ethers.getContractFactory("TestModule");
    const module = await Module.deploy(iAvatar.address, iAvatar.address);
    await avatar.enableModule(module.address);
    const Guard = await hre.ethers.getContractFactory("TestGuard");
    const guard = await Guard.deploy(module.address);
    const tx = {
      to: avatar.address,
      value: 0,
      data: "0x",
      operation: 0,
      avatarTxGas: 0,
      baseGas: 0,
      gasPrice: 0,
      gasToken: AddressZero,
      refundReceiver: AddressZero,
      signatures: "0x",
    };
    return {
      iAvatar,
      guard,
      module,
      tx,
    };
  });

  describe("setAvatar", async () => {
    it("reverts if caller is not the owner", async () => {
      const { iAvatar, module } = await setupTests();
      await module.transferOwnership(user2.address);
      await expect(module.setAvatar(iAvatar.address)).to.be.revertedWith(
        "Ownable: caller is not the owner"
      );
    });

    it("allows owner to set avatar", async () => {
      const { iAvatar, module } = await setupTests();
      await expect(module.setAvatar(iAvatar.address));
    });

    it("emits previous owner and new owner", async () => {
      const { iAvatar, module } = await setupTests();
      await expect(module.setAvatar(user2.address))
        .to.emit(module, "AvatarSet")
        .withArgs(iAvatar.address, user2.address);
    });
  });

  describe("setTarget", async () => {
    it("reverts if caller is not the owner", async () => {
      const { iAvatar, module } = await setupTests();
      await module.transferOwnership(user2.address);
      await expect(module.setTarget(iAvatar.address)).to.be.revertedWith(
        "Ownable: caller is not the owner"
      );
    });

    it("allows owner to set avatar", async () => {
      const { iAvatar, module } = await setupTests();
      await expect(module.setTarget(iAvatar.address));
    });

    it("emits previous owner and new owner", async () => {
      const { iAvatar, module } = await setupTests();
      await expect(module.setTarget(user2.address))
        .to.emit(module, "TargetSet")
        .withArgs(iAvatar.address, user2.address);
    });
  });

  describe("exec", async () => {
    it("skips guard pre-check if no guard is set", async () => {
      const { iAvatar, guard, module, tx } = await setupTests();
      await expect(
        module.executeTransaction(tx.to, tx.value, tx.data, tx.operation)
      );
    });

    it("pre-checks transaction if guard is set", async () => {
      const { iAvatar, guard, module, tx } = await setupTests();
      await module.setGuard(guard.address);
      await expect(
        module.executeTransaction(tx.to, tx.value, tx.data, tx.operation)
      )
        .to.emit(guard, "PreChecked")
        .withArgs(true);
    });

    it("executes a transaction", async () => {
      const { iAvatar, module, tx } = await setupTests();
      await expect(
        module.executeTransaction(tx.to, tx.value, tx.data, tx.operation)
      );
    });

    it("skips post-check if no guard is enabled", async () => {
      const { iAvatar, guard, module, tx } = await setupTests();
      await expect(
        module.executeTransaction(tx.to, tx.value, tx.data, tx.operation)
      ).not.to.emit(guard, "PostChecked");
    });

    it("post-checks transaction if guard is set", async () => {
      const { iAvatar, guard, module, tx } = await setupTests();
      await module.setGuard(guard.address);
      await expect(
        module.executeTransaction(tx.to, tx.value, tx.data, tx.operation)
      )
        .to.emit(guard, "PostChecked")
        .withArgs(true);
    });
  });

  describe("execAndReturnData", async () => {
    it("skips guard pre-check if no guard is set", async () => {
      const { iAvatar, guard, module, tx } = await setupTests();
      await expect(
        module.executeTransactionReturnData(
          tx.to,
          tx.value,
          tx.data,
          tx.operation
        )
      );
    });

    it("pre-checks transaction if guard is set", async () => {
      const { iAvatar, guard, module, tx } = await setupTests();
      await module.setGuard(guard.address);
      await expect(
        module.executeTransactionReturnData(
          tx.to,
          tx.value,
          tx.data,
          tx.operation
        )
      )
        .to.emit(guard, "PreChecked")
        .withArgs(true);
    });

    it("executes a transaction", async () => {
      const { iAvatar, module, tx } = await setupTests();
      await expect(
        module.executeTransactionReturnData(
          tx.to,
          tx.value,
          tx.data,
          tx.operation
        )
      );
    });

    it("skips post-check if no guard is enabled", async () => {
      const { iAvatar, guard, module, tx } = await setupTests();
      await expect(
        module.executeTransactionReturnData(
          tx.to,
          tx.value,
          tx.data,
          tx.operation
        )
      ).not.to.emit(guard, "PostChecked");
    });

    it("post-checks transaction if guard is set", async () => {
      const { iAvatar, guard, module, tx } = await setupTests();
      await module.setGuard(guard.address);
      await expect(
        module.executeTransactionReturnData(
          tx.to,
          tx.value,
          tx.data,
          tx.operation
        )
      )
        .to.emit(guard, "PostChecked")
        .withArgs(true);
    });
  });
});