@ethersproject/providers#TransactionReceipt TypeScript Examples

The following examples show how to use @ethersproject/providers#TransactionReceipt. 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: setup.ts    From integration-tests with MIT License 6 votes vote down vote up
waitForDepositTypeTransaction = async (
  l1OriginatingTx: Promise<TransactionResponse>,
  watcher: Watcher,
  l1Provider: JsonRpcProvider,
  l2Provider: JsonRpcProvider
): Promise<CrossDomainMessagePair> => {
  const res = await l1OriginatingTx
  await res.wait()

  const l1tx = await l1Provider.getTransaction(res.hash)
  const l1receipt = await l1Provider.getTransactionReceipt(res.hash)
  const [l1ToL2XDomainMsgHash] = await watcher.getMessageHashesFromL1Tx(
    res.hash
  )
  const l2receipt = (await watcher.getL2TransactionReceipt(
    l1ToL2XDomainMsgHash
  )) as TransactionReceipt
  const l2tx = await l2Provider.getTransaction(l2receipt.transactionHash)

  return {
    l1tx,
    l1receipt,
    l2tx,
    l2receipt,
  }
}
Example #2
Source File: setup.ts    From integration-tests with MIT License 6 votes vote down vote up
waitForWithdrawalTypeTransaction = async (
  l2OriginatingTx: Promise<TransactionResponse>,
  watcher: Watcher,
  l1Provider: JsonRpcProvider,
  l2Provider: JsonRpcProvider
): Promise<CrossDomainMessagePair> => {
  const res = await l2OriginatingTx
  await res.wait()

  const l2tx = await l2Provider.getTransaction(res.hash)
  const l2receipt = await l2Provider.getTransactionReceipt(res.hash)
  const [l2ToL1XDomainMsgHash] = await watcher.getMessageHashesFromL2Tx(
    res.hash
  )
  const l1receipt = (await watcher.getL1TransactionReceipt(
    l2ToL1XDomainMsgHash
  )) as TransactionReceipt
  const l1tx = await l1Provider.getTransaction(l1receipt.transactionHash)

  return {
    l2tx,
    l2receipt,
    l1tx,
    l1receipt,
  }
}
Example #3
Source File: hooks.ts    From interface-v2 with GNU General Public License v3.0 5 votes vote down vote up
export function useTransactionFinalizer(): (
  receipt: TransactionReceipt,
  customData?: {
    summary?: string;
    approval?: { tokenAddress: string; spender: string };
    claim?: { recipient: string };
  },
) => void {
  const { chainId, account } = useActiveWeb3React();
  const dispatch = useDispatch<AppDispatch>();
  const addPopup = useAddPopup();

  return useCallback(
    (
      receipt: TransactionReceipt,
      {
        summary,
      }: {
        summary?: string;
        claim?: { recipient: string };
        approval?: { tokenAddress: string; spender: string };
      } = {},
    ) => {
      if (!account) return;
      if (!chainId) return;

      const { transactionHash } = receipt;
      if (!transactionHash) {
        throw Error('No transaction hash found.');
      }
      dispatch(
        finalizeTransaction({
          chainId,
          hash: transactionHash,
          receipt: {
            blockHash: receipt.blockHash,
            blockNumber: receipt.blockNumber,
            contractAddress: receipt.contractAddress,
            from: receipt.from,
            status: receipt.status,
            to: receipt.to,
            transactionHash: receipt.transactionHash,
            transactionIndex: receipt.transactionIndex,
          },
        }),
      );
      addPopup(
        {
          txn: {
            hash: transactionHash,
            success: receipt.status === 1,
            summary: summary,
          },
        },
        transactionHash,
      );
    },
    [dispatch, chainId, account, addPopup],
  );
}
Example #4
Source File: transactionErrors.d.ts    From tx2uml with MIT License 5 votes vote down vote up
getTransactionError: (tx: TransactionResponse, receipt: TransactionReceipt, provider: Provider) => Promise<string>
Example #5
Source File: transactionErrors.ts    From tx2uml with MIT License 5 votes vote down vote up
getTransactionError = async (
    tx: TransactionResponse,
    receipt: TransactionReceipt,
    provider: Provider
): Promise<string> => {
    if (typeof tx !== "object") {
        throw TypeError(`tx argument ${tx} must be a transaction object`)
    }
    if (typeof receipt !== "object") {
        throw TypeError(
            `receipt argument ${receipt} must be a transaction receipt object`
        )
    }
    if (receipt.status) {
        throw TypeError(
            "Transaction did not fail. Can only read the revert reason from failed transactions"
        )
    }
    if (!receipt.transactionHash) {
        throw TypeError(`There is no transaction hash on the receipt object`)
    }
    if (receipt.gasUsed === tx.gasLimit) {
        throw Error("Transaction failed as it ran out of gas.")
    }

    let rawMessageData
    try {
        const result = await provider.call(
            {
                ...tx,
            },
            receipt.blockNumber
        )

        // Trim the 0x prefix
        rawMessageData = result.slice(2)
    } catch (e) {
        if (e.message.startsWith("Node error: ")) {
            // Trim "Node error: "
            const errorObjectStr = e.message.slice(12)
            // Parse the error object
            const errorObject = JSON.parse(errorObjectStr)

            if (!errorObject.data) {
                throw Error(
                    "Failed to parse data field error object:" + errorObjectStr
                )
            }

            if (errorObject.data.startsWith("Reverted 0x")) {
                // Trim "Reverted 0x" from the data field
                rawMessageData = errorObject.data.slice(11)
            } else if (errorObject.data.startsWith("0x")) {
                // Trim "0x" from the data field
                rawMessageData = errorObject.data.slice(2)
            } else {
                throw Error(
                    "Failed to parse data field of error object:" +
                        errorObjectStr
                )
            }
        } else {
            throw Error(
                "Failed to parse error message from Ethereum call: " + e.message
            )
        }
    }

    return parseReasonCode(rawMessageData)
}
Example #6
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 #7
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 #8
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 #9
Source File: index.ts    From uniswap-v2-helper with MIT License 4 votes vote down vote up
/**
 * Perform a direct swap between two tokens.
 *
 * Calculates price based on current price on the contracts.
 * Only supports direct erc20 token pairs with a decimals getters.
 */
export async function swapTokens(
  ethersSigner: Signer,
  inputToken: string,
  outputToken: string,
  amount: string,
  isSellOrder: boolean,
  { recipient = '', maxSlippage = 100, maxDelay = 60 * 2 },
): Promise<TransactionReceipt> {
  // resolve recipient
  recipient = recipient === '' ? await ethersSigner.getAddress() : recipient

  // get swap params
  const { amountIn, amountOut, path, deadline } = await getSwapParams(
    inputToken,
    outputToken,
    amount,
    isSellOrder,
    {
      maxSlippage,
      maxDelay,
      ethersProvider: ethersSigner.provider,
    },
  )

  // format addresses
  inputToken = ethers.utils.getAddress(inputToken)

  // create contract instances
  const InputToken = new ethers.Contract(
    inputToken,
    IUniswapV2ERC20.abi,
    ethersSigner,
  )
  const UniswapRouter = new ethers.Contract(
    UniswapRouterAddress,
    IUniswapV2Router.abi,
    ethersSigner,
  )

  // check sufficient balance
  const balance = await InputToken.balanceOf(await ethersSigner.getAddress())
  if (balance.lt(amountIn)) {
    throw new Error('insufficient balance')
  }

  // check sufficient allowance
  const allowance = await InputToken.allowance(
    await ethersSigner.getAddress(),
    UniswapRouter.address,
  )
  if (allowance.lt(amountIn)) {
    await (await InputToken.approve(UniswapRouter.address, amountIn)).wait()
  }

  // perform swap
  const swapTx = await (isSellOrder
    ? await UniswapRouter.swapExactTokensForTokens(
        amountIn,
        amountOut,
        path,
        recipient,
        deadline,
      )
    : await UniswapRouter.swapTokensForExactTokens(
        amountOut,
        amountIn,
        path,
        recipient,
        deadline,
      )
  ).wait()

  // return transaction receipt
  return swapTx
}