diff --git a/src/PoolFunder.sol b/src/PoolFunder.sol new file mode 100644 index 0000000..ed211b3 --- /dev/null +++ b/src/PoolFunder.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.24; + +import {Address} from "oz/utils/Address.sol"; +import {IERC20} from "oz/token/ERC20/IERC20.sol"; + +interface BalancerV2Vault { + enum JoinKind { + INIT, + EXACT_TOKENS_IN_FOR_BPT_OUT, + TOKEN_IN_FOR_EXACT_BPT_OUT, + ALL_TOKENS_IN_FOR_EXACT_BPT_OUT + } + + struct JoinPoolRequest { + address[] assets; + uint256[] maxAmountsIn; + bytes userData; + bool fromInternalBalance; + } + + function joinPool( + bytes32 poolId, + address sender, + address recipient, + JoinPoolRequest calldata request + ) external; + + function getPoolTokens(bytes32 poolId) + external + view + returns (address[] memory, uint256[] memory, uint256); +} + +interface Pool { + function getPoolId() external view returns (bytes32); +} + +contract PoolFunder { + using Address for address; + + BalancerV2Vault public immutable vault; + address public immutable factory; + address public immutable gyd; + + constructor(address vault_, address gyd_, address factory_) { + gyd = gyd_; + vault = BalancerV2Vault(vault_); + factory = factory_; + } + + function fundPool(uint256 gydAmount, bytes calldata creationData) external { + bytes memory result = factory.functionCall(creationData); + address pool = abi.decode(result, (address)); + bytes32 poolId = Pool(pool).getPoolId(); + (address[] memory assets,,) = vault.getPoolTokens(poolId); + uint256[] memory maxAmountsIn = new uint256[](assets.length); + + for (uint256 i = 0; i < assets.length; i++) { + if (assets[i] == gyd) { + maxAmountsIn[i] = gydAmount; + IERC20(gyd).approve(address(vault), gydAmount); + break; + } + } + + bytes memory userData = + abi.encode(BalancerV2Vault.JoinKind.INIT, maxAmountsIn); + vault.joinPool( + poolId, + address(this), + address(this), + BalancerV2Vault.JoinPoolRequest({ + assets: assets, + maxAmountsIn: maxAmountsIn, + userData: userData, + fromInternalBalance: false + }) + ); + } +} diff --git a/test/PoolFunder.t.sol b/test/PoolFunder.t.sol new file mode 100644 index 0000000..898966d --- /dev/null +++ b/test/PoolFunder.t.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-3.0 +pragma solidity ^0.8.24; + +import {Test, stdStorage, StdStorage} from "forge-std/Test.sol"; + +import {IERC20} from "oz/token/ERC20/IERC20.sol"; + +import {PoolFunder} from "../src/PoolFunder.sol"; + +contract PoolFunderTest is Test { + using stdStorage for StdStorage; + + address public constant BALANCER_VAULT_ADDRSS = + 0xBA12222222228d8Ba445958a75a0704d566BF2C8; + address public constant GYD_ADDRESS = + 0xCA5d8F8a8d49439357d3CF46Ca2e720702F132b8; + address public constant ECLP_FACTORY = + 0x5d3Be8aaE57bf0D1986Ff7766cC9607B6cC99b89; + bytes public poolCreationData = + hex"f680d973000000000000000000000000000000000000000000000000000000000000036000000000000000000000000000000000000000000000000000000000000003a000000000000000000000000000000000000000000000000000000000000003e00000000000000000000000000000000000000000000000000ddb6275b03a40000000000000000000000000000000000000000000000000000de60af19e8dc00000000000000000000000000000000000000000000000000009d025defee4df4400000000000000000000000000000000000000000000000009d025defee4df440000000000000000000000000000000000000000000000d8d726b7177a800000ffffffffffffffffffffffffffffffffb89fa781a6bdbb939ac23e7f4013781b0000000000000000000000000000000017c636bc012fc7792e9b6af84d947b0100000000000000000000000000000000475d9ae35c93e935276528c1b97cc17e0000000000000000000000000000000017ce6f8cfefd33b4196881f8170708fe00000000000000000000000000000000475ef9b0daeb16caf1a74b67152bbc940000000000000000000000000000000017ca532480167d94b273ea6507b104ec0000000000000000000000000000000000041c687ee6b61d751091434b89e119fffffffffffffffffffffffffffffffffffea13281a8d26461305c5f3f76c1d0000000000000000000000000000000004b3b4ca85a86c473e421ffd60063dc80000000000000000000000000000000000000000000000000000000000000044000000000000000000000000000000000000000000000000000005af3107a4000000000000000000000000000efde1c764df6522a068b38106563de166ac97f5800000000000000000000000033acca8f9a9592a9a97604e597649b24f5fca665000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000997c49a9ce8b6c6277719750641874943024e05c0000000000000000000000000000000000000000000000000000000012cc03000000000000000000000000000000000000000000000000000000000000278d00000000000000000000000000000000000000000000000000000000000000001a4779726f73636f70652045434c50204759442f41555344432e65000000000000000000000000000000000000000000000000000000000000000000000000001045434c502d4759442d41555344432e65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000ca5d8f8a8d49439357d3cf46ca2e720702f132b8000000000000000000000000f0e7ec247b918311afa054e0aedb99d74c31b809000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f62fd24941b585b91eb059e0ea1a7e729357511"; + + PoolFunder public funder; + + function setUp() public { + vm.createSelectFork("gnosis", 36_732_356); + funder = new PoolFunder(BALANCER_VAULT_ADDRSS, GYD_ADDRESS, ECLP_FACTORY); + } + + function testFundPool() public { + uint256 amount = 1_000_000e18; + _mintTokensFor(GYD_ADDRESS, address(funder), amount); + + funder.fundPool(amount, poolCreationData); + } + + function _mintTokensFor(address token_, address account_, uint256 amount_) + internal + { + stdstore.target(token_).sig(IERC20(token_).balanceOf.selector).with_key( + account_ + ).checked_write(amount_); + } +}