From 788814b0e1cf2b49c19d08f3a7d493543020ff81 Mon Sep 17 00:00:00 2001 From: Ivan P Date: Sun, 2 Feb 2025 14:46:14 -0700 Subject: [PATCH 1/2] Update README with more environment setup info --- .nvmrc | 1 + README.md | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 .nvmrc diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..2bf5ad0 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +stable diff --git a/README.md b/README.md index 2eb3059..64b88d5 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,15 @@ $ cd contracts $ forge install ``` +### Create .env.local file +```sh +$ cp .env.example .env.local +``` + +### Obtain necessary API keys +1. [Alchemy](https://www.alchemy.com) is used to resolve ENS domains. Copy the API key into `NEXT_PUBLIC_ALCHEMY_MAINNET_API_KEY` variable in `.env.local`. +2. [Reown](https://cloud.reown.com) is used for WalletConnect functionality. Create an "Appkit" and copy the project id into `NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID` variable in `.env.local`. + ### Start local node [Anvil](https://book.getfoundry.sh/reference/anvil/) starts a local fork from the most recent block based on the rpc url provided. The chain id is used to identify the anvil network in the app config. Block time is 5 seconds to match gnosis chain. @@ -23,9 +32,11 @@ $ anvil --fork-url https://rpc.gnosis.gateway.fm --chain-id 31337 --block-time 5 ### Setup wallet -We also need a wallet for working locally. To set this up take the private key for the first wallet in the list displayed when you start anvil and add this account to metamask. This is the `DEV_ACCOUNT` address in the setup script. +1. We need a wallet for working locally. To set this up take the private key for the first wallet in the list displayed when you start anvil and add this account to metamask. This is the `DEV_ACCOUNT` address in the setup script. + +2. Add the Anvil RPC url to metamask: `http://127.0.0.1:8545` with chain id `31337` -Because of how the distributor contract works the developer wallet needs to hold some bread before we deploy the contracts for us to be able to vote. The setup script takes care of this as well as funding the wallet with LP tokens which is needed for the LP locking feature. +3. Because of how the distributor contract works the developer wallet needs to hold some bread before we deploy the contracts for us to be able to vote. The setup script takes care of this as well as funding the wallet with LP tokens which is needed for the LP locking feature. ```sh $ pnpm run chain:setup From 8ace89174492239ffd1310399222b5302ed9f853 Mon Sep 17 00:00:00 2001 From: Ivan P Date: Sun, 2 Feb 2025 15:33:44 -0700 Subject: [PATCH 2/2] Allow running preDeploy script before all contracts are deployed --- scripts/lib.ts | 41 +++----- scripts/setupPostDeploy.ts | 4 +- scripts/setupPreDeploy.ts | 2 +- src/app/lpTokenMeta.ts | 4 +- src/chainConfig.ts | 205 +++++++++++++++++++------------------ src/constants.ts | 1 + 6 files changed, 130 insertions(+), 127 deletions(-) diff --git a/scripts/lib.ts b/scripts/lib.ts index eba631c..57f052e 100644 --- a/scripts/lib.ts +++ b/scripts/lib.ts @@ -16,21 +16,12 @@ import { walletActions, } from "viem"; import { foundry } from "viem/chains"; +import { BREAD_ADDRESS, BUTTER_ADDRESS } from "../src/constants"; -export const anvilConfig = getConfig(31337); - -export const anvilAccounts: Array = [ - // mock wallet 2 - // "0x70997970c51812dc3a010c7d01b50e0d17dc79c8", - "0x3c44cdddb6a900fa2b585dd299e03d12fa4293bc", - "0x90f79bf6eb2c4f870365e785982e1f101e93b906", - "0x15d34aaf54267db7d7c367839aaf71a00a2c6a65", - "0x9965507d1a55bcc2695c58ba16fb37d819b0a4dc", - "0x976ea74026e726554db657fa54763abd0c3a0aa9", - "0x14dc79964da2c08b23698b3d3cc7ca32193d9955", - "0x23618e81e3f5cdf7f54c3d65f7fbc0abf5b21e8f", - "0xa0ee7a142d267c1f36714e4a8f75612f20a79720", -]; +// Wrapped into a function so we do not initialize on import, before all contracts are deployed +export function getAnvilConfig() { + return getConfig(31337); +} export const DEV_ACCOUNT = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"; const BUTTER_WHALE = "0xc2fB4B3EA53E10c88D193E709A81C4dc7aEC902e" as Hex; @@ -62,7 +53,7 @@ export async function bakeBread( try { const hash = await testClient.writeContract({ account: account, - address: anvilConfig.BREAD.address, + address: BREAD_ADDRESS, abi: BREAD_ABI, functionName: "mint", // mint is a payable function so we pass the value like this @@ -100,7 +91,7 @@ export async function fundLpTokens(account: Hex = DEV_ACCOUNT) { await testClient.writeContract({ account: BUTTER_WHALE, - address: anvilConfig.BUTTER.address, + address: BUTTER_ADDRESS, abi: ERC20_ABI, functionName: "transfer", // this isn't a payable function so we pass the value as an argument @@ -114,7 +105,7 @@ export async function balanceOf(anvilAccount: Hex) { }); const res = await publicClient.readContract({ - address: anvilConfig.BREAD.address, + address: getAnvilConfig().BREAD.address, abi: BREAD_ABI, functionName: "balanceOf", args: [anvilAccount], @@ -136,7 +127,7 @@ export async function castVote(account: Hex = DEV_ACCOUNT) { try { const hash = await testClient.writeContract({ - address: anvilConfig.DISBURSER.address, + address: getAnvilConfig().DISBURSER.address, abi: DISTRIBUTOR_ABI, functionName: "castVote", account: account, @@ -171,7 +162,7 @@ export async function castVote(account: Hex = DEV_ACCOUNT) { export async function getCurrentDistribution() { const res = await publicClient.readContract({ - address: anvilConfig.DISBURSER.address, + address: getAnvilConfig().DISBURSER.address, abi: DISTRIBUTOR_ABI, functionName: "getCurrentVotingDistribution", }); @@ -186,7 +177,7 @@ export async function setClaimer(newClaimer: Hex) { try { const hash = await testClient.writeContract({ - address: anvilConfig.BREAD.address, + address: getAnvilConfig().BREAD.address, abi: BREAD_ABI, functionName: "setYieldClaimer", account: BREAD_OWNER, @@ -211,10 +202,10 @@ export async function lockLpTokens(account: Hex = DEV_ACCOUNT) { try { const hash = await testClient.writeContract({ account: account, - address: anvilConfig.BUTTER.address, + address: getAnvilConfig().BUTTER.address, abi: ERC20_ABI, functionName: "approve", - args: [anvilConfig.BUTTERED_BREAD.address, parseUnits("5000", 18)], + args: [getAnvilConfig().BUTTERED_BREAD.address, parseUnits("5000", 18)], }); const receipt = await publicClient.waitForTransactionReceipt({ hash }); @@ -231,10 +222,10 @@ export async function lockLpTokens(account: Hex = DEV_ACCOUNT) { try { const hash = await testClient.writeContract({ account: account, - address: anvilConfig.BUTTERED_BREAD.address, + address: getAnvilConfig().BUTTERED_BREAD.address, abi: BUTTERED_BREAD_ABI, functionName: "deposit", - args: [anvilConfig.BUTTER.address, parseUnits("5000", 18)], + args: [getAnvilConfig().BUTTER.address, parseUnits("5000", 18)], }); const receipt = await publicClient.waitForTransactionReceipt({ hash }); @@ -257,7 +248,7 @@ export async function distributeYield(account: Hex = DEV_ACCOUNT) { try { const hash = await testClient.writeContract({ account: account, - address: anvilConfig.DISBURSER.address, + address: getAnvilConfig().DISBURSER.address, abi: DISTRIBUTOR_ABI, functionName: "distributeYield", }); diff --git a/scripts/setupPostDeploy.ts b/scripts/setupPostDeploy.ts index 46a09e3..f97954e 100644 --- a/scripts/setupPostDeploy.ts +++ b/scripts/setupPostDeploy.ts @@ -2,13 +2,13 @@ import { setClaimer, castVote, lockLpTokens, - anvilConfig, + getAnvilConfig, distributeYield, mineBlocks, } from "./lib"; async function main() { - const DISTRIBUTOR_ADDRESS = anvilConfig.DISBURSER.address; + const DISTRIBUTOR_ADDRESS = getAnvilConfig().DISBURSER.address; if (DISTRIBUTOR_ADDRESS === "0x") throw new Error("invalid DISTRIBUTOR_ADDRESS"); diff --git a/scripts/setupPreDeploy.ts b/scripts/setupPreDeploy.ts index d1c81fb..1fdbc27 100644 --- a/scripts/setupPreDeploy.ts +++ b/scripts/setupPreDeploy.ts @@ -1,4 +1,4 @@ -import { bakeBread, fundLpTokens, lockLpTokens } from "./lib"; +import { bakeBread, fundLpTokens } from "./lib"; /* This script sets a dev wallet up with bread and lp tokens diff --git a/src/app/lpTokenMeta.ts b/src/app/lpTokenMeta.ts index a08ea78..68f98d8 100644 --- a/src/app/lpTokenMeta.ts +++ b/src/app/lpTokenMeta.ts @@ -1,5 +1,5 @@ import { Hex } from "viem"; -import { BUTTERED_BREAD_ADDRESS } from "@/constants"; +import { BUTTER_ADDRESS, BUTTERED_BREAD_ADDRESS } from "@/constants"; export type LPTokenMeta = { tokenName: string; @@ -11,7 +11,7 @@ export type LPTokenMeta = { export const lpTokenMeta: { [key: Hex]: LPTokenMeta; } = { - "0xf3d8f3de71657d342db60dd714c8a2ae37eac6b4": { + [BUTTER_ADDRESS]: { tokenName: "BREAD/WXDAI LP", poolName: "BREAD/WXDAI", inspectContract: diff --git a/src/chainConfig.ts b/src/chainConfig.ts index 0db9b58..2c96c7e 100644 --- a/src/chainConfig.ts +++ b/src/chainConfig.ts @@ -1,13 +1,6 @@ import { Hex } from "viem"; -import { BREAD_ADDRESS, BUTTERED_BREAD_ADDRESS } from "./constants"; +import { BREAD_ADDRESS, BUTTER_ADDRESS, BUTTERED_BREAD_ADDRESS } from "./constants"; -let DISTRIBUTOR_DEPLOYED = { ADDRESS: "0x" }; -let BUTTERED_BREAD_DEPLOYED = { ADDRESS: "0x" }; - -if (process.env.NODE_ENV === "development") { - DISTRIBUTOR_DEPLOYED = require("../contracts/out/DISTRIBUTOR.json"); - BUTTERED_BREAD_DEPLOYED = require("../contracts/out/BUTTERED_BREAD.json"); -} interface IToken { address: Hex; @@ -39,97 +32,114 @@ export interface IConfig { DEFAULT: ChainConfiguration; } -const sepolia: ChainConfiguration = { - ID: 11155111, - NETWORK_STRING: "Sepolia", - EXPLORER: "NONE", - BREAD: { - symbol: "BREAD", - decimals: 18, - address: "0x689666145b8e80f705b87f4e4190820d9a4c1646", - }, - DISBURSER: { - address: "0xeE95A62b749d8a2520E0128D9b3aCa241269024b", - }, - BUTTERED_BREAD: { - address: "0x", - }, - BUTTER: { - address: "0xf3d8f3de71657d342db60dd714c8a2ae37eac6b4", - }, - SDAI_ADAPTOR: { - address: "0x", - }, -}; - -const gnosis: ChainConfiguration = { - ID: 100, - NETWORK_STRING: "Gnosis", - EXPLORER: "https://gnosisscan.io", - BREAD: { - symbol: "BREAD", - decimals: 18, - address: BREAD_ADDRESS, - }, - DISBURSER: { - address: "0xeE95A62b749d8a2520E0128D9b3aCa241269024b", - }, - BUTTERED_BREAD: { - address: BUTTERED_BREAD_ADDRESS, - }, - BUTTER: { - address: "0xf3d8f3de71657d342db60dd714c8a2ae37eac6b4", - }, - SDAI_ADAPTOR: { - address: "0xD499b51fcFc66bd31248ef4b28d656d67E591A94", - }, -}; - -const anvil: ChainConfiguration = { - ID: 31337, - NETWORK_STRING: "Anvil", - EXPLORER: "https://gnosisscan.io", - BREAD: { - symbol: "BREAD", - decimals: 18, - address: BREAD_ADDRESS, - }, - DISBURSER: { - address: - (!!DISTRIBUTOR_DEPLOYED && (DISTRIBUTOR_DEPLOYED.ADDRESS as Hex)) || "0x", - }, - BUTTERED_BREAD: { - address: - (!!BUTTERED_BREAD_DEPLOYED && (BUTTERED_BREAD_DEPLOYED.ADDRESS as Hex)) || - "0x", - }, - BUTTER: { - address: "0xf3d8f3de71657d342db60dd714c8a2ae37eac6b4", - }, - SDAI_ADAPTOR: { - address: "0xD499b51fcFc66bd31248ef4b28d656d67E591A94", - }, -}; - -const developmentConfig: IConfig = { - 100: gnosis, - 11155111: sepolia, - 31337: anvil, - DEFAULT: anvil, -}; - -const stagingConfig: IConfig = { - 100: gnosis, - 11155111: sepolia, - DEFAULT: sepolia, -}; - -const prodConfig: IConfig = { - 100: gnosis, - DEFAULT: gnosis, -}; +function makeAllConfigs() { + let DISTRIBUTOR_DEPLOYED = { ADDRESS: "0x" }; + let BUTTERED_BREAD_DEPLOYED = { ADDRESS: "0x" }; + + if (process.env.NODE_ENV === "development") { + DISTRIBUTOR_DEPLOYED = require("../contracts/out/DISTRIBUTOR.json"); + BUTTERED_BREAD_DEPLOYED = require("../contracts/out/BUTTERED_BREAD.json"); + } + + const sepolia: ChainConfiguration = { + ID: 11155111, + NETWORK_STRING: "Sepolia", + EXPLORER: "NONE", + BREAD: { + symbol: "BREAD", + decimals: 18, + address: "0x689666145b8e80f705b87f4e4190820d9a4c1646", + }, + DISBURSER: { + address: "0xeE95A62b749d8a2520E0128D9b3aCa241269024b", + }, + BUTTERED_BREAD: { + address: "0x", + }, + BUTTER: { + address: BUTTER_ADDRESS, + }, + SDAI_ADAPTOR: { + address: "0x", + }, + }; + + const gnosis: ChainConfiguration = { + ID: 100, + NETWORK_STRING: "Gnosis", + EXPLORER: "https://gnosisscan.io", + BREAD: { + symbol: "BREAD", + decimals: 18, + address: BREAD_ADDRESS, + }, + DISBURSER: { + address: "0xeE95A62b749d8a2520E0128D9b3aCa241269024b", + }, + BUTTERED_BREAD: { + address: BUTTERED_BREAD_ADDRESS, + }, + BUTTER: { + address: BUTTER_ADDRESS, + }, + SDAI_ADAPTOR: { + address: "0xD499b51fcFc66bd31248ef4b28d656d67E591A94", + }, + }; + + const anvil: ChainConfiguration = { + ID: 31337, + NETWORK_STRING: "Anvil", + EXPLORER: "https://gnosisscan.io", + BREAD: { + symbol: "BREAD", + decimals: 18, + address: BREAD_ADDRESS, + }, + DISBURSER: { + address: + (!!DISTRIBUTOR_DEPLOYED && (DISTRIBUTOR_DEPLOYED.ADDRESS as Hex)) || "0x", + }, + BUTTERED_BREAD: { + address: + (!!BUTTERED_BREAD_DEPLOYED && (BUTTERED_BREAD_DEPLOYED.ADDRESS as Hex)) || + "0x", + }, + BUTTER: { + address: BUTTER_ADDRESS, + }, + SDAI_ADAPTOR: { + address: "0xD499b51fcFc66bd31248ef4b28d656d67E591A94", + }, + }; + + const developmentConfig: IConfig = { + 100: gnosis, + 11155111: sepolia, + 31337: anvil, + DEFAULT: anvil, + }; + + const stagingConfig: IConfig = { + 100: gnosis, + 11155111: sepolia, + DEFAULT: sepolia, + }; + + const prodConfig: IConfig = { + 100: gnosis, + DEFAULT: gnosis, + }; + + return { + developmentConfig, + stagingConfig, + prodConfig, + }; +} export function getConfig(id: number | "DEFAULT"): ChainConfiguration { + const { developmentConfig, stagingConfig, prodConfig } = makeAllConfigs(); if (process.env.NODE_ENV === "development") { return developmentConfig[id] || developmentConfig["DEFAULT"]; } @@ -142,6 +152,7 @@ export function getConfig(id: number | "DEFAULT"): ChainConfiguration { } export function isChainSupported(id: number) { + const { developmentConfig, stagingConfig, prodConfig } = makeAllConfigs(); if (process.env.NODE_ENV === "development") { return !!developmentConfig[id]; } diff --git a/src/constants.ts b/src/constants.ts index 3522616..c5b3355 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -2,5 +2,6 @@ export const SUBGRAPH_URL = "https://api.thegraph.com/subgraphs/name/breadchaincoop/bread-polygon"; export const BREAD_ADDRESS = "0xa555d5344f6FB6c65da19e403Cb4c1eC4a1a5Ee3"; +export const BUTTER_ADDRESS = "0xf3d8f3de71657d342db60dd714c8a2ae37eac6b4"; export const BUTTERED_BREAD_ADDRESS = "0x680B581605DC0A6902735a80dE35Cb0Ef6E90865";