@ethersproject/units#parseEther TypeScript Examples

The following examples show how to use @ethersproject/units#parseEther. 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: proposing.ts    From safe-tasks with GNU Lesser General Public License v3.0 6 votes vote down vote up
task("propose", "Create a Safe tx proposal json file")
    .addPositionalParam("address", "Address or ENS name of the Safe to check", undefined, types.string)
    .addParam("to", "Address of the target", undefined, types.string)
    .addParam("value", "Value in ETH", "0", types.string, true)
    .addParam("data", "Data as hex string", "0x", types.string, true)
    .addFlag("delegatecall", "Indicator if tx should be executed as a delegatecall")
    .addFlag("onChainHash", "Get hash from chain (required for pre-1.3.0 version)")
    .setAction(async (taskArgs, hre) => {
        console.log(`Running on ${hre.network.name}`)
        const safe = await safeSingleton(hre, taskArgs.address)
        const safeAddress = await safe.resolvedAddress
        console.log(`Using Safe at ${safeAddress}`)
        const nonce = await safe.nonce()
        if (!isHexString(taskArgs.data)) throw Error(`Invalid hex string provided for data: ${taskArgs.data}`)
        const tx = buildSafeTransaction({ to: taskArgs.to, value: parseEther(taskArgs.value).toString(), data: taskArgs.data, nonce: nonce.toString(), operation: taskArgs.delegatecall ? 1 : 0 })
        const chainId = (await safe.provider.getNetwork()).chainId
        const safeTxHash = await calcSafeTxHash(safe, tx, chainId, taskArgs.onChainHash)
        const proposal: SafeTxProposal = {
            safe: safeAddress,
            chainId,
            safeTxHash,
            tx
        }
        await writeToCliCache(proposalFile(safeTxHash), proposal)
        console.log(`Safe transaction hash: ${safeTxHash}`)
    });
Example #2
Source File: signing.ts    From safe-tasks with GNU Lesser General Public License v3.0 6 votes vote down vote up
task("sign-tx", "Signs a Safe transaction")
    .addPositionalParam("address", "Address or ENS name of the Safe to check", undefined, types.string)
    .addParam("to", "Address of the target", undefined, types.string)
    .addParam("value", "Value in ETH", "0", types.string, true)
    .addParam("data", "Data as hex string", "0x", types.string, true)
    .addParam("signerIndex", "Index of the signer to use", 0, types.int, true)
    .addFlag("delegatecall", "Indicator if tx should be executed as a delegatecall")
    .setAction(async (taskArgs, hre) => {
        console.log(`Running on ${hre.network.name}`)
        const signers = await hre.ethers.getSigners()
        const signer = signers[taskArgs.signerIndex]
        const safe = await safeSingleton(hre, taskArgs.address)
        const safeAddress = await safe.resolvedAddress
        console.log(`Using Safe at ${safeAddress} with ${signer.address}`)
        const nonce = await safe.nonce()
        if (!isHexString(taskArgs.data)) throw Error(`Invalid hex string provided for data: ${taskArgs.data}`)
        const tx = buildSafeTransaction({ to: taskArgs.to, value: parseEther(taskArgs.value), data: taskArgs.data, nonce, operation: taskArgs.delegatecall ? 1 : 0 })
        const signature = await safeSignMessage(signer, safe, tx)
        console.log(`Signature: ${signature.data}`)
    });
Example #3
Source File: submitting.ts    From safe-tasks with GNU Lesser General Public License v3.0 6 votes vote down vote up
task("submit-tx", "Executes a Safe transaction")
    .addPositionalParam("address", "Address or ENS name of the Safe to check", undefined, types.string)
    .addParam("to", "Address of the target", undefined, types.string)
    .addParam("value", "Value in ETH", "0", types.string, true)
    .addParam("data", "Data as hex string", "0x", types.string, true)
    .addParam("signatures", "Comma seperated list of signatures", undefined, types.string, true)
    .addParam("gasPrice", "Gas price to be used", undefined, types.int, true)
    .addParam("gasLimit", "Gas limit to be used", undefined, types.int, true)
    .addFlag("delegatecall", "Indicator if tx should be executed as a delegatecall")
    .setAction(async (taskArgs, hre) => {
        console.log(`Running on ${hre.network.name}`)
        const [signer] = await hre.ethers.getSigners()
        const safe = await safeSingleton(hre, taskArgs.address)
        const safeAddress = await safe.resolvedAddress
        console.log(`Using Safe at ${safeAddress} with ${signer.address}`)
        const nonce = await safe.nonce()
        if (!isHexString(taskArgs.data)) throw Error(`Invalid hex string provided for data: ${taskArgs.data}`)
        const tx = buildSafeTransaction({ 
            to: taskArgs.to, 
            value: parseEther(taskArgs.value), 
            data: taskArgs.data, 
            nonce, 
            operation: taskArgs.delegatecall ? 1 : 0 
        })
        const signatures = await prepareSignatures(safe, tx, taskArgs.signatures, signer)
        const populatedTx: PopulatedTransaction = await populateExecuteTx(safe, tx, signatures, { gasLimit: taskArgs.gasLimit, gasPrice: taskArgs.gasPrice })
        const receipt = await signer.sendTransaction(populatedTx).then(tx => tx.wait())
        console.log(receipt.transactionHash)
    });
Example #4
Source File: useGetBridgeEstimate.ts    From sdk with ISC License 6 votes vote down vote up
function fixAmt(ether: BigNumber, token: Token, chainId: number): BigNumber {
    const decimals = token.decimals(chainId) ?? 18;

    if (decimals === 18) {
        return parseEther(ether.toString())
    }

    const unit = unitNames[decimals];

    return parseUnits(ether.toString(), unit)
}
Example #5
Source File: proposing.ts    From safe-tasks with GNU Lesser General Public License v3.0 5 votes vote down vote up
buildMetaTx = (description: TxDescription): MetaTransaction => {
    const to = getAddress(description.to)
    const value = parseEther(description.value).toString()
    const operation = description.operation
    const data = isHexString(description.data) ? description.data!! : (description.method ? buildData(description.method, description.params) : "0x")
    return { to, value, data, operation }
}
Example #6
Source File: PerpService.ts    From sakeperp-arbitrageur with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
// noinspection JSMethodCanBeStatic
    static toWei(val: Big): BigNumber {
        return parseEther(val.toFixed(18))
    }
Example #7
Source File: config.ts    From noether with Apache License 2.0 5 votes vote down vote up
BALANCE_THRESHOLD = parseEther("0.05")
Example #8
Source File: rarityPayouts.ts    From aavegotchi-contracts with MIT License 4 votes vote down vote up
task("rarityPayout")
  .addParam("season")
  .addParam(
    "rarityDataFile",
    "File that contains all the data related to the particular rarity round"
  )
  .addParam("deployerAddress")
  .addParam("tieBreakerIndex", "The Tiebreaker index")
  .setAction(
    async (taskArgs: RarityPayoutTaskArgs, hre: HardhatRuntimeEnvironment) => {
      const filename: string = taskArgs.rarityDataFile;
      const diamondAddress = maticDiamondAddress;
      const deployerAddress = taskArgs.deployerAddress;

      console.log("deployer:", deployerAddress);
      const accounts = await hre.ethers.getSigners();
      tiebreakerIndex = taskArgs.tieBreakerIndex;

      const testing = ["hardhat", "localhost"].includes(hre.network.name);
      let signer: Signer;
      if (testing) {
        await hre.network.provider.request({
          method: "hardhat_impersonateAccount",
          params: [deployerAddress],
        });
        signer = await hre.ethers.provider.getSigner(deployerAddress);
      } else if (hre.network.name === "matic") {
        signer = accounts[0];
      } else {
        throw Error("Incorrect network selected");
      }

      const managedSigner = new NonceManager(signer);

      const rounds = Number(taskArgs.rounds);

      const signerAddress = await signer.getAddress();
      if (signerAddress !== deployerAddress) {
        throw new Error(
          `Deployer ${deployerAddress} does not match signer ${signerAddress}`
        );
      }

      //Get rewards for this season
      const {
        rewardArgs,
      } = require(`../data/airdrops/rarityfarming/szn${taskArgs.season}/rewards`);
      const rewards: RarityFarmingRewardArgs = rewardArgs;

      const maxProcess = 500;
      const finalRewards: rarityRewards = {};

      //Get data for this round from file
      const {
        dataArgs,
      } = require(`../data/airdrops/rarityfarming/szn${taskArgs.season}/${filename}.ts`);
      const data: RarityFarmingData = dataArgs;

      const leaderboards = [
        "withSetsRarityScore",
        "kinship",
        "experience",
        // "kinship",
        // "experience",
      ];
      const dataNames: LeaderboardDataName[] = [
        "rarityGotchis",
        "kinshipGotchis",
        "xpGotchis",
        // "rookieKinshipGotchis",
        // "rookieXpGotchis",
      ];

      //handle rookie now

      const leaderboardResults: RarityFarmingData = {
        rarityGotchis: [],
        xpGotchis: [],
        kinshipGotchis: [],
      };

      // let extraFilter: string = "";
      for (let index = 0; index < leaderboards.length; index++) {
        // if (
        //   index === leaderboards.length - 1 ||
        //   index === leaderboards.length - 2
        // ) {
        //   console.log("getting rookies");
        //   // extraFilter = rookieFilter;
        // }
        let element: LeaderboardType = leaderboards[index] as LeaderboardType;

        const result = stripGotchis(
          await fetchAndSortLeaderboard(
            element,
            taskArgs.blockNumber,
            Number(taskArgs.tieBreakerIndex)
          )
        );

        const dataName: LeaderboardDataName = dataNames[
          index
        ] as LeaderboardDataName;

        const correct = confirmCorrectness(result, data[dataName]);

        console.log("correct:", correct);

        if (correct !== 7500) {
          throw new Error("Results do not line up with subgraph");
        }

        leaderboardResults[dataName] = result;
      }

      //get rewards
      const rarityRoundRewards: string[] = rewards.rarity;
      const kinshipRoundRewards: string[] = rewards.kinship;
      const xpRoundRewards: string[] = rewards.xp;
      // const rookieKinshipRoundRewards: string[] = rewards.rookieKinship;
      // const rookieXpRoundRewards: string[] = rewards.rookieXp;

      //Iterate through all 5000 spots
      for (let index = 0; index < 7500; index++) {
        const gotchis: string[] = [
          leaderboardResults.rarityGotchis[index],
          leaderboardResults.kinshipGotchis[index],
          leaderboardResults.xpGotchis[index],
          // leaderboardResults.rookieKinshipGotchis[index],
          // leaderboardResults.rookieXpGotchis[index],
        ];

        const rewards: string[][] = [
          rarityRoundRewards,
          kinshipRoundRewards,
          xpRoundRewards,
          // rookieKinshipRoundRewards,
          // rookieXpRoundRewards,
        ];

        rewards.forEach((leaderboard, i) => {
          const gotchi = gotchis[i];
          const reward = leaderboard[index];

          if (finalRewards[gotchi])
            finalRewards[gotchi] += Number(reward) / rounds;
          else {
            finalRewards[gotchi] = Number(reward) / rounds;
          }
        });
      }

      //Check that sent amount matches total amount per round
      const roundAmount = Number(taskArgs.totalAmount) / rounds;
      let talliedAmount = 0;

      Object.keys(finalRewards).map((gotchiId) => {
        const amount = finalRewards[gotchiId];

        if (!isNaN(amount)) {
          talliedAmount = talliedAmount + amount;
        }
      });

      const sorted: string[] = [];
      const sortedKeys = Object.keys(finalRewards).sort((a, b) => {
        return finalRewards[b] - finalRewards[a];
      });

      sortedKeys.forEach((key) => {
        sorted.push(`${key}: ${finalRewards[key]}`);
      });

      console.log("Total GHST to send:", talliedAmount);
      console.log("Round amount:", roundAmount);

      let totalGhstSent = BigNumber.from(0);
      let txData = [];
      let txGroup: TxArgs[] = [];
      let tokenIdsNum = 0;

      for (const gotchiID of Object.keys(finalRewards)) {
        let amount = finalRewards[gotchiID];
        let parsedAmount = BigNumber.from(parseEther(amount.toString()));
        let finalParsed = parsedAmount.toString();

        if (maxProcess < tokenIdsNum + 1) {
          txData.push(txGroup);
          txGroup = [];
          tokenIdsNum = 0;
        }

        txGroup.push({
          tokenID: gotchiID,
          amount: amount,
          parsedAmount: finalParsed,
        });
        tokenIdsNum += 1;
      }

      if (tokenIdsNum > 0) {
        txData.push(txGroup);
        txGroup = [];
        tokenIdsNum = 0;
      }

      for (const [i, txGroup] of txData.entries()) {
        console.log("current index:", i);

        if (i < 10) continue;

        let tokenIds: string[] = [];
        let amounts: string[] = [];

        const removeList = ["17231", "21129", "21944", "16681"];

        txGroup.forEach((sendData) => {
          if (removeList.includes(sendData.tokenID)) {
            console.log(
              `Removing ${sendData.tokenID} because it's on the bad list`
            );
          } else {
            tokenIds.push(sendData.tokenID);
            amounts.push(sendData.parsedAmount);
          }
        });

        let totalAmount = amounts.reduce((prev, curr) => {
          return BigNumber.from(prev).add(BigNumber.from(curr)).toString();
        });

        totalGhstSent = totalGhstSent.add(totalAmount);

        console.log(
          `Sending ${formatEther(totalAmount)} GHST to ${
            tokenIds.length
          } Gotchis (from ${tokenIds[0]} to ${tokenIds[tokenIds.length - 1]})`
        );

        const escrowFacet = (
          await hre.ethers.getContractAt("EscrowFacet", diamondAddress)
        ).connect(managedSigner) as EscrowFacet;
        const tx = await escrowFacet.batchDepositGHST(tokenIds, amounts, {
          gasPrice: gasPrice,
        });

        let receipt: ContractReceipt = await tx.wait();
        console.log("receipt:", receipt.transactionHash);
        console.log("Gas used:", strDisplay(receipt.gasUsed.toString()));
        if (!receipt.status) {
          throw Error(`Error:: ${tx.hash}`);
        }
        console.log("Total GHST Sent:", formatEther(totalGhstSent));
      }
    }
  );
Example #9
Source File: 0004-layer2-clearingHouse-amm-openPos.ts    From perpetual-protocol with GNU General Public License v3.0 4 votes vote down vote up
migration: MigrationDefinition = {
    configPath: "hardhat.flatten.clearinghouse.config.ts",

    // deploy the flattened clearingHouse and init it just in case
    getTasks: (context: MigrationContext) => {
        let arbitrageur: string
        let oldClearingHouseImpAddr: string
        let oldAmmImpAddr: string
        let newClearingHouseImplAddr: string
        let newAmmImplAddr: string
        let ammETHAddr: string
        let arbitrageurPosition: any
        let oldQuoteAssetReserve: BigNumber
        let quoteAssetAddr: string
        return [
            async (): Promise<void> => {
                console.log("get state variables for verification later...")

                // flat clearingHouse
                const fileClearingHouse = `${ContractName.ClearingHouse}.sol`
                await flatten(SRC_DIR, hre.config.paths.sources, fileClearingHouse)
                await hre.run(TASK_COMPILE)

                // flat Amm
                const fileAmm = `${ContractName.Amm}.sol`
                await flatten(SRC_DIR, hre.config.paths.sources, fileAmm)
                await hre.run(TASK_COMPILE)

                const clearingHouseContract = await context.factory
                    .create<ClearingHouse>(ContractFullyQualifiedName.FlattenClearingHouse)
                    .instance()
                const ammContract = await context.factory
                    .createAmm(AmmInstanceName.ETHUSDC, ContractFullyQualifiedName.FlattenAmmUnderClearingHouse)
                    .instance()

                oldClearingHouseImpAddr = await getImplementation(clearingHouseContract.address)
                oldAmmImpAddr = await getImplementation(ammContract.address)
                ammETHAddr = ammContract.address

                arbitrageur = context.externalContract.arbitrageur!
                arbitrageurPosition = await clearingHouseContract.getPosition(ammETHAddr, arbitrageur)
                oldQuoteAssetReserve = await ammContract.quoteAssetReserve()
                quoteAssetAddr = await ammContract.quoteAsset()
            },
            async (): Promise<void> => {
                console.log("prepare upgrading...")

                // deploy clearingHouse implementation
                const clearingHouseContract = await context.factory.create<ClearingHouse>(
                    ContractFullyQualifiedName.FlattenClearingHouse,
                )
                newClearingHouseImplAddr = await clearingHouseContract.prepareUpgradeContractLegacy()

                // deploy Amm implementation
                const ammContract = await context.factory.createAmm(
                    AmmInstanceName.ETHUSDC,
                    ContractFullyQualifiedName.FlattenAmmUnderClearingHouse,
                )
                newAmmImplAddr = await ammContract.prepareUpgradeContractLegacy()
            },
            async (): Promise<void> => {
                console.info("upgrading...")

                // create an impersonated signer
                const govSigner = await impersonateAccount(context.externalContract.foundationGovernance!)

                // prepare information for upgrading
                const contractNameClearingHouse = ContractFullyQualifiedName.FlattenClearingHouse
                const proxyClearingHouseAddr = context.factory.create<ClearingHouse>(contractNameClearingHouse).address!

                const proxyAdmin = await upgrades.admin.getInstance()
                await proxyAdmin.connect(govSigner).upgrade(proxyClearingHouseAddr, newClearingHouseImplAddr)
                console.log(
                    `upgrade: contractFullyQualifiedName=${contractNameClearingHouse}, proxy=${proxyClearingHouseAddr}, implementation=${newClearingHouseImplAddr}`,
                )

                await proxyAdmin.connect(govSigner).upgrade(ammETHAddr, newAmmImplAddr)
                console.log(
                    `upgrade: contractFullyQualifiedName=${contractNameClearingHouse}, proxy=${ammETHAddr}, implementation=${newAmmImplAddr}`,
                )
            },
            // verify can openPosition
            async (): Promise<void> => {
                const clearingHouseContract = await context.factory
                    .create<ClearingHouse>(ContractFullyQualifiedName.FlattenClearingHouse)
                    .instance()

                const gov = context.externalContract.foundationGovernance!
                const govSigner = await impersonateAccount(gov)
                const arbitrageurSigner = await impersonateAccount(arbitrageur)

                const usdcAddr = context.externalContract.usdc!
                const USDCArtifact = await artifacts.readArtifact(ContractFullyQualifiedName.FlattenIERC20)
                const usdcInstance = (await ethers.getContractAt(USDCArtifact.abi, usdcAddr)) as ERC20

                // send eth and usdc to gov account
                const txETH = await arbitrageurSigner.sendTransaction({
                    to: gov,
                    value: ethers.utils.parseEther("0.1"),
                })
                await txETH.wait()
                const txUSDC = await usdcInstance.connect(arbitrageurSigner).transfer(gov, parseUnits("1000", 6))
                await txUSDC.wait()
                const txApprove = await usdcInstance
                    .connect(govSigner)
                    .approve(clearingHouseContract.address, parseUnits("1000", 6))
                await txApprove.wait()

                // open position
                const receipt = await clearingHouseContract
                    .connect(govSigner)
                    .openPosition(
                        ammETHAddr,
                        Side.BUY,
                        { d: parseEther("600") },
                        { d: parseEther("1") },
                        { d: parseEther("0") },
                    )

                const ownerPosition = await clearingHouseContract.getPosition(ammETHAddr, gov)
                expect(ownerPosition.margin.d).to.eq(parseEther("600"))
                expect(ownerPosition.blockNumber).to.eq(receipt.blockNumber)
            },
            // verify arbitrageur's position on ETH market and Amm's reserve
            async (): Promise<void> => {
                const clearingHouseContract = await context.factory
                    .create<ClearingHouse>(ContractFullyQualifiedName.FlattenClearingHouse)
                    .instance()
                const ammContract = await context.factory
                    .createAmm(AmmInstanceName.ETHUSDC, ContractFullyQualifiedName.FlattenAmmUnderClearingHouse)
                    .instance()

                // for comparing with the new implementation address
                console.log("old implementation address of ClearingHouse: ", oldClearingHouseImpAddr)
                console.log(
                    "new implementation address of ClearingHouse: ",
                    await getImplementation(clearingHouseContract.address),
                )
                console.log("old implementation address of Amm: ", oldAmmImpAddr)
                console.log("new implementation address of Amm: ", await getImplementation(ammContract.address))

                console.log("arbitrageur position: ")
                const arbitrageurPositionNow = await clearingHouseContract.getPosition(ammETHAddr, arbitrageur)
                console.log("size: ", arbitrageurPositionNow.size.d.toString())
                console.log("margin: ", arbitrageurPositionNow.margin.d.toString())
                console.log("last updated blockNumber: ", arbitrageurPositionNow.blockNumber.toString())
                expect(arbitrageurPosition.size.d).to.eq(arbitrageurPositionNow.size.d)
                expect(arbitrageurPosition.margin.d).to.eq(arbitrageurPositionNow.margin.d)
                expect(arbitrageurPosition.blockNumber).to.eq(arbitrageurPositionNow.blockNumber)

                console.log("amm states: ")
                const newQuoteAssetReserve = await ammContract.quoteAssetReserve()
                console.log("quote asset reserve: ", oldQuoteAssetReserve.toString())
                console.log("USDC addr: ", quoteAssetAddr.toString())
                expect(newQuoteAssetReserve).to.eq(oldQuoteAssetReserve.add(parseEther("600")))
                expect(await ammContract.quoteAsset()).to.eq(quoteAssetAddr)
            },
        ]
    },
}
Example #10
Source File: ProviderInteractions-test.ts    From sdk with ISC License 4 votes vote down vote up
describe("SynapseBridge - Provider Interactions tests", async function(this: Mocha.Suite) {

    interface TestOpts {
        executeSuccess: boolean,
        canBridge:      boolean,
    }

    interface TestCase extends BridgeSwapTestCase<TestOpts> {
        callStatic: boolean,
    }

    const executeFailAmt: BigNumber = parseEther("420.696969");

    const testCases: TestCase[] = [
        {
            args: {
                tokenFrom:   Tokens.ETH,
                tokenTo:     Tokens.WETH,
                chainIdFrom: ChainId.OPTIMISM,
                chainIdTo:   ChainId.ETH,
                amountFrom:  executeFailAmt,
            },
            expected: {
                executeSuccess: false,
                canBridge:      false,
            },
            callStatic:         false,
        },
        {
            args: {
                tokenFrom:   Tokens.ETH,
                tokenTo:     Tokens.WETH,
                chainIdFrom: ChainId.BOBA,
                chainIdTo:   ChainId.ETH,
                amountFrom:  executeFailAmt,
            },
            expected: {
                executeSuccess: false,
                canBridge:      false,
            },
            callStatic:         true,
        },
        {
            args: {
                tokenFrom:   Tokens.ETH,
                tokenTo:     Tokens.WETH_E,
                chainIdFrom: ChainId.ARBITRUM,
                chainIdTo:   ChainId.AVALANCHE,
                amountFrom:  parseEther("0.005"),
            },
            expected: {
                executeSuccess: true,
                canBridge:      true,
            },
            callStatic:         true,
        },
        // {
        //     args: {
        //         tokenFrom:   Tokens.WETH_E,
        //         tokenTo:     Tokens.ETH,
        //         chainIdFrom: ChainId.AVALANCHE,
        //         chainIdTo:   ChainId.ARBITRUM,
        //         amountFrom:  parseEther("0.05"),
        //     },
        //     expected: {
        //         executeSuccess: false,
        //         canBridge:      false,
        //     },
        //     callStatic:         true,
        // },
        {
            args: {
                tokenFrom:   Tokens.ETH,
                tokenTo:     Tokens.NETH,
                chainIdFrom: ChainId.ETH,
                chainIdTo:   ChainId.OPTIMISM,
                amountFrom:  executeFailAmt,
            },
            expected: {
                executeSuccess: false,
                canBridge:      false,
            },
            callStatic:         true,
        },
        {
            args: {
                tokenFrom:   Tokens.ETH,
                tokenTo:     Tokens.NETH,
                chainIdFrom: ChainId.ETH,
                chainIdTo:   ChainId.OPTIMISM,
                amountFrom:  executeFailAmt,
            },
            expected: {
                executeSuccess: false,
                canBridge:      false,
            },
            callStatic:         false,
        },
        {
            args: {
                tokenFrom:   Tokens.NUSD,
                tokenTo:     Tokens.USDT,
                chainIdFrom: ChainId.POLYGON,
                chainIdTo:   ChainId.FANTOM,
                amountFrom:  parseEther("666"),
            },
            expected: {
                executeSuccess: false,
                canBridge:      false,
            },
            callStatic:         false,
        },
    ];

    const getBridgeEstimate = async (
        tc: TestCase,
        {
            address,
            bridgeInstance,
        }: WalletArgs
    ): Promise<EstimateOutputs> =>
        bridgeInstance.estimateBridgeTokenOutput(tc.args)
            .then(res =>
                ({
                    outputEstimate: res,
                    bridgeArgs: {
                        ...tc.args,
                        amountTo: res.amountToReceive,
                        addressTo: address,
                    }
                })
            )
            .catch(rejectPromise)

    testCases.forEach(tc => {
        const
            describeNetFromTitle: string = `${tc.args.tokenFrom.symbol} on ${Networks.networkName(tc.args.chainIdFrom)}`,
            desribeNetToTitle:    string = `${tc.args.tokenTo.symbol} on ${Networks.networkName(tc.args.chainIdTo)}`,
            execModeTitle:        string = tc.callStatic ? "(CallStatic)" : "(Signer Sends)",
            describeTitle:        string = `Test ${describeNetFromTitle} to ${desribeNetToTitle} ${execModeTitle}`,
            executionTestSuffix:  string = `should ${tc.expected.executeSuccess ? "execute succesfully" : "fail"}`;

        const
            executeTxnTestTitle = (txnKind: string): string => `${txnKind} transaction ${executionTestSuffix}`,
            approvalTxnTestTitle: string = executeTxnTestTitle("ERC20.Approve"),
            bridgeTxnTestTitle:   string = executeTxnTestTitle("SynapseBridge token bridge");

        describe(describeTitle, function(this: Mocha.Suite) {
            let
                walletArgs:     WalletArgs,
                wallet:         Wallet,
                bridgeInstance: Bridge.SynapseBridge;

            before(async function(this: Mocha.Context) {
                this.timeout(DEFAULT_TEST_TIMEOUT);

                walletArgs = await buildWalletArgs(
                    tc.args.chainIdFrom,
                    bridgeInteractionsPrivkey.privkey
                );

                wallet         = walletArgs.wallet;
                bridgeInstance = walletArgs.bridgeInstance;
            })

            function executeTxnFunc(
                tc:       TestCase,
                prom:     Promise<TxnResponse>,
                approval: boolean=false
            ): (ctx: Mocha.Context) => PromiseLike<any> {
                return async function (ctx: Mocha.Context): Promise<void | any> {
                    if (approval && tc.args.tokenFrom.isEqual(Tokens.ETH)) return

                    ctx.timeout(20*1000);

                    let execProm = executeTransaction(prom);

                    return (await (tc.expected.executeSuccess
                            ? expectFulfilled(execProm)
                            : expectRejected(execProm)
                    ))
                }
            }

            function callStaticFunc(
                tc:       TestCase,
                prom:     Promise<StaticCallResult>,
                approval: boolean=false
            ): (ctx: Mocha.Context) => PromiseLike<any> {
                return async function (ctx: Mocha.Context): Promise<void | any> {
                    if (approval && tc.args.tokenFrom.isEqual(Tokens.ETH)) return

                    ctx.timeout(5*1000);

                    let execProm = callStatic(prom);

                    return (await (tc.expected.executeSuccess
                            ? expectFulfilled(execProm)
                            : expectRejected(execProm)
                    ))
                }
            }

            let
                outputEstimate: Bridge.BridgeOutputEstimate,
                doBridgeArgs:   Bridge.BridgeTransactionParams;

            step("acquire output estimate", async function(this: Mocha.Context) {
                this.timeout(DEFAULT_TEST_TIMEOUT);

                let prom = getBridgeEstimate(tc, walletArgs);

                await expectFulfilled(prom);

                const {outputEstimate: estimate, bridgeArgs: bridgeParams} = await prom;

                expectNotZero(estimate.amountToReceive);

                outputEstimate = estimate;
                doBridgeArgs = bridgeParams;

                return
            });

            describe("- checkCanBridge()", function(this: Mocha.Suite) {
                const canBridgeTestTitle: string = `should${tc.expected.canBridge ? "" : " not"} be able to bridge`;

                it(canBridgeTestTitle, function(this: Mocha.Context, done: Mocha.Done) {
                    this.timeout(3.5*1000);
                    this.slow(2*1000);

                    let prom = bridgeInstance.checkCanBridge({
                        token: tc.args.tokenFrom,
                        signer: wallet,
                        amount: tc.args.amountFrom,
                    }).then(({canBridge}) => canBridge)

                    expect(prom).to.eventually.equal(tc.expected.canBridge).notify(done);
                })
            });

            describe("- Transaction Builders", function(this: Mocha.Suite) {
                let
                    approvalTxn:     PopulatedTransaction,
                    bridgeTxn:       PopulatedTransaction;

                const
                    approveTitle: string = "approval transaction should be populated successfully",
                    bridgeTitle:  string = "bridge transaction should be populated successfully";

                step(approveTitle, async function(this: Mocha.Context) {
                    if (tc.args.tokenFrom.isEqual(Tokens.ETH)) return
                    this.timeout(DEFAULT_TEST_TIMEOUT);

                    return (await expectFulfilled(
                        bridgeInstance
                            .buildApproveTransaction({token: tc.args.tokenFrom})
                            .then((txn) => approvalTxn = txn)
                    ))
                });

                step(bridgeTitle, async function(this: Mocha.Context) {
                    this.timeout(DEFAULT_TEST_TIMEOUT);

                    return (await expectFulfilled(
                        bridgeInstance.buildBridgeTokenTransaction(doBridgeArgs)
                            .then((txn) => bridgeTxn = txn)
                    ))
                });

                const approval = true;

                step(approvalTxnTestTitle, async function(this: Mocha.Context) {
                    if (tc.callStatic) {
                        return await callStaticFunc(
                            tc,
                            staticCallPopulatedTransaction(approvalTxn, wallet),
                            approval
                        )(this)
                    } else {
                        return await executeTxnFunc(
                            tc,
                            wallet.sendTransaction(approvalTxn),
                            approval
                        )(this)
                    }
                });

                step(bridgeTxnTestTitle, async function(this: Mocha.Context) {
                    if (tc.callStatic) {
                        return await callStaticFunc(
                            tc,
                            staticCallPopulatedTransaction(bridgeTxn, wallet)
                        )(this)
                    } else {
                        return await executeTxnFunc(
                            tc,
                            wallet.sendTransaction(bridgeTxn)
                        )(this)
                    }
                });
            });

            (tc.callStatic ? describe.skip : describe)("- Magic Executors", function(this: Mocha.Suite) {
                const approval = true;

                step(approvalTxnTestTitle, async function(this: Mocha.Context) {
                    return await executeTxnFunc(
                        tc,
                        bridgeInstance.executeApproveTransaction({token: tc.args.tokenFrom}, wallet),
                        approval
                    )(this)
                });

                step(bridgeTxnTestTitle, async function (this: Mocha.Context) {
                    return await executeTxnFunc(
                        tc,
                        bridgeInstance.executeBridgeTokenTransaction(doBridgeArgs, wallet)
                    )(this)
                });
            })
        })
    })
})