import fs from "fs";
import path from "path";
import dotenv from "dotenv";
import "colors";

import { JsonFragment } from "@ethersproject/abi";
import { Wallet } from "@ethersproject/wallet";
import { Signer } from "@ethersproject/abstract-signer";
import { ContractFactory, Overrides } from "@ethersproject/contracts";

import { task, HardhatUserConfig, types, extendEnvironment } from "hardhat/config";
import { HardhatRuntimeEnvironment } from "hardhat/types";
import "@nomiclabs/hardhat-ethers";

import { Decimal } from "@sovryn-zero/lib-base";

import {
  deployAndSetupContracts,
  setSilent,
  OracleAddresses,
  MyntAddresses
} from "./utils/deploy";
import { _LiquityDeploymentJSON } from "./src/contracts";

import accounts from "./accounts.json";
import {
  BorrowerOperations,
  CommunityIssuance,
  ZEROToken,
  ZUSDToken,
  UpgradableProxy,
  Ownable
} from "./types";

dotenv.config();

const numAccounts = 100;

const useLiveVersionEnv = (process.env.USE_LIVE_VERSION ?? "false").toLowerCase();
const useLiveVersion = !["false", "no", "0"].includes(useLiveVersionEnv);

const contractsDir = path.join("..", "contracts");
const artifacts = path.join(contractsDir, "artifacts");
const cache = path.join(contractsDir, "cache");

const generateRandomAccounts = (numberOfAccounts: number) => {
  const accounts = new Array<string>(numberOfAccounts);

  for (let i = 0; i < numberOfAccounts; ++i) {
    accounts[i] = Wallet.createRandom().privateKey;
  }

  return accounts;
};

// const deployerAccount = process.env.DEPLOYER_PRIVATE_KEY || Wallet.createRandom().privateKey;
const deployerPrivateKeys: { [key: string]: string | undefined } = {
  dev: process.env.DEPLOYER_PK_TESTNET,
  rsktestnet: process.env.DEPLOYER_PK_TESTNET,
  rsksovryntestnet: process.env.DEPLOYER_PK_TESTNET,
  rskforkedtestnet: process.env.DEPLOYER_PK_TESTNET,
  rsksovrynmainnet: process.env.DEPLOYER_PK_MAINNET,
  rskforkedmainnet: process.env.DEPLOYER_PK_MAINNET
};

const devChainRichAccount = "0x4d5db4107d237df6a3d58ee5f70ae63d73d7658d4026f2eefd2f204c81682cb7";

const governanceAddresses = {
  mainnet: "",
  rsksovrynmainnet: "0x967c84b731679E36A344002b8E3CE50620A7F69f",
  dev: "0x0000000000000000000000000000000000000003"
};

const feeSharingCollectorAddresses = {
  mainnet: "",
  rsksovrynmainnet: "0x115cAF168c51eD15ec535727F64684D33B7b08D1",
  rsktestnet: "0xedD92fb7C556E4A4faf8c4f5A90f471aDCD018f4",
  dev: ""
};

const wrbtcAddresses = {
  mainnet: "",
  rsksovrynmainnet: "0x542fda317318ebf1d3deaf76e0b632741a7e677d",
  rsktestnet: "0x69FE5cEC81D5eF92600c1A0dB1F11986AB3758Ab",
  dev: ""
};

const marketMakerAddresses = {
  mainnet: "0x0000000000000000000000000000000000000001",
  rskforkedmainnet: "0x0000000000000000000000000000000000000001",
  rsktestnet: "0x0000000000000000000000000000000000000001",
  rskforkedtestnet: "0x0000000000000000000000000000000000000001",
  dev: "0x0000000000000000000000000000000000000003"
};

const presaleAddresses = {
  mainnet: "0x0000000000000000000000000000000000000001",
  rskforkedmainnet: "0x0000000000000000000000000000000000000001",
  rsktestnet: "0x0000000000000000000000000000000000000001",
  rskforkedtestnet: "0x0000000000000000000000000000000000000001",
  dev: ""
};

const zusdTokenAddresses = {
  rsktestnet: "0xe67cbA98C183A1693fC647d63AeeEC4053656dBB",
  dev: ""
};

const oracleAddresses: Record<string, OracleAddresses> = {
  mainnet: {
    mocOracleAddress: "",
    rskOracleAddress: ""
  },
  rsksovrynmainnet: {
    mocOracleAddress: "0x972a21C61B436354C0F35836195D7B67f54E482C",
    rskOracleAddress: "0x99eD262dbd8842442cd22d4c6885936DB38245E6"
  },
  rskforkedmainnet: {
    mocOracleAddress: "0x972a21C61B436354C0F35836195D7B67f54E482C",
    rskOracleAddress: "0x99eD262dbd8842442cd22d4c6885936DB38245E6"
  },
  rsktestnet: {
    mocOracleAddress: "0xb76c405Dfd042D88FD7b8dd2e5d66fe7974A1458",
    rskOracleAddress: "0xE00243Bc6912BF148302e8478996c98c22fE8739"
  },
  rskforkedtestnet: {
    mocOracleAddress: "0xb76c405Dfd042D88FD7b8dd2e5d66fe7974A1458",
    rskOracleAddress: "0xE00243Bc6912BF148302e8478996c98c22fE8739"
  },
  dev: {
    mocOracleAddress: "",
    rskOracleAddress: ""
  }
};

const myntAddresses: Record<string, MyntAddresses> = {
  rsksovrynmainnet: {
    massetManagerAddress: "",
    nueTokenAddress: ""
  },
  rskforkedmainnet: {
    massetManagerAddress: "",
    nueTokenAddress: ""
  },
  rsktestnet: {
    massetManagerAddress: "0xac2d05A148aB512EDEDc7280c00292ED33d31f1A",
    nueTokenAddress: "0x007b3AA69A846cB1f76b60b3088230A52D2A83AC"
  },
  rskforkedtestnet: {
    massetManagerAddress: "0xac2d05A148aB512EDEDc7280c00292ED33d31f1A",
    nueTokenAddress: "0x007b3AA69A846cB1f76b60b3088230A52D2A83AC"
  }
};

const hasOracles = (network: string): boolean => network in oracleAddresses;

const hasGovernance = (network: string): network is keyof typeof governanceAddresses =>
  network in governanceAddresses;

const hasFeeSharingCollector = (
  network: string
): network is keyof typeof feeSharingCollectorAddresses => network in feeSharingCollectorAddresses;

const hasWrbtc = (network: string): network is keyof typeof wrbtcAddresses =>
  network in wrbtcAddresses;

const hasPresale = (network: string): network is keyof typeof presaleAddresses =>
  network in presaleAddresses;

const hasMarketMaker = (network: string): network is keyof typeof marketMakerAddresses =>
  network in marketMakerAddresses;

const hasZusdToken = (network: string): network is keyof typeof zusdTokenAddresses =>
  network in zusdTokenAddresses;

const hasMyntAddresses = (network: string): boolean => network in myntAddresses;

const getDeployerAccount = (network: string) => {
  return deployerPrivateKeys[network];
};

const config: HardhatUserConfig = {
  networks: {
    hardhat: {
      accounts: accounts.slice(0, numAccounts),

      gas: 13e6, // tx gas limit
      blockGasLimit: 13e6,
      initialBaseFeePerGas: 0,

      // Let Ethers throw instead of Buidler EVM
      // This is closer to what will happen in production
      throwOnCallFailures: false,
      throwOnTransactionFailures: false
    },

    dev: {
      url: "http://localhost:4444",
      accounts: [
        getDeployerAccount("dev") || Wallet.createRandom().privateKey,
        devChainRichAccount,
        ...generateRandomAccounts(numAccounts - 2)
      ]
    },

    rskdev: {
      url: "http://127.0.0.1:8545/",
      accounts: [getDeployerAccount("dev") || Wallet.createRandom().privateKey],
      // regtest default prefunded account
      //from: "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826"
      from: "0xeb19817335e5565cf9c4a791d58c2bfa0ce032c7",
      chainId: 30
    },

    rsktestnet: {
      url: "https://public-node.testnet.rsk.co",
      accounts: [getDeployerAccount("rsktestnet") ?? Wallet.createRandom().privateKey],
      chainId: 31,
      gasMultiplier: 1.25
    },
    rsksovryntestnet: {
      url: "https://testnet.sovryn.app/rpc",
      accounts: [getDeployerAccount("rsktestnet") ?? Wallet.createRandom().privateKey],
      chainId: 31,
      gasMultiplier: 1.25
      //timeout: 20000, // increase if needed; 20000 is the default value
      //allowUnlimitedContractSize, //EIP170 contrtact size restriction temporal testnet workaround
    },
    rskforkedtestnet: {
      // run in CLI: npx hardhat node --fork https://testnet.sovryn.app/rpc --no-deploy
      url: "http://127.0.0.1:8545/",
      accounts: [getDeployerAccount("rsktestnet") ?? Wallet.createRandom().privateKey],
      // regtest default prefunded account
      //from: "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826"
      from: "0xeb19817335e5565cf9c4a791d58c2bfa0ce032c7",
      chainId: 31337,
      gasMultiplier: 1.25
    },
    rsksovrynmainnet: {
      url: "https://mainnet.sovryn.app/rpc",
      chainId: 30,
      accounts: [getDeployerAccount("rsksovrynmainnet") ?? Wallet.createRandom().privateKey]
      //timeout: 20000, // increase if needed; 20000 is the default value
    },
    rskforkedmainnet: {
      // run in CLI: npx hardhat node --fork https://mainnet-dev.sovryn.app/rpc --no-deploy --port 4444
      url: "http://localhost:4444/",
      accounts: [getDeployerAccount("rsksovrynmainnet") ?? Wallet.createRandom().privateKey],
      // regtest default prefunded account
      //from: "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826"
      from: "0xeb19817335e5565cf9c4a791d58c2bfa0ce032c7",
      chainId: 31337
    }
  },
  paths: {
    artifacts,
    cache
  },
  mocha: {
    timeout: 60000
  }
};

type DeployLiquityParams = {
  deployer: Signer;
  governanceAddress?: string;
  feeSharingCollectorAddress?: string;
  wrbtcAddress?: string;
  externalPriceFeeds?: OracleAddresses;
  presaleAddress?: string;
  marketMakerAddress?: string;
  zusdTokenAddress?: string;
  massetManagerAddress?: string;
  nueTokenAddress?: string;
  isMainnet?: boolean;
  notTestnet?: boolean;
  overrides?: Overrides;
};

declare module "hardhat/types/runtime" {
  interface HardhatRuntimeEnvironment {
    deployLiquity: (
      deployer: Signer,
      governanceAddress?: string,
      feeSharingCollectorAddress?: string,
      wrbtcAddress?: string,
      externalPriceFeeds?: OracleAddresses,
      presaleAddress?: string,
      marketMakerAddress?: string,
      zusdTokenAddress?: string,
      myntMassetManagerAddress?: string,
      myntNueTokenAddress?: string,
      isMainnet?: boolean,
      notTestnet?: boolean,
      overrides?: Overrides
    ) => Promise<_LiquityDeploymentJSON>;
  }
}

const getLiveArtifact = (name: string): { abi: JsonFragment[]; bytecode: string } =>
  require(`./live/${name}.json`);

const getDeploymentData = (network: string, channel: string): _LiquityDeploymentJSON => {
  const addresses = fs.readFileSync(path.join("deployments", channel, `${network}.json`));

  return JSON.parse(String(addresses));
};

const getContractFactory: (
  env: HardhatRuntimeEnvironment
) => (name: string, signer: Signer) => Promise<ContractFactory> = useLiveVersion
  ? env => (name, signer) => {
      const { abi, bytecode } = getLiveArtifact(name);
      return env.ethers.getContractFactory(abi, bytecode, signer);
    }
  : env => env.ethers.getContractFactory;

extendEnvironment(env => {
  env.deployLiquity = async (
    deployer,
    governanceAddress,
    feeSharingCollectorAddress,
    wrbtcAddress,
    externalPriceFeeds,
    presaleAddress,
    marketMakerAddress,
    zusdTokenAddress,
    myntMassetManagerAddress,
    myntNueTokenAddress,
    isMainnet?: boolean,
    notTestnet?: boolean,
    overrides?: Overrides
  ) => {
    const deployment = await deployAndSetupContracts(
      deployer,
      getContractFactory(env),
      externalPriceFeeds,
      env.network.name === "dev",
      governanceAddress,
      feeSharingCollectorAddress,
      wrbtcAddress,
      presaleAddress,
      marketMakerAddress,
      zusdTokenAddress,
      myntMassetManagerAddress,
      myntNueTokenAddress,
      isMainnet,
      notTestnet,
      overrides
    );

    return { ...deployment };
  };
});

type SetMassetManagerAddressParams = {
  address: string;
  nuetokenaddress: string;
  channel: string;
};

const defaultChannel = process.env.CHANNEL || "default";

task(
  "setMassetManagerAddress",
  "Sets address of massetManager contract in order to support NUE troves"
)
  .addParam("address", "address of deployed MassetManagerProxy contract")
  .addParam("nuetokenaddress", "address of NUE token")
  .addOptionalParam("channel", "Deployment channel to deploy into", defaultChannel, types.string)
  .setAction(async ({ address, channel, nuetokenaddress }: SetMassetManagerAddressParams, hre) => {
    const [deployer] = await hre.ethers.getSigners();
    const deployment = getDeploymentData(hre.network.name, channel);
    const { borrowerOperations: borrowerOperationsAddress } = deployment.addresses;

    const borrowerOperations = (await hre.ethers.getContractAt(
      "BorrowerOperations",
      borrowerOperationsAddress,
      deployer
    )) as unknown as BorrowerOperations;

    const currentMassetAddress = await borrowerOperations.massetManager();
    console.log("Current massetManager address: ", currentMassetAddress);

    const tx = await borrowerOperations.setMassetManagerAddress(address);
    await tx.wait();

    const newMassetAddress = await borrowerOperations.massetManager();
    console.log("New massetManager address: ", newMassetAddress);

    deployment.addresses.nueToken = nuetokenaddress;

    fs.writeFileSync(
      path.join("deployments", channel, `${hre.network.name}.json`),
      JSON.stringify(deployment, undefined, 2)
    );
  });

type FundCommunityIssuance = {
  channel: string;
  amount: string;
};

task(
  "fundCommunityIssuance",
  "Sends funds to the community issuance contract so users can get rewards"
)
  .addParam("amount", "Amount to send", undefined, types.string)
  .addOptionalParam("channel", "Deployment channel to deploy into", defaultChannel, types.string)
  .setAction(async ({ channel, amount }: FundCommunityIssuance, hre) => {
    const [deployer] = await hre.ethers.getSigners();
    const deployment = getDeploymentData(hre.network.name, channel);
    const { zeroToken: zeroTokenAddress, communityIssuance: communityIssuanceAddress } =
      deployment.addresses;

    const zeroToken = (await hre.ethers.getContractAt(
      "ZEROToken",
      zeroTokenAddress,
      deployer
    )) as unknown as ZEROToken;
    const communityIssuance = (await hre.ethers.getContractAt(
      "CommunityIssuance",
      communityIssuanceAddress,
      deployer
    )) as unknown as CommunityIssuance;

    const senderZeroBalance = await zeroToken.balanceOf(deployer.address);
    console.log(`Sender zero balance: ${senderZeroBalance}`);

    const communityIssuanceBalanceBefore = await zeroToken.balanceOf(communityIssuanceAddress);
    console.log(`Community issuance balance before: ${communityIssuanceBalanceBefore}`);

    console.log("Setting allowance");
    const allowance = await zeroToken.allowance(deployer.address, communityIssuanceAddress);
    console.log(`Current allowance: ${allowance}`);
    await (await zeroToken.increaseAllowance(communityIssuanceAddress, amount)).wait();
    console.log("Transferring zero");
    const communityIssuanceBalanceAfter = await zeroToken.balanceOf(communityIssuanceAddress);
    console.log(`Community issuance balance after: ${communityIssuanceBalanceAfter}`);
  });

type DeployParams = {
  channel: string;
  gasPrice?: number;
  useRealPriceFeed?: boolean;
  governanceAddress?: string;
  feeSharingCollectorAddress?: string;
  wrbtcAddress?: string;
  presaleAddress?: string;
  marketMakerAddress?: string;
  zusdTokenAddress?: string;
};

task("deploy", "Deploys the contracts to the network")
  .addOptionalParam("channel", "Deployment channel to deploy into", defaultChannel, types.string)
  .addOptionalParam("gasPrice", "Price to pay for 1 gas [Gwei]", undefined, types.float)
  .addOptionalParam(
    "useRealPriceFeed",
    "Deploy the production version of PriceFeed and connect it to MoC",
    undefined,
    types.boolean
  )
  .addOptionalParam(
    "governanceAddress",
    "Governance contract address to be the owner",
    undefined,
    types.string
  )
  .setAction(
    async (
      {
        channel,
        gasPrice,
        useRealPriceFeed,
        governanceAddress,
        feeSharingCollectorAddress,
        wrbtcAddress,
        presaleAddress,
        marketMakerAddress,
        zusdTokenAddress
      }: DeployParams,
      env
    ) => {
      const overrides = { gasPrice: gasPrice && Decimal.from(gasPrice).div(1000000000).hex };
      const [deployer] = await env.ethers.getSigners();

      const balBefore = await deployer.getBalance();

      console.log({
        balanceBefore: balBefore.toString()
      });

      const mainnets = ["mainnet", "rsksovrynmainnet", "rskmainnet", "rskforkedmainnet"];
      const testnets = ["rsksovryntestnet", "rsktestnet", "rskforkedtestnet"];

      const isMainnet: boolean = mainnets.indexOf(env.network.name) !== -1;
      useRealPriceFeed ??= isMainnet;

      const notTestnet: boolean = testnets.indexOf(env.network.name) == -1;

      if (useRealPriceFeed && !hasOracles(env.network.name)) {
        throw new Error(`PriceFeed not supported on ${env.network.name}`);
      }

      setSilent(false);
      governanceAddress ??= hasGovernance(env.network.name)
        ? governanceAddresses[env.network.name]
        : undefined;
      feeSharingCollectorAddress ??= hasFeeSharingCollector(env.network.name)
        ? feeSharingCollectorAddresses[env.network.name]
        : undefined;
      wrbtcAddress ??= hasWrbtc(env.network.name) ? wrbtcAddresses[env.network.name] : undefined;
      presaleAddress ??= hasPresale(env.network.name)
        ? presaleAddresses[env.network.name]
        : undefined;
      marketMakerAddress ??= hasMarketMaker(env.network.name)
        ? marketMakerAddresses[env.network.name]
        : undefined;
      zusdTokenAddress ??= hasZusdToken(env.network.name)
        ? zusdTokenAddresses[env.network.name]
        : undefined;
      const myntMassetManagerAddress =
        hasMyntAddresses(env.network.name) && myntAddresses[env.network.name]?.massetManagerAddress
          ? myntAddresses[env.network.name]?.massetManagerAddress
          : undefined;
      const myntNueTokenAddress =
        hasMyntAddresses(env.network.name) && myntAddresses[env.network.name]?.nueTokenAddress
          ? myntAddresses[env.network.name]?.nueTokenAddress
          : undefined;

      const deployment = await env.deployLiquity(
        deployer,
        governanceAddress,
        feeSharingCollectorAddress,
        wrbtcAddress,
        useRealPriceFeed ? oracleAddresses[env.network.name] : undefined,
        presaleAddress,
        marketMakerAddress,
        zusdTokenAddress,
        myntMassetManagerAddress,
        myntNueTokenAddress,
        isMainnet,
        notTestnet,
        overrides
      );

      fs.mkdirSync(path.join("deployments", channel), { recursive: true });

      fs.writeFileSync(
        path.join("deployments", channel, `${env.network.name}.json`),
        JSON.stringify(deployment, undefined, 2)
      );

      console.log();
      console.log(deployment);
      console.log();
      console.log({ balanceSpent: balBefore.sub(await deployer.getBalance()).toString() });
    }
  );

type DeployZUSDToken = {
  doInitialize: boolean;
  channel: string;
};

task("deployNewZusdToken", "Deploys new ZUSD token and links it to previous deployment")
  .addFlag(
    "doInitialize",
    "Will use ZUSDTokenTestnet contract to allow reinitialization which otherwise is invalid"
  )
  .addOptionalParam("channel", "Deployment channel to deploy into", defaultChannel, types.string)
  .setAction(async ({ doInitialize, channel }: DeployZUSDToken, hre) => {
    const [deployer] = await hre.ethers.getSigners();
    const deployment = getDeploymentData(hre.network.name, channel);
    const {
      zusdToken: zusdTokenAddress,
      troveManager: troveManagerAddress,
      stabilityPool: stabilityPoolAddress,
      borrowerOperations: borrowerOperationsAddress
    } = deployment.addresses;

    console.log("Deploying new ZUSD token logic for testnet");
    // NOTE this script should only be executed on testnet
    const tokenContractName = doInitialize ? "ZUSDTokenTestnet" : "ZUSDToken";
    const zusdTokenFactory = await hre.ethers.getContractFactory(tokenContractName);
    const zusdTokenContract = await (await zusdTokenFactory.deploy()).deployed();

    const zusdTokenProxy = (await hre.ethers.getContractAt(
      "UpgradableProxy",
      zusdTokenAddress,
      deployer
    )) as unknown as UpgradableProxy;

    //set new implementation
    const oldZUSDAddress = await zusdTokenProxy.getImplementation();
    await zusdTokenProxy.setImplementation(zusdTokenContract.address);
    console.log("Initializing new ZUSD token with the correct dependencies");

    const zusdToken = (await hre.ethers.getContractAt(
      tokenContractName,
      zusdTokenAddress,
      deployer
    )) as unknown as ZUSDToken;
    //call initialize on the new zusdToken by calling proxy - not possible

    if (doInitialize)
      await zusdToken.initialize(
        troveManagerAddress,
        stabilityPoolAddress,
        borrowerOperationsAddress
      );

    console.log(
      "Changing old ZUSD address " + oldZUSDAddress + " to " + zusdTokenContract.address
    );
    const newZUSDAddress = await zusdTokenProxy.getImplementation();
    console.log("Implementation address changed to " + newZUSDAddress);
  });

task("getDeployedContractsOwners", "Prints the deployed contracts owner address")
  .addOptionalParam("channel", "Deployment channel to deploy into", defaultChannel, types.string)
  .setAction(async ({ channel }, hre) => {
    const liveNets = [
      "mainnet",
      "rsksovrynmainnet",
      "rskmainnet",
      "testnet",
      "rsktestnet",
      "rsksovryntestnet"
    ];
    if (liveNets.indexOf(hre.network.name) === -1) {
      console.log("===========================================================");
      console.log("ALERT! Make sure the script is running on a proper network!");
      console.log("===========================================================");
    }
    const deployment = getDeploymentData(hre.network.name, channel);
    const obj = Object.entries(deployment.addresses);
    for await (const item of obj) {
      try {
        const owned = (await hre.ethers.getContractAt("Ownable", item[1])) as unknown as Ownable;
        console.log(`${await owned.getOwner()} is owner of ${item[0]} (${item[1]})`);
      } catch (e) {
        console.log(`${item[0]} (${item[1]}) is NOT Ownable`);
      }
    }
  });

// hh transferOwnership --new-owner 0xcf311e7375083b9513566a47b9f3e93f1fcdcfbf --network rsksovryntestnet
task("transferOwnership", "Transfers contracts ownership from EOA")
  .addParam("newOwner", "New owner of the contracts", undefined, types.string, false)
  .addOptionalParam("channel", "Deployment channel to deploy into", defaultChannel, types.string)
  .setAction(async ({ newOwner, channel }, hre) => {
    const liveNets = [
      "mainnet",
      "rsksovrynmainnet",
      "rskmainnet",
      "testnet",
      "rsktestnet",
      "rsksovryntestnet"
    ];
    const [deployer] = await hre.ethers.getSigners();
    if (liveNets.indexOf(hre.network.name) === -1) {
      console.log("===========================================================");
      console.log("ALERT! Make sure the script is running on a proper network:", liveNets);
      console.log("===========================================================");
    }
    const deployment = getDeploymentData(hre.network.name, channel);
    const obj = Object.entries(deployment.addresses);
    for await (const item of obj) {
      try {
        const owned = (await hre.ethers.getContractAt(
          "Ownable",
          item[1],
          deployer
        )) as unknown as Ownable;
        const owner = await owned.getOwner();
        if (owner == deployer.address) {
          await (await owned.setOwner(newOwner)).wait();
          const _newOwner = await owned.getOwner();
          console.log(`${_newOwner} is the new owner of ${item[0]} (${item[1]})`);
        } else {
          console.log(
            `Deployer ${deployer.address} must be the current owner ${owner} of ${item[0]} (${item[1]})`
          );
        }
      } catch (e) {
        console.log(`${item[0]} (${item[1]}) is NOT Ownable`);
      }
    }
  });

task("getCurrentZUSDImplementation", "Logs to console current ZUSD implementation address")
  .addOptionalParam("channel", "Deployment channel to deploy into", defaultChannel, types.string)
  .setAction(async ({ channel }: DeployZUSDToken, hre) => {
    const [deployer] = await hre.ethers.getSigners();
    const deployment = getDeploymentData(hre.network.name, channel);
    const { zusdToken: zusdTokenAddress, zeroToken: zeroTokenAddress } = deployment.addresses;

    const zusdTokenProxy = (await hre.ethers.getContractAt(
      "UpgradableProxy",
      zusdTokenAddress,
      deployer
    )) as unknown as UpgradableProxy;
    console.log("ZUSD Proxy adddress: " + zusdTokenProxy.address);

    const zusdImplementationAddress = await zusdTokenProxy.getImplementation();
    console.log("ZUSD implelentation address: " + zusdImplementationAddress);

    const zusdToken = (await hre.ethers.getContractAt(
      "ZUSDToken",
      zusdTokenAddress,
      deployer
    )) as unknown as ZUSDToken;

    const zeroToken = (await hre.ethers.getContractAt(
      "ZEROToken",
      zeroTokenAddress,
      deployer
    )) as unknown as ZEROToken;

    /*await zeroToken.mint("0x0", 100);
    await zeroToken.mint(deployer.address, 100);
    console.log("Try mint ZERO");*/
  });

export default config;
