From 12dba4d8c68d9b266c80914808a09b3c45199d5e Mon Sep 17 00:00:00 2001 From: Atris Date: Wed, 22 Nov 2023 10:26:00 +0100 Subject: [PATCH] chore: simplify payout tokens handling --- .../src/context/application/usePayout.ts | 4 +- .../src/context/round/FundContractContext.tsx | 11 +- .../src/context/round/UpdateRoundContext.tsx | 2 +- .../src/features/api/application.ts | 5 +- .../src/features/api/payoutTokens.ts | 299 ++++++++++++++++ .../round-manager/src/features/api/round.ts | 3 +- .../round-manager/src/features/api/utils.ts | 330 +----------------- .../round/ApplicationDirectPayout.tsx | 2 +- .../src/features/round/FundContract.tsx | 3 +- .../features/round/QuadraticFundingForm.tsx | 12 +- .../src/features/round/ReclaimFunds.tsx | 2 +- .../src/features/round/ViewFundGrantees.tsx | 9 +- .../ViewRoundResults/ViewRoundResults.tsx | 3 +- .../src/features/round/ViewRoundSettings.tsx | 3 +- .../src/features/round/ViewRoundStats.tsx | 8 +- .../__tests__/QuadraticFundingForm.test.tsx | 2 +- 16 files changed, 339 insertions(+), 359 deletions(-) create mode 100644 packages/round-manager/src/features/api/payoutTokens.ts diff --git a/packages/round-manager/src/context/application/usePayout.ts b/packages/round-manager/src/context/application/usePayout.ts index cb62cfba99..5496a9da1f 100644 --- a/packages/round-manager/src/context/application/usePayout.ts +++ b/packages/round-manager/src/context/application/usePayout.ts @@ -1,12 +1,12 @@ import { Dispatch, SetStateAction, useState } from "react"; import { ProgressStatus, ProgressStep } from "../../features/api/types"; -import { PayoutToken } from "../../features/api/utils"; import { DirectPayoutStrategy__factory, Erc20__factory, } from "../../types/generated/typechain"; import { BigNumber, ethers } from "ethers"; import { waitForSubgraphSyncTo } from "../../features/api/subgraph"; +import { PayoutTokens } from "../../features/api/payoutTokens"; export function usePayout() { const [contractApproveSpendStatus, setContractApproveSpendStatus] = @@ -71,7 +71,7 @@ export function usePayout() { type Args = { address: string; signer: ethers.Signer; - token: PayoutToken; + token: PayoutTokens; projectId: string; applicationIndex: number; payoutStrategyAddress: string; diff --git a/packages/round-manager/src/context/round/FundContractContext.tsx b/packages/round-manager/src/context/round/FundContractContext.tsx index 5e9b39dfb7..44cb0a2b5b 100644 --- a/packages/round-manager/src/context/round/FundContractContext.tsx +++ b/packages/round-manager/src/context/round/FundContractContext.tsx @@ -12,7 +12,8 @@ import { fundRoundContract } from "../../features/api/application"; import { waitForSubgraphSyncTo } from "../../features/api/subgraph"; import { ProgressStatus } from "../../features/api/types"; -import { PayoutToken } from "../../features/api/utils"; + +import { PayoutTokens } from "../../features/api/payoutTokens"; export interface FundContractState { tokenApprovalStatus: ProgressStatus; @@ -30,14 +31,14 @@ export interface FundContractState { export type FundContractParams = { roundId: string; fundAmount: number; - payoutToken: PayoutToken; + payoutToken: PayoutTokens; }; interface SubmitFundParams { signer: Signer; context: FundContractState; roundId: string; - payoutToken: PayoutToken; + payoutToken: PayoutTokens; fundAmount: number; } @@ -179,7 +180,7 @@ async function _fundContract({ async function approveTokenForFunding( signerOrProvider: Signer, roundId: string, - token: PayoutToken, + token: PayoutTokens, amount: number, context: FundContractState ): Promise { @@ -203,7 +204,7 @@ async function approveTokenForFunding( async function fund( signerOrProvider: Signer, roundId: string, - token: PayoutToken, + token: PayoutTokens, fundAmount: number, context: FundContractState ): Promise { diff --git a/packages/round-manager/src/context/round/UpdateRoundContext.tsx b/packages/round-manager/src/context/round/UpdateRoundContext.tsx index 6cd59cc944..e078ddcbde 100644 --- a/packages/round-manager/src/context/round/UpdateRoundContext.tsx +++ b/packages/round-manager/src/context/round/UpdateRoundContext.tsx @@ -5,9 +5,9 @@ import { saveToIPFS } from "../../features/api/ipfs"; import { TransactionBuilder, UpdateAction } from "../../features/api/round"; import { waitForSubgraphSyncTo } from "../../features/api/subgraph"; import { EditedGroups, ProgressStatus, Round } from "../../features/api/types"; -import { getPayoutTokenOptions } from "../../features/api/utils"; import { useWallet } from "../../features/common/Auth"; import subSeconds from "date-fns/subSeconds"; +import { getPayoutTokenOptions } from "../../features/api/payoutTokens"; type SetStatusFn = React.Dispatch>; diff --git a/packages/round-manager/src/features/api/application.ts b/packages/round-manager/src/features/api/application.ts index ed11bec706..27833a8699 100644 --- a/packages/round-manager/src/features/api/application.ts +++ b/packages/round-manager/src/features/api/application.ts @@ -1,4 +1,4 @@ -import { fetchFromIPFS, PayoutToken, pinToIPFS } from "./utils"; +import { fetchFromIPFS, pinToIPFS } from "./utils"; import { AppStatus, GrantApplication, @@ -19,6 +19,7 @@ import { Signer } from "@ethersproject/abstract-signer"; import { Web3Provider } from "@ethersproject/providers"; import { graphql_fetch } from "common"; import { DirectPayoutStrategy__factory } from "../../types/generated/typechain"; +import { PayoutTokens } from "./payoutTokens"; // import { verifyApplicationMetadata } from "common/src/verification"; // import { fetchProjectOwners } from "common/src/registry"; @@ -484,7 +485,7 @@ export const updateApplicationList = async ( export const fundRoundContract = async ( roundId: string, signer: Signer, - payoutToken: PayoutToken, + payoutToken: PayoutTokens, amount: BigNumber ): Promise<{ txBlockNumber: number; txHash: string }> => { // checksum conversion diff --git a/packages/round-manager/src/features/api/payoutTokens.ts b/packages/round-manager/src/features/api/payoutTokens.ts new file mode 100644 index 0000000000..86484c9937 --- /dev/null +++ b/packages/round-manager/src/features/api/payoutTokens.ts @@ -0,0 +1,299 @@ +import { ChainId, RedstoneTokenIds } from "common"; +import { ethers } from "ethers"; + +export type PayoutTokens = { + name: string; + chainId: number; + address: string; + logo?: string; + default?: boolean; // TODO: this is only used to provide the initial placeholder item, look for better solution + redstoneTokenId?: string; + decimal: number; +}; +export const TokenNamesAndLogos = { + FTM: "/logos/fantom-logo.svg", + BUSD: "/logos/busd-logo.svg", + DAI: "/logos/dai-logo.svg", + USDC: "./logos/usdc-logo.svg", + ETH: "/logos/ethereum-eth-logo.svg", + OP: "/logos/optimism-logo.svg", + ARB: "/logos/arb-logo.svg", + GCV: "/logos/gcv.svg", + GTC: "/logos/gtc.svg", + AVAX: "/logos/avax-logo.svg", + MATIC: "/logos/pol-logo.svg", + CVP: "/logos/power-pool.png", // PowerPool +} as const; +const MAINNET_TOKENS: PayoutTokens[] = [ + { + name: "DAI", + chainId: ChainId.MAINNET, + address: "0x6B175474E89094C44Da98b954EedeAC495271d0F", + decimal: 18, + logo: TokenNamesAndLogos["DAI"], + redstoneTokenId: RedstoneTokenIds["DAI"], + }, + { + name: "ETH", + chainId: ChainId.MAINNET, + address: ethers.constants.AddressZero, + decimal: 18, + logo: TokenNamesAndLogos["ETH"], + redstoneTokenId: RedstoneTokenIds["ETH"], + }, + { + name: "CVP", + chainId: ChainId.MAINNET, + address: "0x38e4adB44ef08F22F5B5b76A8f0c2d0dCbE7DcA1", + decimal: 18, + logo: TokenNamesAndLogos["CVP"], + redstoneTokenId: RedstoneTokenIds["CVP"], + }, +]; +const OPTIMISM_MAINNET_TOKENS: PayoutTokens[] = [ + { + name: "DAI", + chainId: ChainId.OPTIMISM_MAINNET_CHAIN_ID, + address: "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1", + decimal: 18, + logo: TokenNamesAndLogos["DAI"], + redstoneTokenId: RedstoneTokenIds["DAI"], + }, + { + name: "ETH", + chainId: ChainId.OPTIMISM_MAINNET_CHAIN_ID, + address: ethers.constants.AddressZero, + decimal: 18, + logo: TokenNamesAndLogos["ETH"], + redstoneTokenId: RedstoneTokenIds["ETH"], + }, +]; +const FANTOM_MAINNET_TOKENS: PayoutTokens[] = [ + { + name: "WFTM", + chainId: ChainId.FANTOM_MAINNET_CHAIN_ID, + address: "0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83", + decimal: 18, + logo: TokenNamesAndLogos["FTM"], + redstoneTokenId: RedstoneTokenIds["FTM"], + }, + { + name: "FTM", + chainId: ChainId.FANTOM_MAINNET_CHAIN_ID, + address: ethers.constants.AddressZero, + decimal: 18, + logo: TokenNamesAndLogos["FTM"], + redstoneTokenId: RedstoneTokenIds["FTM"], + }, + { + name: "BUSD", + chainId: ChainId.FANTOM_MAINNET_CHAIN_ID, + address: "0xC931f61B1534EB21D8c11B24f3f5Ab2471d4aB50", + decimal: 18, + logo: TokenNamesAndLogos["BUSD"], + redstoneTokenId: RedstoneTokenIds["BUSD"], + }, + { + name: "DAI", + chainId: ChainId.FANTOM_MAINNET_CHAIN_ID, + address: "0x8d11ec38a3eb5e956b052f67da8bdc9bef8abf3e", + decimal: 18, + logo: TokenNamesAndLogos["DAI"], + redstoneTokenId: RedstoneTokenIds["DAI"], + }, + { + name: "GcV", + chainId: ChainId.FANTOM_MAINNET_CHAIN_ID, + address: "0x83791638da5EB2fAa432aff1c65fbA47c5D29510", + decimal: 18, + logo: TokenNamesAndLogos["GCV"], + redstoneTokenId: RedstoneTokenIds["DAI"], // We use DAI for the price + }, +]; +const FANTOM_TESTNET_TOKENS: PayoutTokens[] = [ + { + name: "DAI", + chainId: ChainId.FANTOM_TESTNET_CHAIN_ID, + address: "0xEdE59D58d9B8061Ff7D22E629AB2afa01af496f4", + decimal: 18, + logo: TokenNamesAndLogos["DAI"], + redstoneTokenId: RedstoneTokenIds["DAI"], + }, + { + name: "FTM", + chainId: ChainId.FANTOM_TESTNET_CHAIN_ID, + address: ethers.constants.AddressZero, + decimal: 18, + logo: TokenNamesAndLogos["FTM"], + redstoneTokenId: RedstoneTokenIds["FTM"], + }, +]; +const PGN_TESTNET_TOKENS: PayoutTokens[] = [ + { + name: "TEST", + chainId: ChainId.PGN_TESTNET, + address: "0x5FbDB2315678afecb367f032d93F642f64180aa3", + logo: TokenNamesAndLogos["DAI"], + decimal: 18, + }, + { + name: "ETH", + chainId: ChainId.PGN_TESTNET, + address: ethers.constants.AddressZero, + logo: TokenNamesAndLogos["ETH"], + decimal: 18, + }, +]; +const PGN_MAINNET_TOKENS: PayoutTokens[] = [ + { + name: "ETH", + chainId: ChainId.PGN, + address: ethers.constants.AddressZero, + decimal: 18, + logo: TokenNamesAndLogos["ETH"], + redstoneTokenId: RedstoneTokenIds["ETH"], + }, + { + name: "GTC", + chainId: ChainId.PGN, + address: "0x7c6b91D9Be155A6Db01f749217d76fF02A7227F2", + decimal: 18, + logo: TokenNamesAndLogos["GTC"], + redstoneTokenId: RedstoneTokenIds["GTC"], + }, + { + name: "DAI", + chainId: ChainId.PGN, + address: "0x6C121674ba6736644A7e73A8741407fE8a5eE5BA", + decimal: 18, + logo: TokenNamesAndLogos["DAI"], + redstoneTokenId: RedstoneTokenIds["DAI"], + }, +]; +const ARBITRUM_GOERLI_TOKENS: PayoutTokens[] = [ + { + name: "ETH", + chainId: ChainId.ARBITRUM_GOERLI, + address: ethers.constants.AddressZero, + decimal: 18, + logo: TokenNamesAndLogos["ETH"], + redstoneTokenId: RedstoneTokenIds["ETH"], + }, +]; +const ARBITRUM_TOKENS: PayoutTokens[] = [ + { + name: "ETH", + chainId: ChainId.ARBITRUM, + address: ethers.constants.AddressZero, + decimal: 18, + logo: TokenNamesAndLogos["ETH"], + redstoneTokenId: RedstoneTokenIds["ETH"], + }, + { + name: "USDC", + chainId: ChainId.ARBITRUM, + address: "0xaf88d065e77c8cc2239327c5edb3a432268e5831", + decimal: 6, + logo: TokenNamesAndLogos["USDC"], + redstoneTokenId: RedstoneTokenIds["USDC"], + }, + { + name: "ARB", + chainId: ChainId.ARBITRUM, + address: "0x912CE59144191C1204E64559FE8253a0e49E6548", + decimal: 18, + logo: TokenNamesAndLogos["ARB"], + redstoneTokenId: RedstoneTokenIds["ARB"], + }, +]; +const AVALANCHE_TOKENS: PayoutTokens[] = [ + { + name: "AVAX", + chainId: ChainId.AVALANCHE, + address: ethers.constants.AddressZero, + decimal: 18, + logo: TokenNamesAndLogos["AVAX"], + redstoneTokenId: RedstoneTokenIds["AVAX"], + }, + { + name: "USDC", + chainId: ChainId.AVALANCHE, + address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", + decimal: 6, + logo: TokenNamesAndLogos["USDC"], + redstoneTokenId: RedstoneTokenIds["USDC"], + }, +]; +const FUJI_TOKENS: PayoutTokens[] = [ + { + name: "AVAX", + chainId: ChainId.FUJI, + address: ethers.constants.AddressZero, + decimal: 18, + logo: TokenNamesAndLogos["AVAX"], + redstoneTokenId: RedstoneTokenIds["AVAX"], + }, + { + name: "USDC", + chainId: ChainId.FUJI, + address: "0x5425890298aed601595a70ab815c96711a31bc65", + decimal: 6, + logo: TokenNamesAndLogos["USDC"], + redstoneTokenId: RedstoneTokenIds["USDC"], + }, +]; +const POLYGON_TOKENS: PayoutTokens[] = [ + { + name: "MATIC", + chainId: ChainId.POLYGON, + address: ethers.constants.AddressZero, + decimal: 18, + logo: TokenNamesAndLogos["MATIC"], + redstoneTokenId: RedstoneTokenIds["MATIC"], + }, + { + name: "USDC", + chainId: ChainId.POLYGON, + address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", + decimal: 6, + logo: TokenNamesAndLogos["USDC"], + redstoneTokenId: RedstoneTokenIds["USDC"], + }, +]; +const POLYGON_MUMBAI_TOKENS: PayoutTokens[] = [ + { + name: "MATIC", + chainId: ChainId.POLYGON_MUMBAI, + address: ethers.constants.AddressZero, + decimal: 18, + logo: TokenNamesAndLogos["MATIC"], + redstoneTokenId: RedstoneTokenIds["MATIC"], + }, + { + name: "USDC", + chainId: ChainId.POLYGON_MUMBAI, + address: "0x9999f7Fea5938fD3b1E26A12c3f2fb024e194f97", + decimal: 6, + logo: TokenNamesAndLogos["USDC"], + redstoneTokenId: RedstoneTokenIds["USDC"], + }, +]; +export const payoutTokens = [ + ...MAINNET_TOKENS, + ...OPTIMISM_MAINNET_TOKENS, + ...FANTOM_MAINNET_TOKENS, + ...FANTOM_TESTNET_TOKENS, + ...PGN_TESTNET_TOKENS, + ...PGN_MAINNET_TOKENS, + ...ARBITRUM_TOKENS, + ...ARBITRUM_GOERLI_TOKENS, + ...AVALANCHE_TOKENS, + ...FUJI_TOKENS, + ...POLYGON_TOKENS, + ...POLYGON_MUMBAI_TOKENS, +]; + +export const getPayoutTokenOptions = (chainId: ChainId): PayoutTokens[] => { + const tokens = payoutTokens.filter((token) => token.chainId === chainId); + return tokens.length > 0 ? tokens : MAINNET_TOKENS; +}; diff --git a/packages/round-manager/src/features/api/round.ts b/packages/round-manager/src/features/api/round.ts index 83a3f165c5..c551c4772f 100644 --- a/packages/round-manager/src/features/api/round.ts +++ b/packages/round-manager/src/features/api/round.ts @@ -15,8 +15,9 @@ import { MetadataPointer, Round, } from "./types"; -import { fetchFromIPFS, payoutTokens } from "./utils"; +import { fetchFromIPFS } from "./utils"; import { maxDateForUint256 } from "../../constants"; +import { payoutTokens } from "./payoutTokens"; export enum UpdateAction { UPDATE_APPLICATION_META_PTR = "updateApplicationMetaPtr", diff --git a/packages/round-manager/src/features/api/utils.ts b/packages/round-manager/src/features/api/utils.ts index 8defd8f06b..cb7780716a 100644 --- a/packages/round-manager/src/features/api/utils.ts +++ b/packages/round-manager/src/features/api/utils.ts @@ -2,12 +2,12 @@ import { StandardMerkleTree } from "@openzeppelin/merkle-tree"; import { BigNumber, ethers } from "ethers"; import { ApplicationMetadata, - IPFSObject, InputType, + IPFSObject, MatchingStatsData, Program, } from "./types"; -import { ChainId, RedstoneTokenIds } from "common"; +import { ChainId } from "common"; // NB: number keys are coerced into strings for JS object keys export const CHAINS: Record = { @@ -83,338 +83,12 @@ export const CHAINS: Record = { }, }; -export type PayoutToken = { - name: string; - chainId: number; - address: string; - logo?: string; - default?: boolean; // TODO: this is only used to provide the initial placeholder item, look for better solution - redstoneTokenId?: string; - decimal: number; -}; - export type SupportType = { name: string; regex: string; default: boolean; }; -export const TokenNamesAndLogos = { - FTM: "/logos/fantom-logo.svg", - BUSD: "/logos/busd-logo.svg", - DAI: "/logos/dai-logo.svg", - USDC: "./logos/usdc-logo.svg", - ETH: "/logos/ethereum-eth-logo.svg", - OP: "/logos/optimism-logo.svg", - ARB: "/logos/arb-logo.svg", - GCV: "/logos/gcv.svg", - GTC: "/logos/gtc.svg", - AVAX: "/logos/avax-logo.svg", - MATIC: "/logos/pol-logo.svg", - CVP: "/logos/power-pool.png", // PowerPool -} as const; - -const MAINNET_TOKENS: PayoutToken[] = [ - { - name: "DAI", - chainId: ChainId.MAINNET, - address: "0x6B175474E89094C44Da98b954EedeAC495271d0F", - decimal: 18, - logo: TokenNamesAndLogos["DAI"], - redstoneTokenId: RedstoneTokenIds["DAI"], - }, - { - name: "ETH", - chainId: ChainId.MAINNET, - address: ethers.constants.AddressZero, - decimal: 18, - logo: TokenNamesAndLogos["ETH"], - redstoneTokenId: RedstoneTokenIds["ETH"], - }, - { - name: "CVP", - chainId: ChainId.MAINNET, - address: "0x38e4adB44ef08F22F5B5b76A8f0c2d0dCbE7DcA1", - decimal: 18, - logo: TokenNamesAndLogos["CVP"], - redstoneTokenId: RedstoneTokenIds["CVP"], - }, -]; - -const OPTIMISM_MAINNET_TOKENS: PayoutToken[] = [ - { - name: "DAI", - chainId: ChainId.OPTIMISM_MAINNET_CHAIN_ID, - address: "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1", - decimal: 18, - logo: TokenNamesAndLogos["DAI"], - redstoneTokenId: RedstoneTokenIds["DAI"], - }, - { - name: "ETH", - chainId: ChainId.OPTIMISM_MAINNET_CHAIN_ID, - address: ethers.constants.AddressZero, - decimal: 18, - logo: TokenNamesAndLogos["ETH"], - redstoneTokenId: RedstoneTokenIds["ETH"], - }, -]; - -const FANTOM_MAINNET_TOKENS: PayoutToken[] = [ - { - name: "WFTM", - chainId: ChainId.FANTOM_MAINNET_CHAIN_ID, - address: "0x21be370D5312f44cB42ce377BC9b8a0cEF1A4C83", - decimal: 18, - logo: TokenNamesAndLogos["FTM"], - redstoneTokenId: RedstoneTokenIds["FTM"], - }, - { - name: "FTM", - chainId: ChainId.FANTOM_MAINNET_CHAIN_ID, - address: ethers.constants.AddressZero, - decimal: 18, - logo: TokenNamesAndLogos["FTM"], - redstoneTokenId: RedstoneTokenIds["FTM"], - }, - { - name: "BUSD", - chainId: ChainId.FANTOM_MAINNET_CHAIN_ID, - address: "0xC931f61B1534EB21D8c11B24f3f5Ab2471d4aB50", - decimal: 18, - logo: TokenNamesAndLogos["BUSD"], - redstoneTokenId: RedstoneTokenIds["BUSD"], - }, - { - name: "DAI", - chainId: ChainId.FANTOM_MAINNET_CHAIN_ID, - address: "0x8d11ec38a3eb5e956b052f67da8bdc9bef8abf3e", - decimal: 18, - logo: TokenNamesAndLogos["DAI"], - redstoneTokenId: RedstoneTokenIds["DAI"], - }, - { - name: "GcV", - chainId: ChainId.FANTOM_MAINNET_CHAIN_ID, - address: "0x83791638da5EB2fAa432aff1c65fbA47c5D29510", - decimal: 18, - logo: TokenNamesAndLogos["GCV"], - redstoneTokenId: RedstoneTokenIds["DAI"], // We use DAI for the price - }, -]; - -const FANTOM_TESTNET_TOKENS: PayoutToken[] = [ - { - name: "DAI", - chainId: ChainId.FANTOM_TESTNET_CHAIN_ID, - address: "0xEdE59D58d9B8061Ff7D22E629AB2afa01af496f4", - decimal: 18, - logo: TokenNamesAndLogos["DAI"], - redstoneTokenId: RedstoneTokenIds["DAI"], - }, - { - name: "FTM", - chainId: ChainId.FANTOM_TESTNET_CHAIN_ID, - address: ethers.constants.AddressZero, - decimal: 18, - logo: TokenNamesAndLogos["FTM"], - redstoneTokenId: RedstoneTokenIds["FTM"], - }, -]; - -const PGN_TESTNET_TOKENS: PayoutToken[] = [ - { - name: "TEST", - chainId: ChainId.PGN_TESTNET, - address: "0x5FbDB2315678afecb367f032d93F642f64180aa3", - logo: TokenNamesAndLogos["DAI"], - decimal: 18, - }, - { - name: "ETH", - chainId: ChainId.PGN_TESTNET, - address: ethers.constants.AddressZero, - logo: TokenNamesAndLogos["ETH"], - decimal: 18, - }, -]; - -const PGN_MAINNET_TOKENS: PayoutToken[] = [ - { - name: "ETH", - chainId: ChainId.PGN, - address: ethers.constants.AddressZero, - decimal: 18, - logo: TokenNamesAndLogos["ETH"], - redstoneTokenId: RedstoneTokenIds["ETH"], - }, - { - name: "GTC", - chainId: ChainId.PGN, - address: "0x7c6b91D9Be155A6Db01f749217d76fF02A7227F2", - decimal: 18, - logo: TokenNamesAndLogos["GTC"], - redstoneTokenId: RedstoneTokenIds["GTC"], - }, - { - name: "DAI", - chainId: ChainId.PGN, - address: "0x6C121674ba6736644A7e73A8741407fE8a5eE5BA", - decimal: 18, - logo: TokenNamesAndLogos["DAI"], - redstoneTokenId: RedstoneTokenIds["DAI"], - }, -]; - -const ARBITRUM_GOERLI_TOKENS: PayoutToken[] = [ - { - name: "ETH", - chainId: ChainId.ARBITRUM_GOERLI, - address: ethers.constants.AddressZero, - decimal: 18, - logo: TokenNamesAndLogos["ETH"], - redstoneTokenId: RedstoneTokenIds["ETH"], - }, -]; - -const ARBITRUM_TOKENS: PayoutToken[] = [ - { - name: "ETH", - chainId: ChainId.ARBITRUM, - address: ethers.constants.AddressZero, - decimal: 18, - logo: TokenNamesAndLogos["ETH"], - redstoneTokenId: RedstoneTokenIds["ETH"], - }, - { - name: "USDC", - chainId: ChainId.ARBITRUM, - address: "0xaf88d065e77c8cc2239327c5edb3a432268e5831", - decimal: 6, - logo: TokenNamesAndLogos["USDC"], - redstoneTokenId: RedstoneTokenIds["USDC"], - }, - { - name: "ARB", - chainId: ChainId.ARBITRUM, - address: "0x912CE59144191C1204E64559FE8253a0e49E6548", - decimal: 18, - logo: TokenNamesAndLogos["ARB"], - redstoneTokenId: RedstoneTokenIds["ARB"], - }, -]; - -const AVALANCHE_TOKENS: PayoutToken[] = [ - { - name: "AVAX", - chainId: ChainId.AVALANCHE, - address: ethers.constants.AddressZero, - decimal: 18, - logo: TokenNamesAndLogos["AVAX"], - redstoneTokenId: RedstoneTokenIds["AVAX"], - }, - { - name: "USDC", - chainId: ChainId.AVALANCHE, - address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E", - decimal: 6, - logo: TokenNamesAndLogos["USDC"], - redstoneTokenId: RedstoneTokenIds["USDC"], - }, -]; - -const FUJI_TOKENS: PayoutToken[] = [ - { - name: "AVAX", - chainId: ChainId.FUJI, - address: ethers.constants.AddressZero, - decimal: 18, - logo: TokenNamesAndLogos["AVAX"], - redstoneTokenId: RedstoneTokenIds["AVAX"], - }, - { - name: "USDC", - chainId: ChainId.FUJI, - address: "0x5425890298aed601595a70ab815c96711a31bc65", - decimal: 6, - logo: TokenNamesAndLogos["USDC"], - redstoneTokenId: RedstoneTokenIds["USDC"], - }, -]; - -const POLYGON_TOKENS: PayoutToken[] = [ - { - name: "MATIC", - chainId: ChainId.POLYGON, - address: ethers.constants.AddressZero, - decimal: 18, - logo: TokenNamesAndLogos["MATIC"], - redstoneTokenId: RedstoneTokenIds["MATIC"], - }, - { - name: "USDC", - chainId: ChainId.POLYGON, - address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", - decimal: 6, - logo: TokenNamesAndLogos["USDC"], - redstoneTokenId: RedstoneTokenIds["USDC"], - }, -]; - -const POLYGON_MUMBAI_TOKENS: PayoutToken[] = [ - { - name: "MATIC", - chainId: ChainId.POLYGON_MUMBAI, - address: ethers.constants.AddressZero, - decimal: 18, - logo: TokenNamesAndLogos["MATIC"], - redstoneTokenId: RedstoneTokenIds["MATIC"], - }, - { - name: "USDC", - chainId: ChainId.POLYGON_MUMBAI, - address: "0x9999f7Fea5938fD3b1E26A12c3f2fb024e194f97", - decimal: 6, - logo: TokenNamesAndLogos["USDC"], - redstoneTokenId: RedstoneTokenIds["USDC"], - }, -]; - -export const payoutTokens = [ - ...MAINNET_TOKENS, - ...OPTIMISM_MAINNET_TOKENS, - ...FANTOM_MAINNET_TOKENS, - ...FANTOM_TESTNET_TOKENS, - ...PGN_TESTNET_TOKENS, - ...PGN_MAINNET_TOKENS, - ...ARBITRUM_TOKENS, - ...ARBITRUM_GOERLI_TOKENS, - ...AVALANCHE_TOKENS, - ...FUJI_TOKENS, - ...POLYGON_TOKENS, - ...POLYGON_MUMBAI_TOKENS, -]; - -const payoutTokensMap: Record = { - [ChainId.MAINNET]: MAINNET_TOKENS, - [ChainId.POLYGON_MUMBAI]: POLYGON_MUMBAI_TOKENS, - [ChainId.POLYGON]: POLYGON_TOKENS, - [ChainId.OPTIMISM_MAINNET_CHAIN_ID]: OPTIMISM_MAINNET_TOKENS, - [ChainId.PGN]: PGN_MAINNET_TOKENS, - [ChainId.PGN_TESTNET]: PGN_TESTNET_TOKENS, - [ChainId.ARBITRUM]: ARBITRUM_TOKENS, - [ChainId.ARBITRUM_GOERLI]: ARBITRUM_GOERLI_TOKENS, - [ChainId.FUJI]: FUJI_TOKENS, - [ChainId.AVALANCHE]: AVALANCHE_TOKENS, - [ChainId.FANTOM_MAINNET_CHAIN_ID]: FANTOM_MAINNET_TOKENS, - [ChainId.FANTOM_TESTNET_CHAIN_ID]: FANTOM_TESTNET_TOKENS, - [ChainId.DEV1]: MAINNET_TOKENS, - [ChainId.DEV2]: MAINNET_TOKENS, -}; - -export const getPayoutTokenOptions = (chainId: ChainId): PayoutToken[] => - payoutTokensMap[chainId]; - /** * Fetch data from IPFS * TODO: include support for fetching abitrary data e.g images diff --git a/packages/round-manager/src/features/round/ApplicationDirectPayout.tsx b/packages/round-manager/src/features/round/ApplicationDirectPayout.tsx index ff60fd2c07..7a0591c72e 100644 --- a/packages/round-manager/src/features/round/ApplicationDirectPayout.tsx +++ b/packages/round-manager/src/features/round/ApplicationDirectPayout.tsx @@ -9,7 +9,6 @@ import { yupResolver } from "@hookform/resolvers/yup"; import * as yup from "yup"; import { ExternalLinkIcon } from "@heroicons/react/outline"; -import { getPayoutTokenOptions } from "../api/utils"; import { Erc20__factory } from "../../types/generated/typechain"; import { usePayout } from "../../context/application/usePayout"; import { Button, Input } from "common/src/styles"; @@ -18,6 +17,7 @@ import { AnswerBlock, GrantApplication, Round } from "../api/types"; import { formatUTCDateAsISOString, getUTCTime } from "common"; import { useNetwork } from "wagmi"; import { errorModalDelayMs } from "../../constants"; +import { getPayoutTokenOptions } from "../api/payoutTokens"; const schema = yup.object().shape({ amount: yup diff --git a/packages/round-manager/src/features/round/FundContract.tsx b/packages/round-manager/src/features/round/FundContract.tsx index afdaff8fe1..06ce05d2c1 100644 --- a/packages/round-manager/src/features/round/FundContract.tsx +++ b/packages/round-manager/src/features/round/FundContract.tsx @@ -9,13 +9,14 @@ import { useAccount, useBalance, useNetwork } from "wagmi"; import { errorModalDelayMs } from "../../constants"; import { useFundContract } from "../../context/round/FundContractContext"; import { ProgressStatus, Round } from "../api/types"; -import { getTxExplorerForContract, payoutTokens } from "../api/utils"; +import { getTxExplorerForContract } from "../api/utils"; import ConfirmationModal from "../common/ConfirmationModal"; import ErrorModal from "../common/ErrorModal"; import ProgressModal from "../common/ProgressModal"; import { Spinner } from "../common/Spinner"; import { classNames, useTokenPrice } from "common"; import { assertAddress } from "common/src/address"; +import { payoutTokens } from "../api/payoutTokens"; export default function FundContract(props: { round: Round | undefined; diff --git a/packages/round-manager/src/features/round/QuadraticFundingForm.tsx b/packages/round-manager/src/features/round/QuadraticFundingForm.tsx index 9fd950d1a3..278aa55eff 100644 --- a/packages/round-manager/src/features/round/QuadraticFundingForm.tsx +++ b/packages/round-manager/src/features/round/QuadraticFundingForm.tsx @@ -21,10 +21,10 @@ import { import ReactTooltip from "react-tooltip"; import * as yup from "yup"; import { Round } from "../api/types"; -import { PayoutToken, getPayoutTokenOptions } from "../api/utils"; import { useWallet } from "../common/Auth"; import { FormStepper } from "../common/FormStepper"; import { FormContext } from "../common/FormWizard"; +import { getPayoutTokenOptions, PayoutTokens } from "../api/payoutTokens"; interface QuadraticFundingFormProps { stepper: typeof FormStepper; } @@ -95,7 +95,7 @@ export default function QuadraticFundingForm(props: QuadraticFundingFormProps) { }; const { chain } = useWallet(); - const payoutTokenOptions: PayoutToken[] = [ + const payoutTokenOptions: PayoutTokens[] = [ { name: "Choose Payout Token", chainId: chain.id, @@ -288,7 +288,7 @@ function LeftSidebar() { function PayoutTokenButton(props: { errors: FieldErrors; - token?: PayoutToken; + token?: PayoutTokens; }) { const { token } = props; return ( @@ -353,7 +353,7 @@ function PayoutTokenDropdown(props: { register: UseFormRegisterReturn; errors: FieldErrors; control: Control; - payoutTokenOptions: PayoutToken[]; + payoutTokenOptions: PayoutTokens[]; }) { const { field } = useController({ name: "token", @@ -463,7 +463,7 @@ function MatchingFundsAvailable(props: { register: UseFormRegisterReturn; errors: FieldErrors; token: string; - payoutTokenOptions: PayoutToken[]; + payoutTokenOptions: PayoutTokens[]; }) { // not sure why UseFormRegisterReturn only takes strings for react-hook-form return ( @@ -522,7 +522,7 @@ function MatchingCap(props: { errors: FieldErrors; control?: Control; token: string; - payoutTokenOptions: PayoutToken[]; + payoutTokenOptions: PayoutTokens[]; }) { const { field: matchingCapField } = useController({ name: "roundMetadata.quadraticFundingConfig.matchingCap", diff --git a/packages/round-manager/src/features/round/ReclaimFunds.tsx b/packages/round-manager/src/features/round/ReclaimFunds.tsx index 071af666d0..ba06569ec7 100644 --- a/packages/round-manager/src/features/round/ReclaimFunds.tsx +++ b/packages/round-manager/src/features/round/ReclaimFunds.tsx @@ -8,7 +8,6 @@ import { useBalance } from "wagmi"; import { errorModalDelayMs } from "../../constants"; import { useReclaimFunds } from "../../context/round/ReclaimFundsContext"; import { ProgressStatus, Round } from "../api/types"; -import { payoutTokens } from "../api/utils"; import ConfirmationModal from "../common/ConfirmationModal"; import ErrorModal from "../common/ErrorModal"; import ProgressModal from "../common/ProgressModal"; @@ -16,6 +15,7 @@ import { Spinner } from "../common/Spinner"; import { AdditionalGasFeesNote } from "./BulkApplicationCommon"; import { useTokenPrice } from "common"; import { assertAddress } from "common/src/address"; +import { payoutTokens } from "../api/payoutTokens"; export default function ReclaimFunds(props: { round: Round | undefined; diff --git a/packages/round-manager/src/features/round/ViewFundGrantees.tsx b/packages/round-manager/src/features/round/ViewFundGrantees.tsx index 8b5937d95e..7e30c7b120 100644 --- a/packages/round-manager/src/features/round/ViewFundGrantees.tsx +++ b/packages/round-manager/src/features/round/ViewFundGrantees.tsx @@ -19,13 +19,14 @@ import { Round, TransactionBlock, } from "../api/types"; -import { formatCurrency, PayoutToken, payoutTokens } from "../api/utils"; +import { formatCurrency } from "../api/utils"; import { useWallet } from "../common/Auth"; import ConfirmationModal from "../common/ConfirmationModal"; import InfoModal from "../common/InfoModal"; import ProgressModal from "../common/ProgressModal"; import { Spinner } from "../common/Spinner"; import { assertAddress } from "common/src/address"; +import { PayoutTokens, payoutTokens } from "../api/payoutTokens"; export default function ViewFundGrantees(props: { round: Round | undefined; @@ -99,7 +100,7 @@ function FinalizedRoundContent(props: { round: Round }) { const [unpaidProjects, setUnpaidProjects] = useState([]); const [price, setPrice] = useState(0); - const matchingFundPayoutToken: PayoutToken = payoutTokens.filter( + const matchingFundPayoutToken: PayoutTokens = payoutTokens.filter( (t) => t.address.toLowerCase() == props.round.token.toLowerCase() )[0]; @@ -183,7 +184,7 @@ function FinalizedRoundContent(props: { round: Round }) { // TODO: Add types export function PayProjectsTable(props: { projects: MatchingStatsData[]; - token: PayoutToken; + token: PayoutTokens; price: number; round: Round; allProjects: MatchingStatsData[]; @@ -512,7 +513,7 @@ export function PayProjectsTable(props: { export function PaidProjectsTable(props: { projects: MatchingStatsData[]; chainId: number; - token: PayoutToken; + token: PayoutTokens; price: number; }) { // todo: such a nice table should be in a separate and shared file diff --git a/packages/round-manager/src/features/round/ViewRoundResults/ViewRoundResults.tsx b/packages/round-manager/src/features/round/ViewRoundResults/ViewRoundResults.tsx index 4ee03729d0..a533c40f80 100644 --- a/packages/round-manager/src/features/round/ViewRoundResults/ViewRoundResults.tsx +++ b/packages/round-manager/src/features/round/ViewRoundResults/ViewRoundResults.tsx @@ -28,7 +28,7 @@ import { useFinalizeRound } from "../../../context/round/FinalizeRoundContext"; import { setReadyForPayout } from "../../api/round"; import { errorModalDelayMs } from "../../../constants"; import { useRoundById } from "../../../context/round/RoundContext"; -import { payoutTokens, fetchFromIPFS } from "../../api/utils"; +import { fetchFromIPFS } from "../../api/utils"; import { roundApplicationsToCSV } from "../../api/exports"; import { Signer } from "@ethersproject/abstract-signer"; import { @@ -36,6 +36,7 @@ import { roundImplementationContract, } from "../../api/contracts"; import { TransactionResponse } from "@ethersproject/providers"; +import { payoutTokens } from "../../api/payoutTokens"; type RevisedMatch = { revisedContributionCount: number; diff --git a/packages/round-manager/src/features/round/ViewRoundSettings.tsx b/packages/round-manager/src/features/round/ViewRoundSettings.tsx index 001db1ecd4..3860ca19db 100644 --- a/packages/round-manager/src/features/round/ViewRoundSettings.tsx +++ b/packages/round-manager/src/features/round/ViewRoundSettings.tsx @@ -38,7 +38,7 @@ import { ProgressStep, Round, } from "../api/types"; -import { CHAINS, SupportType, payoutTokens } from "../api/utils"; +import { CHAINS, SupportType } from "../api/utils"; import ConfirmationModal from "../common/ConfirmationModal"; import ErrorModal from "../common/ErrorModal"; import FormValidationErrorList from "../common/FormValidationErrorList"; @@ -52,6 +52,7 @@ import { } from "./RoundDetailForm"; import { isDirectRound } from "./ViewRoundPage"; import { maxDateForUint256 } from "../../constants"; +import { payoutTokens } from "../api/payoutTokens"; type EditMode = { canEdit: boolean; diff --git a/packages/round-manager/src/features/round/ViewRoundStats.tsx b/packages/round-manager/src/features/round/ViewRoundStats.tsx index c75f539412..be6f948023 100644 --- a/packages/round-manager/src/features/round/ViewRoundStats.tsx +++ b/packages/round-manager/src/features/round/ViewRoundStats.tsx @@ -8,8 +8,8 @@ import { useRoundMatchingFunds, } from "../../hooks"; import { getUTCDate } from "common"; -import { payoutTokens } from "../api/utils"; import { useChainId } from "wagmi"; +import { payoutTokens } from "../api/payoutTokens"; export default function ViewRoundStats() { const { id } = useParams(); @@ -29,7 +29,7 @@ export default function ViewRoundStats() { payoutTokens.find( (t) => t.address.toLowerCase() == round.token.toLowerCase() && - t.chainId === chainId, + t.chainId === chainId ); return ( @@ -52,7 +52,7 @@ export default function ViewRoundStats() { round && `${utils.formatUnits( round.matchAmount, - matchToken?.decimal, + matchToken?.decimal )} ${matchToken?.name}` } title={"Matching Funds Available"} @@ -102,7 +102,7 @@ export default function ViewRoundStats() { matches.map((match: Match) => { const percentage = Number( - (BigInt(1000000) * match.matched) / round.matchAmount, + (BigInt(1000000) * match.matched) / round.matchAmount ) / 10000; return ( diff --git a/packages/round-manager/src/features/round/__tests__/QuadraticFundingForm.test.tsx b/packages/round-manager/src/features/round/__tests__/QuadraticFundingForm.test.tsx index a5873753e1..e1172dcefb 100644 --- a/packages/round-manager/src/features/round/__tests__/QuadraticFundingForm.test.tsx +++ b/packages/round-manager/src/features/round/__tests__/QuadraticFundingForm.test.tsx @@ -3,10 +3,10 @@ import { fireEvent, screen } from "@testing-library/react"; import { renderWrapped } from "../../../test-utils"; import { ChainId } from "common"; -import { getPayoutTokenOptions } from "../../api/utils"; import { useWallet } from "../../common/Auth"; import { FormStepper } from "../../common/FormStepper"; import QuadraticFundingForm from "../QuadraticFundingForm"; +import { getPayoutTokenOptions } from "../../api/payoutTokens"; jest.mock("../../common/Auth"); jest.mock("@rainbow-me/rainbowkit", () => ({