From 661c8d0800923a6a1cbdc0dc50ae9142fb30c0c5 Mon Sep 17 00:00:00 2001 From: Matthew Pereira Date: Sat, 22 Jun 2024 18:31:23 -0700 Subject: [PATCH] add a second pool deploy script --- packages/foundry/package.json | 4 +- .../foundry/script/00_DeployMockTokens.s.sol | 63 +- .../foundry/script/01_DeployFactory.s.sol | 15 + ..._DeployPool.s.sol => 02_DeployPool1.s.sol} | 10 +- packages/foundry/script/03_DeployPool2.s.sol | 123 +++ packages/foundry/utils/HelperConfig.sol | 9 +- .../nextjs/contracts/deployedContracts.ts | 706 ++++++++++++++++++ 7 files changed, 890 insertions(+), 40 deletions(-) rename packages/foundry/script/{02_DeployPool.s.sol => 02_DeployPool1.s.sol} (93%) create mode 100644 packages/foundry/script/03_DeployPool2.s.sol diff --git a/packages/foundry/package.json b/packages/foundry/package.json index bdcd41e4..1d5a20f1 100644 --- a/packages/foundry/package.json +++ b/packages/foundry/package.json @@ -5,9 +5,9 @@ "account": "node script/ListAccount.js", "chain": "anvil --config-out localhost.json", "compile": "forge compile", - "deploy:tokens": "forge build --build-info --build-info-path out/build-info/ && forge script script/00_DeployMockTokens.s.sol --rpc-url ${1:-default_network} --broadcast --legacy", "deploy:factory": "forge build --build-info --build-info-path out/build-info/ && forge script script/01_DeployFactory.s.sol --rpc-url ${1:-default_network} --broadcast --legacy && node script/generateTsAbis.js", - "deploy:pool": "forge build --build-info --build-info-path out/build-info/ && forge script script/02_DeployPool.s.sol --rpc-url ${1:-default_network} --broadcast", + "deploy:pool1": "forge script script/02_DeployPool1.s.sol --rpc-url ${1:-default_network} --broadcast", + "deploy:pool2": "forge script script/03_DeployPool2.s.sol --rpc-url ${1:-default_network} --broadcast", "deploy:verify": "forge build --build-info --build-info-path out/build-info/ && forge script script/DeployFactoryAndPool.s.sol --rpc-url ${1:-default_network} --broadcast --legacy --verify ; node script/generateTsAbis.js", "flatten": "forge flatten", "fork": "anvil --fork-url ${0:-sepolia} --chain-id 31337 --config-out localhost.json", diff --git a/packages/foundry/script/00_DeployMockTokens.s.sol b/packages/foundry/script/00_DeployMockTokens.s.sol index 0073935f..a95b8e02 100644 --- a/packages/foundry/script/00_DeployMockTokens.s.sol +++ b/packages/foundry/script/00_DeployMockTokens.s.sol @@ -1,36 +1,41 @@ //SPDX-License-Identifier: MIT pragma solidity ^0.8.24; -import { ScaffoldETHDeploy, console } from "./ScaffoldETHDeploy.s.sol"; -import { MockToken1 } from "../contracts/MockToken1.sol"; -import { MockToken2 } from "../contracts/MockToken2.sol"; - /** - * @title Deploy Mock Tokens - * @dev Run this script with `yarn deploy:tokens` + * @notice TODO - to use this deploy script, must figure out how to carry contract info from foundry/ to nextjs/ for more than a single deploy script + * @dev See `ScaffoldETHDeploy.s.sol` and `generateTsAbis.js` to understand why */ -contract DeployMockTokens is ScaffoldETHDeploy { - function run() external virtual { - uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); - if (deployerPrivateKey == 0) { - revert InvalidPrivateKey( - "You don't have a deployer account. Make sure you have set DEPLOYER_PRIVATE_KEY in .env or use `yarn generate` to generate a new random account" - ); - } - vm.startBroadcast(deployerPrivateKey); - MockToken1 scUSD = new MockToken1("Scaffold USD", "scUSD"); - MockToken2 scDAI = new MockToken2("Scaffold DAI", "scDAI"); - console.log("Deployed MockToken1 Address: %s", address(scUSD)); - console.log("Deployed MockToken2 Address: %s", address(scDAI)); - vm.stopBroadcast(); +// import { ScaffoldETHDeploy, console } from "./ScaffoldETHDeploy.s.sol"; +// import { MockToken1 } from "../contracts/MockToken1.sol"; +// import { MockToken2 } from "../contracts/MockToken2.sol"; + +// /** +// * @title Deploy Mock Tokens +// * @dev Run this script with `yarn deploy:tokens` +// */ +// contract DeployMockTokens is ScaffoldETHDeploy { +// function run() external virtual { +// uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); +// if (deployerPrivateKey == 0) { +// revert InvalidPrivateKey( +// "You don't have a deployer account. Make sure you have set DEPLOYER_PRIVATE_KEY in .env or use `yarn generate` to generate a new random account" +// ); +// } + +// vm.startBroadcast(deployerPrivateKey); +// MockToken1 scUSD = new MockToken1("Scaffold USD", "scUSD"); +// MockToken2 scDAI = new MockToken2("Scaffold DAI", "scDAI"); +// console.log("Deployed MockToken1 Address: %s", address(scUSD)); +// console.log("Deployed MockToken2 Address: %s", address(scDAI)); +// vm.stopBroadcast(); - // TODO: figure out how to carry contract info from foundry to nextjs for more than a single deploy script - // /** - // * This function generates the file containing the contracts Abi definitions. - // * These definitions are used to derive the types needed in the custom scaffold-eth hooks, for example. - // * This function should be called last. - // */ - // exportDeployments(); - } -} +// +// // /** +// // * This function generates the file containing the contracts Abi definitions. +// // * These definitions are used to derive the types needed in the custom scaffold-eth hooks, for example. +// // * This function should be called last. +// // */ +// // exportDeployments(); +// } +// } diff --git a/packages/foundry/script/01_DeployFactory.s.sol b/packages/foundry/script/01_DeployFactory.s.sol index 29ad515f..07036e7b 100644 --- a/packages/foundry/script/01_DeployFactory.s.sol +++ b/packages/foundry/script/01_DeployFactory.s.sol @@ -4,6 +4,8 @@ pragma solidity ^0.8.24; import { ScaffoldETHDeploy, console } from "./ScaffoldETHDeploy.s.sol"; import { ConstantSumFactory } from "../contracts/ConstantSumFactory.sol"; import { HelperConfig } from "../utils/HelperConfig.sol"; +import { MockToken1 } from "../contracts/MockToken1.sol"; +import { MockToken2 } from "../contracts/MockToken2.sol"; /** * @title Deploy Factory @@ -20,7 +22,20 @@ contract DeployFactory is HelperConfig, ScaffoldETHDeploy { } uint32 pauseWindowDuration = getFactoryConfig(); + vm.startBroadcast(deployerPrivateKey); + /** + * @notice Deploy mock tokens to be used for initializing pools + * @dev remove this if you plan to use already deployed tokens + */ + MockToken1 scUSD = new MockToken1("Scaffold USD", "scUSD"); + MockToken2 scDAI = new MockToken2("Scaffold DAI", "scDAI"); + console.log("Deployed MockToken1 Address: %s", address(scUSD)); + console.log("Deployed MockToken2 Address: %s", address(scDAI)); + + /** + * @notice Deploys the factory contract using the pauseWindowDuration set in `HelperConfig.sol` + */ ConstantSumFactory factory = new ConstantSumFactory(vault, pauseWindowDuration); console.log("Deployed Factory Address: %s", address(factory)); vm.stopBroadcast(); diff --git a/packages/foundry/script/02_DeployPool.s.sol b/packages/foundry/script/02_DeployPool1.s.sol similarity index 93% rename from packages/foundry/script/02_DeployPool.s.sol rename to packages/foundry/script/02_DeployPool1.s.sol index cfd6382d..65fe3d6c 100644 --- a/packages/foundry/script/02_DeployPool.s.sol +++ b/packages/foundry/script/02_DeployPool1.s.sol @@ -8,8 +8,6 @@ import { DevOpsTools } from "lib/foundry-devops/src/DevOpsTools.sol"; import { Script, console } from "forge-std/Script.sol"; import { RegistrationConfig, InitializationConfig } from "../utils/PoolTypes.sol"; -import { InputHelpers } from "@balancer-labs/v3-solidity-utils/contracts/helpers/InputHelpers.sol"; - /** * @title Deploy Pool Script * @notice This script deploys a new pool using the most recently deployed pool factory and mock tokens @@ -26,7 +24,9 @@ contract DeployPool is HelperConfig, Script { "You don't have a deployer account. Make sure you have set DEPLOYER_PRIVATE_KEY in .env or use `yarn generate` to generate a new random account" ); } - // Set the pool registration and initialization configurations in `HelperConfig.sol` + /** + * @dev Set the pool registration and initialization configurations in `HelperConfig.sol` + */ RegistrationConfig memory regConfig = getPoolConfig(); InitializationConfig memory initConfig = getInitializationConfig(regConfig.tokenConfig); // Grab the most recently deployed address of the pool factory @@ -42,7 +42,7 @@ contract DeployPool is HelperConfig, Script { regConfig.name, regConfig.symbol, regConfig.salt, - sortTokenConfig(regConfig.tokenConfig), + regConfig.tokenConfig, regConfig.swapFeePercentage, regConfig.protocolFeeExempt, regConfig.roleAccounts, @@ -53,7 +53,7 @@ contract DeployPool is HelperConfig, Script { // Initialize the pool initializePool( newPool, - InputHelpers.sortTokens(initConfig.tokens), + initConfig.tokens, initConfig.exactAmountsIn, initConfig.minBptAmountOut, initConfig.wethIsEth, diff --git a/packages/foundry/script/03_DeployPool2.s.sol b/packages/foundry/script/03_DeployPool2.s.sol new file mode 100644 index 00000000..dd2a1aa4 --- /dev/null +++ b/packages/foundry/script/03_DeployPool2.s.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.24; + +import { LiquidityManagement, PoolRoleAccounts } from "@balancer-labs/v3-interfaces/contracts/vault/VaultTypes.sol"; + +import { HelperConfig } from "../utils/HelperConfig.sol"; +import { ConstantSumFactory } from "../contracts/ConstantSumFactory.sol"; +import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol"; +import { DevOpsTools } from "lib/foundry-devops/src/DevOpsTools.sol"; +import { Script, console } from "forge-std/Script.sol"; +import { RegistrationConfig, InitializationConfig } from "../utils/PoolTypes.sol"; + +/** + * @title Deploy Pool Script + * @notice This script deploys a new pool using the most recently deployed pool factory and mock tokens + * @dev Some of the pool registration/initialization configurations are set in `HelperConfig.sol`, some are set directly in this script + * @dev Run this script with `yarn deploy:pool` + */ +contract DeployPool is HelperConfig, Script { + error InvalidPrivateKey(string); + + function run() external virtual { + uint256 deployerPrivateKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); + if (deployerPrivateKey == 0) { + revert InvalidPrivateKey( + "You don't have a deployer account. Make sure you have set DEPLOYER_PRIVATE_KEY in .env or use `yarn generate` to generate a new random account" + ); + } + // Set the pool registration and initialization configurations in `HelperConfig.sol` + RegistrationConfig memory regConfig = getPoolConfig(); + InitializationConfig memory initConfig = getInitializationConfig(regConfig.tokenConfig); + // Grab the most recently deployed address of the pool factory + address poolFactoryAddress = DevOpsTools.get_most_recent_deployment( + "ConstantSumFactory", // Must match the pool factory contract name + block.chainid + ); + ConstantSumFactory factory = ConstantSumFactory(poolFactoryAddress); + /** + * @notice Altering some of the config for this second pool + * @dev watch out for "stack too deep" error if you declare too many vars directly in this `run()` function + */ + PoolRoleAccounts memory roleAccounts = PoolRoleAccounts({ + pauseManager: msg.sender, // Account empowered to pause/unpause the pool (or 0 to delegate to governance) + swapFeeManager: msg.sender, // Account empowered to set static swap fees for a pool (or 0 to delegate to goverance) + poolCreator: msg.sender // Account empowered to set the pool creator fee percentage + }); + LiquidityManagement memory liquidityManagement = LiquidityManagement({ + disableUnbalancedLiquidity: false, + enableAddLiquidityCustom: true, + enableRemoveLiquidityCustom: true + }); + // Send the transactions + vm.startBroadcast(deployerPrivateKey); + // Deploy the pool (and register it with the vault) + address newPool = factory.create( + "Constant Sum Pool #2", // name + "CS2-50scUSD-50scDAI", // symbol + keccak256(abi.encode("Constant Sum Pool #2")), // salt + regConfig.tokenConfig, // tokenConfigs + 33e12, // swapFeePercentage of 0.000033% + regConfig.protocolFeeExempt, + roleAccounts, + regConfig.poolHooksContract, + liquidityManagement + ); + console.log("Deployed pool at address: %s", newPool); + // Initialize the pool + initializePool( + newPool, + initConfig.tokens, + initConfig.exactAmountsIn, + initConfig.minBptAmountOut, + initConfig.wethIsEth, + initConfig.userData + ); + console.log("Pool initialized successfully!"); + vm.stopBroadcast(); + } + + /** + * @notice Approves the vault to spend tokens and then initializes the pool + */ + function initializePool( + address pool, + IERC20[] memory tokens, + uint256[] memory exactAmountsIn, + uint256 minBptAmountOut, + bool wethIsEth, + bytes memory userData + ) internal { + // Approve Permit2 to spend account tokens + approveSpenderOnToken(address(permit2), tokens); + // Approve Router to spend account tokens using Permit2 + approveSpenderOnPermit2(address(router), tokens); + // Initialize pool with the tokens that have been permitted + router.initialize(pool, tokens, exactAmountsIn, minBptAmountOut, wethIsEth, userData); + } + + /** + * @notice Max approving to speed up UX on frontend + * @param tokens Array of tokens to approve + * @param spender Address of the spender + */ + function approveSpenderOnToken(address spender, IERC20[] memory tokens) internal { + uint256 maxAmount = type(uint256).max; + for (uint256 i = 0; i < tokens.length; ++i) { + tokens[i].approve(spender, maxAmount); + } + } + + /** + * @notice Max approving to speed up UX on frontend + * @param tokens Array of tokens to approve + * @param spender Address of the spender + */ + function approveSpenderOnPermit2(address spender, IERC20[] memory tokens) internal { + uint160 maxAmount = type(uint160).max; + uint48 maxExpiration = type(uint48).max; + for (uint256 i = 0; i < tokens.length; ++i) { + permit2.approve(address(tokens[i]), spender, maxAmount, maxExpiration); + } + } +} diff --git a/packages/foundry/utils/HelperConfig.sol b/packages/foundry/utils/HelperConfig.sol index 029d9060..036b2f41 100644 --- a/packages/foundry/utils/HelperConfig.sol +++ b/packages/foundry/utils/HelperConfig.sol @@ -10,6 +10,7 @@ import { import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol"; import { IRouter } from "@balancer-labs/v3-interfaces/contracts/vault/IRouter.sol"; import { IRateProvider } from "@balancer-labs/v3-interfaces/contracts/vault/IRateProvider.sol"; +import { InputHelpers } from "@balancer-labs/v3-solidity-utils/contracts/helpers/InputHelpers.sol"; import { IPermit2 } from "permit2/src/interfaces/IPermit2.sol"; import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol"; @@ -41,8 +42,8 @@ contract HelperConfig { * @dev Set all of the configurations for deploying and registering a pool here */ function getPoolConfig() internal view returns (RegistrationConfig memory regConfig) { - string memory name = "Scaffold Balancer Constant Sum Pool #1"; // name for the pool - string memory symbol = "SB-50scUSD-50scDAI"; // symbol for the BPT + string memory name = "Constant Sum Pool #1"; // name for the pool + string memory symbol = "CS1-50scUSD-50scDAI"; // symbol for the BPT bytes32 salt = keccak256(abi.encode(name)); // salt for the pool deployment via factory // Grab the most recently deployed addresses of mock tokens address mockToken1 = DevOpsTools.get_most_recent_deployment( @@ -88,7 +89,7 @@ contract HelperConfig { name: name, symbol: symbol, salt: salt, - tokenConfig: tokenConfig, + tokenConfig: sortTokenConfig(tokenConfig), swapFeePercentage: swapFeePercentage, protocolFeeExempt: protocolFeeExempt, roleAccounts: roleAccounts, @@ -115,7 +116,7 @@ contract HelperConfig { bytes memory userData = bytes(""); // Additional (optional) data required for adding initial liquidity poolInitConfig = InitializationConfig({ - tokens: tokens, + tokens: InputHelpers.sortTokens(tokens), exactAmountsIn: exactAmountsIn, minBptAmountOut: minBptAmountOut, wethIsEth: wethIsEth, diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index b01c09a0..b6b52f86 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -6,6 +6,712 @@ import { GenericContractsDeclaration } from "~~/utils/scaffold-eth/contract"; const deployedContracts = { 31337: { + MockToken1: { + address: "0x579bB3a775c831526D5E94EF9ccd6601F62790Bc", + abi: [ + { + type: "constructor", + inputs: [ + { + name: "name", + type: "string", + internalType: "string", + }, + { + name: "symbol", + type: "string", + internalType: "string", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "allowance", + inputs: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + { + name: "spender", + type: "address", + internalType: "address", + }, + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "approve", + inputs: [ + { + name: "spender", + type: "address", + internalType: "address", + }, + { + name: "value", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "balanceOf", + inputs: [ + { + name: "account", + type: "address", + internalType: "address", + }, + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "decimals", + inputs: [], + outputs: [ + { + name: "", + type: "uint8", + internalType: "uint8", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "mint", + inputs: [ + { + name: "amount", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "name", + inputs: [], + outputs: [ + { + name: "", + type: "string", + internalType: "string", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "symbol", + inputs: [], + outputs: [ + { + name: "", + type: "string", + internalType: "string", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "totalSupply", + inputs: [], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "transfer", + inputs: [ + { + name: "to", + type: "address", + internalType: "address", + }, + { + name: "value", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "transferFrom", + inputs: [ + { + name: "from", + type: "address", + internalType: "address", + }, + { + name: "to", + type: "address", + internalType: "address", + }, + { + name: "value", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "event", + name: "Approval", + inputs: [ + { + name: "owner", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "spender", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "value", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "Transfer", + inputs: [ + { + name: "from", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "to", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "value", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "error", + name: "ERC20InsufficientAllowance", + inputs: [ + { + name: "spender", + type: "address", + internalType: "address", + }, + { + name: "allowance", + type: "uint256", + internalType: "uint256", + }, + { + name: "needed", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + type: "error", + name: "ERC20InsufficientBalance", + inputs: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "balance", + type: "uint256", + internalType: "uint256", + }, + { + name: "needed", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + type: "error", + name: "ERC20InvalidApprover", + inputs: [ + { + name: "approver", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "ERC20InvalidReceiver", + inputs: [ + { + name: "receiver", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "ERC20InvalidSender", + inputs: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "ERC20InvalidSpender", + inputs: [ + { + name: "spender", + type: "address", + internalType: "address", + }, + ], + }, + ], + inheritedFunctions: { + allowance: "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol", + approve: "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol", + balanceOf: "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol", + decimals: "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol", + name: "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol", + symbol: "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol", + totalSupply: "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol", + transfer: "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol", + transferFrom: "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol", + }, + }, + MockToken2: { + address: "0x3626DEff4AFB3Acd8f217288cd33FBE3a337Ce0B", + abi: [ + { + type: "constructor", + inputs: [ + { + name: "name", + type: "string", + internalType: "string", + }, + { + name: "symbol", + type: "string", + internalType: "string", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "allowance", + inputs: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + { + name: "spender", + type: "address", + internalType: "address", + }, + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "approve", + inputs: [ + { + name: "spender", + type: "address", + internalType: "address", + }, + { + name: "value", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "balanceOf", + inputs: [ + { + name: "account", + type: "address", + internalType: "address", + }, + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "decimals", + inputs: [], + outputs: [ + { + name: "", + type: "uint8", + internalType: "uint8", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "mint", + inputs: [ + { + name: "amount", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "name", + inputs: [], + outputs: [ + { + name: "", + type: "string", + internalType: "string", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "symbol", + inputs: [], + outputs: [ + { + name: "", + type: "string", + internalType: "string", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "totalSupply", + inputs: [], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "transfer", + inputs: [ + { + name: "to", + type: "address", + internalType: "address", + }, + { + name: "value", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "transferFrom", + inputs: [ + { + name: "from", + type: "address", + internalType: "address", + }, + { + name: "to", + type: "address", + internalType: "address", + }, + { + name: "value", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "event", + name: "Approval", + inputs: [ + { + name: "owner", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "spender", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "value", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "Transfer", + inputs: [ + { + name: "from", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "to", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "value", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "error", + name: "ERC20InsufficientAllowance", + inputs: [ + { + name: "spender", + type: "address", + internalType: "address", + }, + { + name: "allowance", + type: "uint256", + internalType: "uint256", + }, + { + name: "needed", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + type: "error", + name: "ERC20InsufficientBalance", + inputs: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "balance", + type: "uint256", + internalType: "uint256", + }, + { + name: "needed", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + type: "error", + name: "ERC20InvalidApprover", + inputs: [ + { + name: "approver", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "ERC20InvalidReceiver", + inputs: [ + { + name: "receiver", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "ERC20InvalidSender", + inputs: [ + { + name: "sender", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "ERC20InvalidSpender", + inputs: [ + { + name: "spender", + type: "address", + internalType: "address", + }, + ], + }, + ], + inheritedFunctions: { + allowance: "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol", + approve: "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol", + balanceOf: "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol", + decimals: "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol", + name: "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol", + symbol: "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol", + totalSupply: "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol", + transfer: "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol", + transferFrom: "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol", + }, + }, ConstantSumFactory: { address: "0x69221a99e5Bc30E0cf891992e958E3Ba3815bfc6", abi: [