diff --git a/balancer-js/.gitignore b/balancer-js/.gitignore index f31266ee5..5ef0db4a6 100644 --- a/balancer-js/.gitignore +++ b/balancer-js/.gitignore @@ -10,3 +10,5 @@ cache/ balancer-js.iml temp/ src/contracts/ + +.vscode/* \ No newline at end of file diff --git a/balancer-js/examples/helpers/erc20.ts b/balancer-js/examples/helpers/erc20.ts index 6509b0427..8baac3db7 100644 --- a/balancer-js/examples/helpers/erc20.ts +++ b/balancer-js/examples/helpers/erc20.ts @@ -75,3 +75,12 @@ export const getTokenBalance = async ( const erc20 = new Contract(token, iERC20, provider); return erc20.balanceOf(account); }; + +export const getNativeAssetBalance = async ( + account: string, + provider: JsonRpcProvider +): Promise => { + return BigNumber.from( + await provider.send('eth_getBalance', [account, 'latest']) + ).toString(); +}; diff --git a/balancer-js/examples/pools/aprs/aprs.polygon.ts b/balancer-js/examples/pools/aprs/aprs.polygon.ts index b3c6fd127..6da1fabbd 100644 --- a/balancer-js/examples/pools/aprs/aprs.polygon.ts +++ b/balancer-js/examples/pools/aprs/aprs.polygon.ts @@ -15,7 +15,7 @@ const { pools } = sdk; const main = async () => { const pool = await pools.find( - '0x216690738aac4aa0c4770253ca26a28f0115c595000000000000000000000b2c' + '0xf0ad209e2e969eaaa8c882aac71f02d8a047d5c2000200000000000000000b49' ); if (pool) { diff --git a/balancer-js/examples/pools/aprs/aprs.zkevm.ts b/balancer-js/examples/pools/aprs/aprs.zkevm.ts index c1036d808..ff8ab4b50 100644 --- a/balancer-js/examples/pools/aprs/aprs.zkevm.ts +++ b/balancer-js/examples/pools/aprs/aprs.zkevm.ts @@ -8,14 +8,14 @@ import { BalancerSDK } from '@balancer-labs/sdk'; const sdk = new BalancerSDK({ network: 1101, - rpcUrl: 'https://rpc.ankr.com/polygon_zkevm', + rpcUrl: 'https://zkevm-rpc.com', }); const { pools } = sdk; const main = async () => { const pool = await pools.find( - '0xe1f2c039a68a216de6dd427be6c60decf405762a00000000000000000000000e' + '0x1d0a8a31cdb04efac3153237526fb15cc65a252000000000000000000000000f' ); if (pool) { diff --git a/balancer-js/examples/pools/exit/recovery-exit.ts b/balancer-js/examples/pools/exit/recovery-exit.ts index 38a4f61f0..5ce00391d 100644 --- a/balancer-js/examples/pools/exit/recovery-exit.ts +++ b/balancer-js/examples/pools/exit/recovery-exit.ts @@ -4,62 +4,122 @@ * Run command: * yarn example ./examples/pools/exit/recovery-exit.ts */ +import { FORK_NODES } from '@/test/lib/utils'; import { BalancerSDK, - insert, + removeItem, Network, truncateAddresses, } from '@balancer-labs/sdk'; import { parseEther } from '@ethersproject/units'; import { getTokenBalance, reset, setTokenBalance } from 'examples/helpers'; -async function recoveryExit() { +async function recoveryExitLive() { + const network = Network.MAINNET; + const rpcUrl = FORK_NODES[network]; + const poolId = + '0x20b156776114e8a801e9767d90c6ccccc8adf398000000000000000000000499'; + const userAddress = '0x0000000000000000000000000000000000000000'; + const bptAmount = String(parseEther('1')); + const slippage = '1'; // 1 bps = 0.1% + + const balancer = new BalancerSDK({ + network, + rpcUrl, + }); + const { poolsOnChain, pools } = balancer.data; + + // Use SDK to find pool info + let pool = await pools.find(poolId); + if (!pool) throw 'POOL_DOESNT_EXIST'; + + // Refresh pool data from chain before building and sending tx + pool = await poolsOnChain.refresh(pool); + + // Build transaction + const { expectedAmountsOut, minAmountsOut } = + balancer.pools.buildRecoveryExit({ + pool, + bptAmount, + userAddress, + slippage, + }); + + console.log(expectedAmountsOut.toString()); + console.log(minAmountsOut.toString()); +} + +async function recoveryExitFork() { + const poolId = + '0x20b156776114e8a801e9767d90c6ccccc8adf398000000000000000000000499'; + const blockNo = 17700000; + const balancer = new BalancerSDK({ network: Network.MAINNET, rpcUrl: 'http://127.0.0.1:8545', // Using local fork for simulation }); + const { poolsOnChain, pools } = balancer.data; // Setup exit parameters const signer = balancer.provider.getSigner(); - const address = await signer.getAddress(); - - const poolId = - // '0x50cf90b954958480b8df7958a9e965752f62712400000000000000000000046f'; // bb-e-usd - // '0xd4e7c1f3da1144c9e2cfd1b015eda7652b4a439900000000000000000000046a'; // bb-e-usdc - // '0xa13a9247ea42d743238089903570127dda72fe4400000000000000000000035d'; // bb-a-usd - '0xa718042e5622099e5f0ace4e7122058ab39e1bbe000200000000000000000475'; // 50temple_50bb-e-usd + const userAddress = await signer.getAddress(); - const bptIn = String(parseEther('1')); + const bptAmount = String(parseEther('1')); const slippage = '200'; // 200 bps = 2% // Use SDK to find pool info - const pool = await balancer.pools.find(poolId); + let pool = await pools.find(poolId); if (!pool) throw 'POOL_DOESNT_EXIST'; // Prepare local fork for simulation - await reset(balancer.provider, 17700000); - await setTokenBalance(balancer.provider, address, pool.address, bptIn, 0); + await reset(balancer.provider, blockNo); + await setTokenBalance( + balancer.provider, + userAddress, + pool.address, + bptAmount, + 0 + ); + + // Refresh pool data from chain before building and sending tx + pool = await poolsOnChain.refresh(pool); // Build transaction const { to, data, expectedAmountsOut, minAmountsOut } = - pool.buildRecoveryExit(address, bptIn, slippage); + balancer.pools.buildRecoveryExit({ + pool, + bptAmount, + userAddress, + slippage, + }); // Send transaction await signer.sendTransaction({ to, data }); + // Refresh pool data from chain before building and sending tx + pool = await poolsOnChain.refresh(pool); + + const bptIndex = pool.tokensList.indexOf(pool.address); + const tokensWithoutBpt = + bptIndex === -1 ? pool.tokensList : removeItem(pool.tokensList, bptIndex); // Check balances after transaction to confirm success - const balances = await Promise.all( - pool.tokensList.map((token) => - getTokenBalance(token, address, balancer.provider) - ) - ); + const balances = await Promise.all([ + ...tokensWithoutBpt.map((token) => + getTokenBalance(token, userAddress, balancer.provider) + ), + getTokenBalance(pool.address, userAddress, balancer.provider), + ]); console.table({ - tokensOut: truncateAddresses(pool.tokensList), - minAmountsOut: insert(minAmountsOut, pool.bptIndex, bptIn), - expectedAmountsOut: insert(expectedAmountsOut, pool.bptIndex, bptIn), - amountsOut: balances.map((b) => b.toString()), + tokensOut: truncateAddresses(tokensWithoutBpt), + minAmountsOut: minAmountsOut, + expectedAmountsOut: expectedAmountsOut, + amountsOut: removeItem(balances, balances.length - 1).map((b) => + b.toString() + ), }); + console.log(`BPT Balance: `, balances[balances.length - 1].toString()); } -recoveryExit(); +// recoveryExitFork(); +recoveryExitLive(); diff --git a/balancer-js/examples/pools/volume/volume.avalanche.ts b/balancer-js/examples/pools/volume/volume.avalanche.ts new file mode 100644 index 000000000..c2ee73a88 --- /dev/null +++ b/balancer-js/examples/pools/volume/volume.avalanche.ts @@ -0,0 +1,34 @@ +/** + * Display APRs for pool ids hardcoded under `const ids` + * + * Run command: + * yarn example ./examples/pools/volume/volume.avalanche.ts + */ +import { BalancerSDK } from '@balancer-labs/sdk'; + +const sdk = new BalancerSDK({ + network: 43114, + rpcUrl: 'https://avalanche.public-rpc.com', +}); + +const { pools } = sdk; + +const main = async () => { + const pool = await pools.find( + '0xa1d14d922a575232066520eda11e27760946c991000000000000000000000012' + ); + + if (pool) { + const volume = await pools.volume(pool); + console.table([ + { + id: pool.id, + type: pool.poolType, + totalVolume: pool.totalSwapVolume, + volume, + }, + ]); + } +}; + +main(); diff --git a/balancer-js/hardhat.config.avalanche.ts b/balancer-js/hardhat.config.avalanche.ts new file mode 100644 index 000000000..0c9632aca --- /dev/null +++ b/balancer-js/hardhat.config.avalanche.ts @@ -0,0 +1,12 @@ +import '@nomiclabs/hardhat-ethers'; + +/** + * @type import('hardhat/config').HardhatUserConfig + */ +export default { + networks: { + hardhat: { + chainId: 43114, + }, + }, +}; diff --git a/balancer-js/package.json b/balancer-js/package.json index 704026d4e..64fe16b3f 100644 --- a/balancer-js/package.json +++ b/balancer-js/package.json @@ -1,6 +1,6 @@ { "name": "@balancer-labs/sdk", - "version": "1.1.3", + "version": "1.1.4", "description": "JavaScript SDK for interacting with the Balancer Protocol V2", "license": "GPL-3.0-only", "homepage": "https://github.com/balancer-labs/balancer-sdk#readme", @@ -35,6 +35,7 @@ "node:arbitrum": "npx hardhat --tsconfig tsconfig.testing.json --config hardhat.config.arbitrum.ts node --fork $(. ./.env && echo $ALCHEMY_URL_ARBITRUM) --port 8161", "node:gnosis": "npx hardhat --tsconfig tsconfig.testing.json --config hardhat.config.gnosis.ts node --fork $(. ./.env && echo $RPC_URL_GNOSIS) --port 8100", "node:zkevm": "npx hardhat --tsconfig tsconfig.testing.json --config hardhat.config.zkevm.ts node --fork $(. ./.env && echo $ALCHEMY_URL_ZKEVM) --port 8101", + "node:avalanche": "npx hardhat --tsconfig tsconfig.testing.json --config hardhat.config.avalanche.ts node --fork $(. ./.env && echo $RPC_URL_AVALANCHE) --port 8114", "typechain:generate": "npx typechain --target ethers-v5 --out-dir src/contracts './src/lib/abi/*.json'" }, "devDependencies": { @@ -87,7 +88,7 @@ "typescript": "^4.0.2" }, "dependencies": { - "@balancer-labs/sor": "4.1.1-beta.13", + "@balancer-labs/sor": "^4.1.1-beta.16", "@ethersproject/abi": "^5.4.0", "@ethersproject/abstract-signer": "^5.4.0", "@ethersproject/address": "^5.4.0", diff --git a/balancer-js/src/lib/abi/Authoriser.json b/balancer-js/src/lib/abi/Authoriser.json new file mode 100644 index 000000000..f71580394 --- /dev/null +++ b/balancer-js/src/lib/abi/Authoriser.json @@ -0,0 +1,342 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "admin", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "previousAdminRole", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "newAdminRole", + "type": "bytes32" + } + ], + "name": "RoleAdminChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleGranted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "RoleRevoked", + "type": "event" + }, + { + "inputs": [], + "name": "DEFAULT_ADMIN_ROLE", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "actionId", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "canPerform", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleAdmin", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "getRoleMember", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + } + ], + "name": "getRoleMemberCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32[]", + "name": "roles", + "type": "bytes32[]" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "grantRoles", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32[]", + "name": "roles", + "type": "bytes32[]" + }, + { + "internalType": "address[]", + "name": "accounts", + "type": "address[]" + } + ], + "name": "grantRolesToMany", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "hasRole", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "renounceRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "role", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRole", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32[]", + "name": "roles", + "type": "bytes32[]" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "revokeRoles", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32[]", + "name": "roles", + "type": "bytes32[]" + }, + { + "internalType": "address[]", + "name": "accounts", + "type": "address[]" + } + ], + "name": "revokeRolesFromMany", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/balancer-js/src/lib/abi/GyroConfig.json b/balancer-js/src/lib/abi/GyroConfig.json new file mode 100644 index 000000000..d36ba2957 --- /dev/null +++ b/balancer-js/src/lib/abi/GyroConfig.json @@ -0,0 +1,324 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "key", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "previousValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newValue", + "type": "uint256" + } + ], + "name": "ConfigChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "key", + "type": "bytes32" + } + ], + "name": "ConfigFrozen", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "bytes32", + "name": "key", + "type": "bytes32" + } + ], + "name": "ConfigUnset", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "GovernorChangeRequested", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "oldGovernor", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "GovernorChanged", + "type": "event" + }, + { + "inputs": [], + "name": "acceptGovernance", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newGovernor", + "type": "address" + } + ], + "name": "changeGovernor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "key", + "type": "bytes32" + } + ], + "name": "freeze", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "key", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "defaultValue", + "type": "address" + } + ], + "name": "getAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "key", + "type": "bytes32" + } + ], + "name": "getAddress", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "key", + "type": "bytes32" + } + ], + "name": "getConfigMeta", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + }, + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "key", + "type": "bytes32" + } + ], + "name": "getUint", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "governor", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "key", + "type": "bytes32" + } + ], + "name": "hasKey", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_governor", + "type": "address" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "listKeys", + "outputs": [ + { + "internalType": "bytes32[]", + "name": "", + "type": "bytes32[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pendingGovernor", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "key", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "newValue", + "type": "address" + } + ], + "name": "setAddress", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "key", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "newValue", + "type": "uint256" + } + ], + "name": "setUint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "key", + "type": "bytes32" + } + ], + "name": "unset", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/balancer-js/src/lib/constants/addresses.json b/balancer-js/src/lib/constants/addresses.json index 1157c4d6b..2e70ee6fc 100644 --- a/balancer-js/src/lib/constants/addresses.json +++ b/balancer-js/src/lib/constants/addresses.json @@ -473,6 +473,61 @@ }, "tokens": {} }, + "8453": { + "contracts": { + "aaveLinearPoolFactory": "0x687b8c9b41e01be8b591725fac5d5f52d0564d79", + "authorizer": "0x809b79b53f18e9bc08a961ed4678b901ac93213a", + "authorizerAdaptor": "0x6cad2ea22bfa7f4c14aae92e47f510cd5c509bc7", + "authorizerAdaptorEntrypoint": "0x9129e834e15ea19b6069e8f08a8ecfc13686b8dc", + "authorizerWithAdaptorValidation": "0xa69e0ccf150a29369d8bbc0b3f510849db7e8eee", + "bal": "0x7c6b91d9be155a6db01f749217d76ff02a7227f2", + "balancerHelpers": "0x8e9aa87e45e92bad84d5f8dd1bff34fb92637de9", + "balancerQueries": "0x300ab2038eac391f26d9f895dc61f8f66a548833", + "balancerRelayer": "0x76f7204b62f554b79d444588edac9dfa7032c71a", + "batchRelayerLibrary": "0xdf9b5b00ef9bca66e9902bd813db14e4343be025", + "childChainGauge": "0x59562f93c447656f6e4799fc1fc7c3d977c3324f", + "childChainGaugeFactory": "0x2498a2b0d6462d2260eac50ae1c3e03f4829ba95", + "circuitBreakerLib": "0xef454a7b3f965d3f6723e462405246f8cd865425", + "composableStablePoolFactory": "0x8df317a729fcaa260306d7de28888932cb579b88", + "erc4626LinearPoolFactory": "0x161f4014c27773840ccb4ec1957113e6dd028846", + "externalWeightedMath": "0x7920bfa1b2041911b354747ca7a6cdd2dfc50cfd", + "gaugeWorkingBalanceHelper": "0xa7d524046ef89de9f8e4f2d7b029f66ccb738d48", + "gearboxLinearPoolFactory": "0x9dd32684176638d977883448a4c914311c07bd62", + "l2BalancerPseudoMinter": "0xc7e5ed1054a24ef31d827e6f86caa58b3bc168d7", + "l2LayerZeroBridgeForwarder": "0x8ea89804145c007e7d226001a96955ad53836087", + "managedPoolAddRemoveTokenLib": "0xb19382073c7a0addbb56ac6af1808fa49e377b75", + "managedPoolAmmLib": "0x7d2248f194755dca9a1887099394f39476d28c9a", + "managedPoolFactory": "0x9a62c91626d39d0216b3959112f9d4678e20134d", + "mockAaveLendingPool": "0x88ed12a90142fdbfe2a28f7d5b48927254c7e760", + "mockAaveLinearPool": "0x712e23a9b91aa30ee7997d1c7a1a285d4c7912d1", + "mockComposableStablePool": "0x7f6e9d6a4093af9d09bae92d24bfe42fc5369ae6", + "mockErc4626LinearPool": "0x1cc2e10c486ffb7228810b9c7c183d0cd92cdf44", + "mockErc4626Token": "0x4638ab64022927c9bd5947607459d13f57f1551c", + "mockGearboxDieselToken": "0xb521dd5c8e13fe202626cac98873fea2b7760ce4", + "mockGearboxLinearPool": "0x3c28a59356b473847aaa5e0b6f561636079213a6", + "mockGearboxVault": "0x79e435875ccee3cd9e8da23fe34f9a011d05ea6c", + "mockLiquidityBootstrappingPool": "", + "mockManagedPool": "0xe50ad96af9370d05d3c1ce85f17c31557b29c4ee", + "mockStaticAToken": "0x698caed853be9cea96c268f565e2b61d3b2bcda4", + "mockWeightedPool": "0xd9dbbfaa703f3c33838365ae00fe4eb22cdf8d46", + "mockYearnLinearPool": "0x369877467fc3b6774de4e11e0c0abbde70eb40fd", + "mockYearnTokenVault": "0xdbf7b9f1d2bfba14e42709f84dda3187ee410e38", + "noProtocolFeeLiquidityBootstrappingPoolFactory": "0x0c6052254551eae3ecac77b01dfcf1025418828f", + "nullVotingEscrow": "0x475d18169be8a89357a9ee3ab00ca386d20fa229", + "poolRecoveryHelper": "0x03f3fb107e74f2eac9358862e91ad3c692712054", + "protocolFeePercentagesProvider": "0xded7fef7d8ecdcb74f22f0169e1a9ec696e6695d", + "protocolFeesCollector": "0xce88686553686da562ce7cea497ce749da109f9f", + "protocolFeesWithdrawer": "0xacf05be5134d64d150d153818f8c67ee36996650", + "protocolIdRegistry": "0x682f0ddbfd41d1272982f64a499fb62d80e27589", + "recoveryModeHelper": "0x313a8d36b1d90a4cba3a505fdc3480c3870be053", + "vault": "0xba12222222228d8ba445958a75a0704d566bf2c8", + "votingEscrowDelegationProxy": "0xd87f44df0159dc78029ab9ca7d7e57e7249f5acd", + "weightedPoolFactory": "0x4c32a8a8fda4e24139b51b456b42290f51d6a1c4", + "weth": "0x4200000000000000000000000000000000000006", + "yearnLinearPoolFactory": "0x44d33798dddcdabc93fe6a40c80588033dc502d3" + }, + "tokens": {} + }, "42161": { "contracts": { "aaveLinearPoolFactory": "0x7396f99b48e7436b152427bfa3dd6aa8c7c6d05b", diff --git a/balancer-js/src/lib/constants/config.ts b/balancer-js/src/lib/constants/config.ts index 4f8c2da0e..b0d77fca5 100644 --- a/balancer-js/src/lib/constants/config.ts +++ b/balancer-js/src/lib/constants/config.ts @@ -18,7 +18,10 @@ export const BALANCER_NETWORK_CONFIG: Record = { poolDataQueries: '0xf5CDdF6feD9C589f1Be04899F48f9738531daD59', lidoRelayer: '0xdcdbf71A870cc60C6F9B621E28a7D3Ffd6Dd4965', veBal: '0xC128a9954e6c874eA3d62ce62B468bA073093F25', + gaugeControllerCheckpointer: + '0x8e5698dc4897dc12243c8642e77b4f21349db97c', veBalProxy: '0x6f5a2eE11E7a772AeB5114A20d0D7c0ff61EB8A0', + gyroConfigProxy: '0xac89cc9d78bbad7eb3a02601b4d65daa1f908aa6', ...addressesByNetwork[Network.MAINNET].contracts, }, tokens: { @@ -86,6 +89,7 @@ export const BALANCER_NETWORK_CONFIG: Record = { multicall: '0xa1B2b503959aedD81512C37e9dce48164ec6a94d', poolDataQueries: '0x84813aA3e079A665C0B80F944427eE83cBA63617', gaugeClaimHelper: '0xaeb406b0e430bf5ea2dc0b9fe62e4e53f74b3a33', + gyroConfigProxy: '0xfdc2e9e03f515804744a40d0f8d25c16e93fbe67', ...addressesByNetwork[Network.POLYGON].contracts, }, tokens: { @@ -171,6 +175,10 @@ export const BALANCER_NETWORK_CONFIG: Record = { address: '0x82aF49447D8a07e3bd95BD0d56f35241523fBab1', }, ], + sorTriPathMidPoolIds: [ + '0x178e029173417b1f9c8bc16dcec6f697bc323746000200000000000000000158', // wstEth/USDC.e to open up auraBAL/USDC + '0x0052688295413b32626d226a205b95cdb337de860002000000000000000003d1', // arb/USDC.e to open up aura/USDC + ], }, [Network.GOERLI]: { chainId: Network.GOERLI, //5 @@ -219,6 +227,7 @@ export const BALANCER_NETWORK_CONFIG: Record = { contracts: { multicall: '0x2dc0e2aa608532da689e89e237df582b783e552c', poolDataQueries: '0x6B5dA774890Db7B7b96C6f44e6a4b0F657399E2e', + gyroConfigProxy: '0x32acb44fc929339b9f16f0449525cc590d2a23f3', ...addressesByNetwork[Network.OPTIMISM].contracts, }, tokens: { @@ -432,6 +441,8 @@ export const BALANCER_NETWORK_CONFIG: Record = { 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-avalanche-v2', gaugesSubgraph: 'https://api.thegraph.com/subgraphs/name/balancer-labs/balancer-gauges-avalanche', + blockNumberSubgraph: + 'https://api.thegraph.com/subgraphs/name/iliaazhel/avalanche-blocks', }, thirdParty: { coingecko: { @@ -441,7 +452,58 @@ export const BALANCER_NETWORK_CONFIG: Record = { }, pools: {}, poolsToIgnore: [], - sorConnectingTokens: [], + sorConnectingTokens: [ + { + symbol: 'WAVAX', + address: '0xb31f66aa3c1e785363f0875a1b74e27b85fd66c7', + }, + { + symbol: 'sAVAX', + address: '0x2b2c81e08f1af8835a78bb2a90ae924ace0ea4be', + }, + ], + }, + [Network.BASE]: { + chainId: Network.BASE, //8453 + addresses: { + contracts: { + balancerMinter: '0xc7E5ED1054A24Ef31D827E6F86caA58B3Bc168d7', + multicall: '0xcA11bde05977b3631167028862bE2a173976CA11', + poolDataQueries: '', + ...addressesByNetwork[Network.BASE].contracts, + }, + tokens: { + bal: addressesByNetwork[Network.BASE].contracts.bal, + wrappedNativeAsset: addressesByNetwork[Network.BASE].contracts.weth, + ...addressesByNetwork[Network.BASE].tokens, + }, + }, + urls: { + subgraph: + 'https://api.studio.thegraph.com/query/24660/balancer-base-v2/version/latest', + gaugesSubgraph: + 'https://api.studio.thegraph.com/query/24660/balancer-gauges-base/version/latest', + blockNumberSubgraph: + 'https://api.studio.thegraph.com/query/48427/bleu-base-blocks/version/latest', + }, + thirdParty: { + coingecko: { + nativeAssetId: 'eth', + platformId: 'base', + }, + }, + averageBlockTime: 2, + pools: {}, + poolsToIgnore: [], + sorConnectingTokens: [ + { + symbol: 'weth', + address: '0x4200000000000000000000000000000000000006', + }, + ], + sorTriPathMidPoolIds: [ + '0x2db50a0e0310723ef0c2a165cb9a9f80d772ba2f00020000000000000000000d', // weth/staBal + ], }, }; diff --git a/balancer-js/src/lib/constants/network.ts b/balancer-js/src/lib/constants/network.ts index 87ea07a22..b26c025e9 100644 --- a/balancer-js/src/lib/constants/network.ts +++ b/balancer-js/src/lib/constants/network.ts @@ -6,6 +6,7 @@ export enum Network { GNOSIS = 100, POLYGON = 137, FANTOM = 250, + BASE = 8453, ZKEVM = 1101, ARBITRUM = 42161, AVALANCHE = 43114, diff --git a/balancer-js/src/lib/constants/poolsToIgnore.ts b/balancer-js/src/lib/constants/poolsToIgnore.ts new file mode 100644 index 000000000..774f3b5cc --- /dev/null +++ b/balancer-js/src/lib/constants/poolsToIgnore.ts @@ -0,0 +1,335 @@ +export const poolsToIgnore = [ + '0x00c2a4be503869fa751c2dbcb7156cc970b5a8da000000000000000000000477', + '0x02d928e68d8f10c0358566152677db51e1e2dc8c00000000000000000000051e', + '0x04248aabca09e9a1a3d5129a7ba05b7f17de768400000000000000000000050e', + '0x05513ca725b6ce035ca2641075474eb469f05f4c00020000000000000000041f', + '0x0a0fb4ff697de5ac5b6770cd8ee1b72af80b57cf000000000000000000000496', + '0x0afbd58beca09545e4fb67772faf3858e610bcd00000000000000000000004b9', + '0x0d05aac44ac7dd3c7ba5d50be93eb884a057d23400000000000000000000051c', + '0x11839d635e2f0270da37e8ef4324d4d5d54329570002000000000000000004d8', + '0x126e7643235ec0ab9c103c507642dc3f4ca23c66000000000000000000000468', + '0x133d241f225750d2c92948e464a5a80111920331000000000000000000000476', + '0x159cb00338fb63f263fd6f621df619cef71da9540000000000000000000004d5', + '0x173063a30e095313eee39411f07e95a8a806014e0002000000000000000003ab', + '0x1bd2f176a812e312077bca87e37c08432bb09f3e0000000000000000000005a1', + '0x20b156776114e8a801e9767d90c6ccccc8adf398000000000000000000000499', + '0x246ffb4d928e394a02e45761fecdba6c2e79b8eb000000000000000000000541', + '0x25accb7943fd73dda5e23ba6329085a3c24bfb6a000200000000000000000387', + '0x26c2b83fc8535deead276f5cc3ad9c1a2192e02700020000000000000000056b', + '0x2b218683178d029bab6c9789b1073aa6c96e517600000000000000000000058c', + '0x2ba7aa2213fa2c909cd9e46fed5a0059542b36b00000000000000000000003a3', + '0x2bbf681cc4eb09218bee85ea2a5d3d13fa40fc0c0000000000000000000000fd', + '0x2e52c64fd319e380cdbcfc4577ea1fda558a32e40002000000000000000005ba', + '0x2f4eb100552ef93840d5adc30560e5513dfffacb000000000000000000000334', + '0x2ff1a9dbdacd55297452cfd8a4d94724bc22a5f7000000000000000000000484', + '0x3035917be42af437cbdd774be26b9ec90a2bd677000200000000000000000543', + '0x331d50e0b00fc1c32742f151e56b9b616227e23e00000000000000000000047c', + '0x334c96d792e4b26b841d28f53235281cec1be1f200020000000000000000038a', + '0x335d1709d4da9aca59d16328db5cd4ea66bfe06b0000000000000000000004d6', + '0x395d8a1d9ad82b5abe558f8abbfe183b27138af40000000000000000000004e5', + '0x3bb22fc9033b802f2ac47c18885f63476f158afc000000000000000000000483', + '0x3c640f0d3036ad85afa2d5a9e32be651657b874f00000000000000000000046b', + '0x3cdae4f12a67ba563499e102f309c73213cb241c000000000000000000000335', + '0x3dbb8d974b82e82ce79c20c0f5995f4f1f533ede000000000000000000000470', + '0x3f7a7fd7f214be45ec26820fd01ac3be4fc75aa70002000000000000000004c5', + '0x3fcb7085b8f2f473f80bf6d879cae99ea4de934400000000000000000000056d', + '0x41503c9d499ddbd1dcdf818a1b05e9774203bf46000000000000000000000594', + '0x4228290ee9cab692938ff0b4ba303fbcdb68e9f200020000000000000000057d', + '0x454ed96955d04d2f5cdd05e0fd1c77975bfe5307000000000000000000000410', + '0x481c5fc05d63a58aa2f0f2aa417c021b5d419cb200000000000000000000056a', + '0x483006684f422a9448023b2382615c57c5ecf18f000000000000000000000488', + '0x4a82b580365cff9b146281ab72500957a849abdc000000000000000000000494', + '0x4c81255cc9ed7062180ea99962fe05ac0d57350b0000000000000000000005a3', + '0x4c8d2e60863e8d7e1033eda2b3d84e92a641802000000000000000000000040f', + '0x4cbde5c4b4b53ebe4af4adb85404725985406163000000000000000000000595', + '0x4ce0bd7debf13434d3ae127430e9bd4291bfb61f00020000000000000000038b', + '0x4ce277df0feb5b4d07a0ca2adcf5326e4005239d000000000000000000000518', + '0x4fd4687ec38220f805b6363c3c1e52d0df3b5023000200000000000000000473', + '0x4fd63966879300cafafbb35d157dc5229278ed230000000000000000000000e9', + '0x50cf90b954958480b8df7958a9e965752f62712400000000000000000000046f', + '0x53bc3cba3832ebecbfa002c12023f8ab1aa3a3a0000000000000000000000411', + '0x5a6a8cffb4347ff7fc484bf5f0f8a2e234d34255000200000000000000000275', + '0x5b3240b6be3e7487d61cd1afdfc7fe4fa1d81e6400000000000000000000037b', + '0x60683b05e9a39e3509d8fdb9c959f23170f8a0fa000000000000000000000489', + '0x60d604890feaa0b5460b28a424407c24fe89374a0000000000000000000004fc', + '0x639883476960a23b38579acfd7d71561a0f408cf000200000000000000000505', + '0x652d486b80c461c397b0d95612a404da936f3db30000000000000000000000e7', + '0x6667c6fa9f2b3fc1cc8d85320b62703d938e43850000000000000000000004fb', + '0x6a1eb2e9b45e772f55bd9a34659a04b6f75da68700000000000000000000040d', + '0x6c56e72c551b5ac4bf54a620a76077ca768c8fe40002000000000000000004da', + '0x70b7d3b3209a59fb0400e17f67f3ee8c37363f4900020000000000000000018f', + '0x7337224d59cb16c2dc6938cd45a7b2c60c865d6a0000000000000000000004d4', + '0x74cbfaf94a3577c539a9dcee9870a6349a33b34f000000000000000000000534', + '0x779d01f939d78a918a3de18cc236ee89221dfd4e0000000000000000000004c7', + '0x7b50775383d3d6f0215a8f290f2c9e2eebbeceb20000000000000000000000fe', + '0x804cdb9116a10bb78768d3252355a1b18067bf8f0000000000000000000000fb', + '0x813e3fe1761f714c502d1d2d3a7cceb33f37f59d00000000000000000000040c', + '0x82698aecc9e28e9bb27608bd52cf57f704bd1b83000000000000000000000336', + '0x8a6b25e33b12d1bb6929a8793961076bd1f9d3eb0002000000000000000003e8', + '0x8e6ec57a822c2f527f2df7c7d7d361df3e7530a1000000000000000000000498', + '0x8f4063446f5011bc1c9f79a819efe87776f23704000000000000000000000197', + '0x9001cbbd96f54a658ff4e6e65ab564ded76a543100000000000000000000050a', + '0x9210f1204b5a24742eba12f710636d76240df3d00000000000000000000000fc', + '0x9516a2d25958edb8da246a320f2c7d94a0dbe25d000000000000000000000519', + '0x959216bb492b2efa72b15b7aacea5b5c984c3cca000200000000000000000472', + '0x968024662b9566b42d78af23a0f441bc8723fa83000200000000000000000418', + '0x99c88ad7dc566616548adde8ed3effa730eb6c3400000000000000000000049a', + '0x9b1c8407a360443a9e5eca004713e4088fab8ac0000000000000000000000497', + '0x9b692f571b256140a39a34676bffa30634c586e100000000000000000000059d', + '0x9d7f992c900fbea0ec314bdd71b7cc1becf76a33000200000000000000000573', + '0x9fb771d530b0ceba5160f7bfe2dd1e8b8aa1340300000000000000000000040e', + '0xa13a9247ea42d743238089903570127dda72fe4400000000000000000000035d', + '0xa1697f9af0875b63ddc472d6eebada8c1fab85680000000000000000000004f9', + '0xa3823e50f20982656557a4a6a9c06ba5467ae9080000000000000000000000e6', + '0xa718042e5622099e5f0ace4e7122058ab39e1bbe000200000000000000000475', + '0xa8b103a10a94f4f2d7ed2fdcd5545e807557330700000000000000000000048e', + '0xac5b4ef7ede2f2843a704e96dcaa637f4ba3dc3f00000000000000000000051d', + '0xac976bb42cb0c85635644e8c7c74d0e0286aa61c0000000000000000000003cb', + '0xae37d54ae477268b9997d4161b96b8200755935c000000000000000000000337', + '0xae8535c23afedda9304b03c68a3563b75fc8f92b0000000000000000000005a0', + '0xb0f75e97a114a4eb4a425edc48990e6760726709000000000000000000000198', + '0xb5e3de837f869b0248825e0175da73d4e8c3db6b000200000000000000000474', + '0xb841b062ea8ccf5c4cb78032e91de4ae875560420002000000000000000005b7', + '0xb9bd68a77ccf8314c0dfe51bc291c77590c4e9e6000200000000000000000385', + '0xbb6881874825e60e1160416d6c426eae65f2459e000000000000000000000592', + '0xbc0f2372008005471874e426e86ccfae7b4de79d000000000000000000000485', + '0xbf2ef8bdc2fc0f3203b3a01778e3ec5009aeef3300000000000000000000058d', + '0xbfa413a2ff0f20456d57b643746133f54bfe0cd20000000000000000000004c3', + '0xc2b021133d1b0cf07dba696fd5dd89338428225b000000000000000000000598', + '0xc443c15033fcb6cf72cc24f1bda0db070ddd9786000000000000000000000593', + '0xc50d4347209f285247bda8a09fc1c12ce42031c3000000000000000000000590', + '0xc5dc1316ab670a2eed5716d7f19ced321191f38200000000000000000000056e', + '0xc8c79fcd0e859e7ec81118e91ce8e4379a481ee6000000000000000000000196', + '0xcaa052584b462198a5a9356c28bce0634d65f65c0000000000000000000004db', + '0xcbfa4532d8b2ade2c261d3dd5ef2a2284f7926920000000000000000000004fa', + '0xcfae6e251369467f465f13836ac8135bd42f8a56000000000000000000000591', + '0xd4e7c1f3da1144c9e2cfd1b015eda7652b4a439900000000000000000000046a', + '0xd6e355036f41dc261b3f1ed3bbc6003e87aadb4f000000000000000000000495', + '0xd7edb56f63b2a0191742aea32df1f98ca81ed9c600000000000000000000058e', + '0xd997f35c9b1281b82c8928039d14cddab5e13c2000000000000000000000019c', + '0xdba274b4d04097b90a72b62467d828cefd708037000000000000000000000486', + '0xdc063deafce952160ec112fa382ac206305657e60000000000000000000004c4', + '0xdec02e6642e2c999af429f5ce944653cad15e093000000000000000000000469', + '0xe03af00fabe8401560c1ff7d242d622a5b601573000000000000000000000493', + '0xe0fcbf4d98f0ad982db260f86cf28b49845403c5000000000000000000000504', + '0xe2d16b0a39f3fbb4389a0e8f1efcbecfb3d1e6e10000000000000000000005a7', + '0xe4dc3c1998ac693d68f4c77476d7c815694c3e94000200000000000000000416', + '0xe6bcc79f328eec93d4ec8f7ed35534d9ab549faa0000000000000000000000e8', + '0xe8c56405bc405840154d9b572927f4197d110de10000000000000000000005a4', + '0xeb486af868aeb3b6e53066abc9623b1041b42bc000000000000000000000046c', + '0xeb567dde03f3da7fe185bdacd5ab495ab220769d000000000000000000000548', + '0xec3626fee40ef95e7c0cbb1d495c8b67b34d398300000000000000000000053d', + '0xf22ff21e17157340575158ad7394e068048dd98b0000000000000000000004b8', + '0xf57c794f42da72b38c8f610ff3b5e8502e48cbde00000000000000000000055c', + '0xf71d0774b214c4cf51e33eb3d30ef98132e4dbaa00000000000000000000046e', + '0xfa24a90a3f2bbe5feea92b95cd0d14ce709649f900000000000000000000058f', + '0xfd11ccdbdb7ab91cb9427a6d6bf570c95876d1950000000000000000000004c2', + '0xfebb0bbf162e64fb9d0dfe186e517d84c395f016000000000000000000000502', + '0xfef969638c52899f91781f1be594af6f40b99bad00000000000000000000047b', + '0x02e139d53ebf4033bf78ab66c6a1e7f1f204487f0002000000000000000009f9', + '0x03090a9811181a2afe830a3a0b467698ccf3a8b1000000000000000000000bf5', + '0x0320c1c5b6df19a194d48882aaec1c72940081d9000000000000000000000a7d', + '0x04b54ea92d73de2d62d651db7d9778f0c49157d8000200000000000000000ba2', + '0x0503dd6b2d3dd463c9bef67fb5156870af63393e00000000000000000000042e', + '0x0889b240a5876aae745ac19f1771853671dc5d36000000000000000000000b3f', + '0x0bc54e914f53f98d16035f4f0d948f3e09c2fac0000200000000000000000bac', + '0x0c06e87c7b88d998f645b91c1f53b51294b12bca000100000000000000000bb9', + '0x10b040038f87219d9b42e025e3bd9b8095c87dd9000000000000000000000b11', + '0x117a3d474976274b37b7b94af5dcade5c90c6e85000000000000000000000aca', + '0x11884da90fb4221b3aa288a7741c51ec4fc43b2f000000000000000000000a5f', + '0x1379b816b9be611431d693290289c204720ca56d000100000000000000000b6f', + '0x150e7b885bdfce974f2abe88a72fdbd692175c6f0002000000000000000009fd', + '0x178e029173417b1f9c8bc16dcec6f697bc323746000000000000000000000758', + '0x1aafc31091d93c3ff003cff5d2d8f7ba2e7284250000000000000000000003b3', + '0x216690738aac4aa0c4770253ca26a28f0115c595000000000000000000000b2c', + '0x216d6db0c28204014618482c369d7fbf0a8f3232000100000000000000000b60', + '0x230ecdb2a7cee56d6889965a023aa0473d6da507000000000000000000000bf3', + '0x252ff6a3a6fd7b5e8e999de8e3f5c3b306ed1401000200000000000000000bec', + '0x25e57f4612912614e6c99616bd2abb9b5ae71e99000000000000000000000bf0', + '0x2645b13fd2c5295296e94a76280b968bdcbbdfed000000000000000000000c11', + '0x284eb68520c8fa83361c1a3a5910aec7f873c18b000000000000000000000ac9', + '0x2c8dbe8eb86135d9f2f26d196748c088d47f73e7000200000000000000000a29', + '0x31bccf9e28b94e5dacebaa67fe8bc1603cecd904000000000000000000000a01', + '0x341068a547c3cde3c09e338714010dd01b32f93f000200000000000000000a34', + '0x3db543faf7a92052de7860c5c9debabee59ed5bd000000000000000000000a62', + '0x3dd0843a028c86e0b760b1a76929d1c5ef93a2dd00000000000000000000070d', + '0x3efb91c4f9b103ee45885695c67794591916f34e000200000000000000000b43', + '0x402cfdb7781fa85d52f425352661128250b79e12000000000000000000000be3', + '0x43894de14462b421372bcfe445fa51b1b4a0ff3d000000000000000000000b36', + '0x4739e50b59b552d490d3fdc60d200977a38510c0000000000000000000000b10', + '0x48e6b98ef6329f8f0a30ebb8c7c960330d64808500000000000000000000075b', + '0x4a0b73f0d13ff6d43e304a174697e3d5cfd310a400020000000000000000091c', + '0x4a77ef015ddcd972fd9ba2c7d5d658689d090f1a000000000000000000000b38', + '0x4ae3661afa119892f0cc8c43edaf6a94989ac171000000000000000000000c06', + '0x4ccb966d8246240afb7a1a24628efb930870b1c40002000000000000000009fc', + '0x52cc8389c6b93d740325729cc7c958066cee4262000000000000000000000b0f', + '0x5b77107fcdf2b41903bab2bc555d4fc14cf7667d000000000000000000000b32', + '0x5bae72b75caab1f260d21bc028c630140607d6e8000000000000000000000ac6', + '0x600bd01b6526611079e12e1ff93aba7a3e34226f0000000000000000000009e4', + '0x63ce19ccd39930725b8a3d2733627804718ab83d000000000000000000000bf2', + '0x64efad69f099813021b41f4cac6e749fd55e188f000000000000000000000b39', + '0x6933ec1ca55c06a894107860c92acdfd2dd8512f000000000000000000000428', + '0x6abe4e7a497b8293c258389c1b00d177e4f257ed00010000000000000000080d', + '0x6c8c7fc50247a47038015eb1fd5dc105d05dafba000200000000000000000ba0', + '0x7079a25dec33be61bbd81b2fb69b468e80d3e72c0000000000000000000009ff', + '0x71bd10c2a590b5858f5576550c163976a48af906000000000000000000000b27', + '0x7c82a23b4c48d796dee36a9ca215b641c6a8709d000000000000000000000acd', + '0x7f4f4942f2a14b6ab7b08b10ada1aacede4ee8d4000200000000000000000b44', + '0x86aef31951e0a3a54333bd9e72f9a95587d058c5000200000000000000000912', + '0x882c7a84231484b3e9f3fd45ac04b1eb5d35b076000200000000000000000a91', + '0x894c82800526e0391e709c0983a5aea3718b7f6d000000000000000000000ac5', + '0x89b28a9494589b09dbccb69911c189f74fdadc5a000000000000000000000b33', + '0x89bb15076c9f2d86aa98ec6cffc1a71e31c38953000000000000000000000bf1', + '0x89f1146fee52b5d9166e9c83cc388b6d8f69f1380001000000000000000009e7', + '0x8a819a4cabd6efcb4e5504fe8679a1abd831dd8f00000000000000000000042d', + '0x8b58a1e7fff52001c22386c2918d45938a6a9be30001000000000000000008d9', + '0x8b8225bfedebaf1708c55743acb4ad43fd4d0f21000200000000000000000918', + '0x8fbd0f8e490735cfc3abf4f29cbddd5c3289b9a7000000000000000000000b5b', + '0x8fd39252d683fdb60bddd4df4b53c9380b496d59000200000000000000000b45', + '0x9321e2250767d79bab5aa06daa8606a2b3b7b4c5000000000000000000000bf4', + '0x949a12b95ec5b80c375b98963a5d6b33b0d0efff0002000000000000000009fe', + '0x9a020bdc2faff5bd24c6acc2020d01ff9f2c627a000000000000000000000ae2', + '0x9cf9358300e34bf9373d30129a1e718d8d058b54000200000000000000000913', + '0x9e34631547adcf2f8cefa0f5f223955c7b137571000000000000000000000ad5', + '0xa5a935833f6a5312715f182733eab088452335d7000100000000000000000bee', + '0xa5fe91dde37d8bf2dacacc0168b115d28ed03f84000000000000000000000b35', + '0xa8bf1c584519be0184311c48adbdc4c15cb2e8c1000000000000000000000bf6', + '0xab269164a10fab22bc87c39946da06c870b172d6000000000000000000000bfc', + '0xac2cae8d2f78a4a8f92f20dbe74042cd0a8d5af3000000000000000000000be2', + '0xae646817e458c0be890b81e8d880206710e3c44e000000000000000000000acb', + '0xaef2c171dbe64b0c18977e16e70bfd29d4ee0256000000000000000000000ac8', + '0xb0c830dceb4ef55a60192472c20c8bf19df03488000000000000000000000be1', + '0xb266ac3b7c98d7bcb28731dac0ef42dba1b276be000000000000000000000be4', + '0xb371aa09f5a110ab69b39a84b5469d29f9b22b76000000000000000000000b37', + '0xb3d658d5b95bf04e2932370dd1ff976fe18dd66a000000000000000000000ace', + '0xb54b2125b711cd183edd3dd09433439d5396165200000000000000000000075e', + '0xb59be8f3c85a9dd6e2899103b6fbf6ea405b99a4000000000000000000000b34', + '0xb878ecce26838fbba4f78cb5b791a0e09152c067000000000000000000000427', + '0xb973ca96a3f0d61045f53255e319aedb6ed4924000000000000000000000042f', + '0xbd4e35784c832d0f9049b54cb3609e5907c5b495000100000000000000000b14', + '0xc55ec796a4debe625d95436a3531f4950b11bdcf000000000000000000000b3e', + '0xc7e6389e364f4275eb442ef215ed21877028e2af000000000000000000000ac7', + '0xc83b55bbd005f1f84906545fcdb145dee53523e0000200000000000000000b30', + '0xcb21a9e647c95127ed784626485b3104cb28d0e7000000000000000000000425', + '0xd00f9ca46ce0e4a63067c4657986f0167b0de1e5000000000000000000000b42', + '0xd2f3b9e67c69762dd1c88f1d3dadd1649a190761000200000000000000000bf7', + '0xd4accb350f9cf59fe3cf7a5ee6ed9ace6a568ea9000200000000000000000b75', + '0xda1cd1711743e57dd57102e9e61b75f3587703da000000000000000000000acc', + '0xdae301690004946424e41051ace1791083be42a1000000000000000000000b40', + '0xde0a77ab6689b980c30306b10f9131a007e1af81000200000000000000000ba1', + '0xe051605a83deae38d26a7346b100ef1ac2ef8a0b0000000000000000000003ce', + '0xe1fb90d0d3b47e551d494d7ebe8f209753526b01000000000000000000000ac4', + '0xe2272cddb2cc408e79e02a73d1db9acc24a843d5000200000000000000000ba7', + '0xe2dc0e0f2c358d6e31836dee69a558ab8d1390e70000000000000000000009fa', + '0xe4885ed2818cc9e840a25f94f9b2a28169d1aea7000000000000000000000b29', + '0xe6909c2f18a29d97217a6146f045e1780606991f000100000000000000000bfe', + '0xe78b25c06db117fdf8f98583cdaaa6c92b79e917000000000000000000000b2b', + '0xea11645ac7d8f2def94c9d8d86bd766296c9b6b6000000000000000000000b3a', + '0xeb480dbbdd921cd6c359e4cc4c65ddea6395e2a1000200000000000000000946', + '0xed35f28f837e96f81240ebb82e0e3f518c7e8a2f000100000000000000000bb5', + '0xf0211cceebe6fcc45052b4e57ee95d233f5669d2000100000000000000000c01', + '0xf22a66046b5307842f21b311ecb4c462c24c0635000000000000000000000b15', + '0xf28f17be00f8ca3c9b7f66a4aad5513757fb3341000200000000000000000b5a', + '0xf42ed61450458ee4620f5ef4f29adb25a6ef0fb6000000000000000000000bf8', + '0xf48f01dcb2cbb3ee1f6aab0e742c2d3941039d56000000000000000000000445', + '0xf93579002dbe8046c43fefe86ec78b1112247bb8000000000000000000000759', + '0xf984eb2b8a7ef780245a797a2fccd82f346409ca000000000000000000000a59', + '0xfa2c0bd8327c99db5bde4c9e9e5cbf30946351bb000000000000000000000948', + '0xff4ce5aaab5a627bf82f4a571ab1ce94aa365ea600000000000000000000075a', + '0x1ac55c31dac78ca943cb8ebfca5945ce09e036e2000000000000000000000024', + '0x225e0047671939a8d78e08ebd692788abe63f15c000000000000000000000009', + '0x41211bba6d37f5a74b22e667533f080c7c7f3f1300000000000000000000000b', + '0x4de21b365d6543661d0e105e579a34b963862497000200000000000000000045', + '0x581ec1f5e7ced12b186deae32256adb53bdd5b08000000000000000000000001', + '0x66f33ae36dd80327744207a48122f874634b3ada000100000000000000000013', + '0xa3ed6f78edc29f69df8a0d16b1d1ccf9871f918c000000000000000000000032', + '0xa611a551b95b205ccd9490657acf7899daee5db700000000000000000000002e', + '0xb95829adbacd8af89e291dee78bc09e24de51d6b000000000000000000000043', + '0xb973ca96a3f0d61045f53255e319aedb6ed49240000200000000000000000011', + '0xba1a5b19d09a79dada039b1f974015c5a989d5fd000100000000000000000046', + '0xbb9cd48d33033f5effbedec9dd700c7d7e1dcf5000000000000000000000000e', + '0xd16f72b02da5f51231fde542a8b9e2777a478c8800000000000000000000000f', + '0xd4015683b8153666190e0b2bec352580ebc4caca00000000000000000000000d', + '0xe15cac1df3621e001f76210ab12a7f1a1691481f000000000000000000000044', + '0xe7f88d7d4ef2eb18fcf9dd7216ba7da1c46f3dd600000000000000000000000a', + '0xf48f01dcb2cbb3ee1f6aab0e742c2d3941039d56000200000000000000000012', + '0xfedb19ec000d38d92af4b21436870f115db22725000000000000000000000010', + '0xffff76a3280e95dc855696111c2562da09db2ac000000000000000000000000c', + '0x00fcd3d55085e998e291a0005cedecf58ac14c4000020000000000000000047f', + '0x077794c30afeccdf5ad2abc0588e8cee7197b71a000000000000000000000352', + '0x117a3d474976274b37b7b94af5dcade5c90c6e85000000000000000000000381', + '0x11884da90fb4221b3aa288a7741c51ec4fc43b2f000000000000000000000353', + '0x19b1c92631405a0a9495ccba0becf4f2e8e908bd000000000000000000000410', + '0x1e550b7764da9638fdd32c8a701364de31f45ee800000000000000000000047c', + '0x1fa7f727934226aedab636d62a084931b97d366b000000000000000000000411', + '0x23ca0306b21ea71552b148cf3c4db4fc85ae19290000000000000000000000c9', + '0x284eb68520c8fa83361c1a3a5910aec7f873c18b000000000000000000000380', + '0x2a96254ca32020b20ed3506f8f75318da24709f9000200000000000000000456', + '0x36942963e3b6f37ecc45a4e72349558514233f0000000000000000000000048a', + '0x3f53a862919ccfa023cb6ace91378a79fb0f6bf500000000000000000000040f', + '0x40af308e3d07ec769d85eb80afb116525ff4ac99000000000000000000000485', + '0x418de00ae109e6f874d872658767866d680eaa1900000000000000000000047d', + '0x45c4d1376943ab28802b995acffc04903eb5223f000000000000000000000470', + '0x4689122d360c4725d244c5cfea22861333d862e6000100000000000000000468', + '0x4739e50b59b552d490d3fdc60d200977a38510c0000000000000000000000409', + '0x49a0e3334496442a9706e481617724e7e37eaa080000000000000000000003ff', + '0x519cce718fcd11ac09194cff4517f12d263be067000000000000000000000382', + '0x52cc8389c6b93d740325729cc7c958066cee4262000000000000000000000408', + '0x567ecfcb22205d279bb8eed3e066989902bf03d5000000000000000000000452', + '0x585d95df0231fa08aeee35ff0c16b92fd0ecdc3300020000000000000000045f', + '0x5a7f39435fd9c381e4932fa2047c9a5136a5e3e7000000000000000000000400', + '0x5bae72b75caab1f260d21bc028c630140607d6e8000000000000000000000350', + '0x6cb787a419c3e6ee2e9ff365856c29cd10659113000000000000000000000474', + '0x7c82a23b4c48d796dee36a9ca215b641c6a8709d000000000000000000000406', + '0x81fc12c60ee5b753cf5fd0adc342dfb5f3817e3200000000000000000000035d', + '0x894c82800526e0391e709c0983a5aea3718b7f6d00000000000000000000034f', + '0x970712708a08e8fb152be4d81b2dc586923f5369000200000000000000000479', + '0x9bf7c3b63c77b4b4f2717776f15a4bec1b532a280000000000000000000000c8', + '0x9cebf13bb702f253abf1579294694a1edad00eaa000000000000000000000486', + '0x9e34631547adcf2f8cefa0f5f223955c7b137571000000000000000000000407', + '0x9fb7d6dcac7b6aa20108bad226c35b85a9e31b63000200000000000000000412', + '0xa1ea76c42b2938cfa9abea12357881006c52851300000000000000000000048f', + '0xa50f89e9f439fde2a6fe05883721a00475da3c4500000000000000000000048b', + '0xa612b6aed2e7ca1a3a4f23fbca9128461bbb7718000000000000000000000274', + '0xa8af146d79ac0bb981e4e0d8b788ec5711b1d5d000000000000000000000047b', + '0xad28940024117b442a9efb6d0f25c8b59e1c950b00000000000000000000046f', + '0xae646817e458c0be890b81e8d880206710e3c44e00000000000000000000039d', + '0xaef2c171dbe64b0c18977e16e70bfd29d4ee0256000000000000000000000351', + '0xbbf9d705b75f408cfcaee91da32966124d2c6f7d00000000000000000000047e', + '0xbd724eb087d4cc0f61a5fed1fffaf937937e14de000000000000000000000473', + '0xbe0f30217be1e981add883848d0773a86d2d2cd4000000000000000000000471', + '0xc46be4b8bb6b5a3d3120660efae9c5416318ed40000000000000000000000472', + '0xc69771058481551261709d8db44977e9afde645000010000000000000000042a', + '0xc6eee8cb7643ec2f05f46d569e9ec8ef8b41b389000000000000000000000475', + '0xcba9ff45cfb9ce238afde32b0148eb82cbe635620000000000000000000003fd', + '0xcf8b555b7754556cf2ac2165e77ee23ed8517d7900020000000000000000045e', + '0xd0dc20e6342db2de82692b8dc842301ff9121805000200000000000000000454', + '0xd3d5d45f4edf82ba0dfaf061d230766032a10e07000200000000000000000413', + '0xd6d20527c7b0669989ee082b9d3a1c63af742290000000000000000000000483', + '0xda1cd1711743e57dd57102e9e61b75f3587703da0000000000000000000003fc', + '0xe1fb90d0d3b47e551d494d7ebe8f209753526b0100000000000000000000034e', + '0xee02583596aee94cccb7e8ccd3921d955f17982a00000000000000000000040a', + '0xf984eb2b8a7ef780245a797a2fccd82f346409ca00000000000000000000034d', + '0xff8f84e8c87532af96aef5582ee451572233678b000200000000000000000478', + '0x054e7b0c73e1ee5aed6864fa511658fc2b54bcaa000000000000000000000015', + '0x3f1a2c4a3a751f6626bd90ef16e104f0772d4d6b00020000000000000000001b', + '0x7275c131b1f67e8b53b4691f92b0e35a4c1c6e22000000000000000000000010', + '0xa154009870e9b6431305f19b09f9cfd7284d4e7a000000000000000000000013', + '0xa1d14d922a575232066520eda11e27760946c991000000000000000000000012', + '0xa826a114b0c7db4d1ff4a4be845a78998c64564c000000000000000000000008', + '0xea67626e1f0b59e0d172a04f5702ef90bcdf440c00000000000000000000000f', + '0xeb496161099d45b3ea4892408ef745c6182eb56e00000000000000000000000e', + '0xece571847897fd61e764d455dc15cf1cd9de8d6f000000000000000000000014', + '0xed3e2f496cbcd8e212192fb8d1499842f04a0d19000000000000000000000009', + '0x02c9dcb975262719a61f9b40bdf0987ead9add3a000000000000000000000006', + '0x16c9a4d841e88e52b51936106010f27085a529ec00000000000000000000000c', + '0x32be2d0ddeaf3333501b24a28668ce373ba8e763000200000000000000000014', + '0x32f03464fdf909fdf3798f87ff3712b10c59bd86000000000000000000000005', + '0x4b718e0e2fea1da68b763cd50c446fba03ceb2ea00000000000000000000000b', + '0x68a69c596b3839023c0e08d09682314f582314e5000200000000000000000011', + '0x6f34a44fce1506352a171232163e7716dd073ade000200000000000000000015', + '0x9e2d87f904862671eb49cb358e74284762cc9f42000200000000000000000013', + '0xac4b72c01072a52b73ca71105504f1372efcce0d000000000000000000000003', + '0xbfd65c6160cfd638a85c645e6e6d8acac5dac935000000000000000000000004', + '0xe274c9deb6ed34cfe4130f8d0a8a948dea5bb28600000000000000000000000d', +]; diff --git a/balancer-js/src/lib/utils/aaveHelpers.ts b/balancer-js/src/lib/utils/aaveHelpers.ts deleted file mode 100644 index 147e63f66..000000000 --- a/balancer-js/src/lib/utils/aaveHelpers.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { JsonRpcProvider } from '@ethersproject/providers'; -import { Contract } from '@ethersproject/contracts'; -import { StaticATokenRateProvider__factory } from '@/contracts'; - -export class AaveHelpers { - static async getRate( - rateProviderAddress: string, - provider: JsonRpcProvider - ): Promise { - const rateProviderContract = new Contract( - rateProviderAddress, - StaticATokenRateProvider__factory.createInterface(), - provider - ); - - const rate = await rateProviderContract.getRate(); - return rate.toString(); - } -} diff --git a/balancer-js/src/lib/utils/index.ts b/balancer-js/src/lib/utils/index.ts index 146e8401c..efff5d1d8 100644 --- a/balancer-js/src/lib/utils/index.ts +++ b/balancer-js/src/lib/utils/index.ts @@ -4,7 +4,6 @@ import { Log, TransactionReceipt } from '@ethersproject/providers'; import { Interface, LogDescription } from '@ethersproject/abi'; import { Logger } from '@/lib/utils/logger'; -export * from './aaveHelpers'; export * from './assetHelpers'; export * from './errors'; export * from './permit'; diff --git a/balancer-js/src/lib/utils/logger.ts b/balancer-js/src/lib/utils/logger.ts index 6b322d04b..9712d8602 100644 --- a/balancer-js/src/lib/utils/logger.ts +++ b/balancer-js/src/lib/utils/logger.ts @@ -35,4 +35,16 @@ export class Logger { console.error(`[ERROR] ${message}`); } } + + time(message: string): void { + if (this.enableLogging) { + console.time(`[TIME] ${message}`); + } + } + + timeEnd(message: string): void { + if (this.enableLogging) { + console.timeEnd(`[TIME] ${message}`); + } + } } diff --git a/balancer-js/src/modules/contracts/contracts.module.ts b/balancer-js/src/modules/contracts/contracts.module.ts index b7920bbd2..08a0331ce 100644 --- a/balancer-js/src/modules/contracts/contracts.module.ts +++ b/balancer-js/src/modules/contracts/contracts.module.ts @@ -23,6 +23,8 @@ import { GaugeClaimHelper__factory, GearboxLinearPoolFactory, GearboxLinearPoolFactory__factory, + GyroConfig, + GyroConfig__factory, LidoRelayer, LidoRelayer__factory, LiquidityGaugeV5__factory, @@ -54,6 +56,7 @@ export interface ContractInstances { eulerLinearPoolFactory?: EulerLinearPoolFactory; gaugeClaimHelper?: GaugeClaimHelper; gearboxLinearPoolFactory?: GearboxLinearPoolFactory; + gyroConfig?: GyroConfig; lidoRelayer?: LidoRelayer; liquidityGauge: ContractFactory; multicall: Multicall; @@ -173,6 +176,14 @@ export class Contracts { provider ); } + let gyroConfig: undefined | GyroConfig; + if (this.contractAddresses.gyroConfigProxy) { + gyroConfig = GyroConfig__factory.connect( + this.contractAddresses.gyroConfigProxy, + provider + ); + } + this.instances = { aaveLinearPoolFactory, balancerHelpers, @@ -183,6 +194,7 @@ export class Contracts { eulerLinearPoolFactory, gaugeClaimHelper, gearboxLinearPoolFactory, + gyroConfig, liquidityGauge: this.getLiquidityGauge, lidoRelayer, multicall, diff --git a/balancer-js/src/modules/data/fee-distributor/repository.ts b/balancer-js/src/modules/data/fee-distributor/repository.ts index c704ce595..c371ff7b6 100644 --- a/balancer-js/src/modules/data/fee-distributor/repository.ts +++ b/balancer-js/src/modules/data/fee-distributor/repository.ts @@ -35,9 +35,9 @@ const veBalInterface = new Interface([ 'function totalSupply() view returns (uint)', ]); -const bbAUsdInterface = new Interface([ - 'function getRate() view returns (uint)', -]); +// const bbAUsdInterface = new Interface([ +// 'function getRate() view returns (uint)', +// ]); export class FeeDistributorRepository implements BaseFeeDistributor { feeDistributor: Contract; @@ -75,10 +75,11 @@ export class FeeDistributorRepository implements BaseFeeDistributor { target: this.veBalAddress, callData: veBalInterface.encodeFunctionData('totalSupply', []), }, - { - target: this.bbAUsdAddress, - callData: bbAUsdInterface.encodeFunctionData('getRate', []), - }, + // Removed while wrappers are broken + // { + // target: this.bbAUsdAddress, + // callData: bbAUsdInterface.encodeFunctionData('getRate', []), + // }, ]; const [, res] = await this.multicall.callStatic.aggregate(payload); @@ -86,7 +87,8 @@ export class FeeDistributorRepository implements BaseFeeDistributor { balAmount: parseFloat(formatUnits(res[0], 18)), bbAUsdAmount: parseFloat(formatUnits(res[1], 18)), veBalSupply: parseFloat(formatUnits(res[2], 18)), - bbAUsdPrice: parseFloat(formatUnits(res[3], 18)), + // bbAUsdPrice: parseFloat(formatUnits(res[3], 18)), + bbAUsdPrice: parseFloat('1.0'), balAddress: this.balAddress, }; diff --git a/balancer-js/src/modules/data/gauge-controller/multicall.spec.ts b/balancer-js/src/modules/data/gauge-controller/multicall.spec.ts index 62e5043c4..71510ab09 100644 --- a/balancer-js/src/modules/data/gauge-controller/multicall.spec.ts +++ b/balancer-js/src/modules/data/gauge-controller/multicall.spec.ts @@ -8,7 +8,8 @@ describe('Gauge controller', () => { const { contracts } = new Contracts(1, provider); const fetcher = new GaugeControllerMulticallRepository( contracts.multicall, - '0xc128468b7ce63ea702c1f104d55a2566b13d3abd' + '0xc128468b7ce63ea702c1f104d55a2566b13d3abd', + '0x8e5698dc4897dc12243c8642e77b4f21349db97c' ); it('is fetching relative weights for current period', async () => { diff --git a/balancer-js/src/modules/data/gauge-controller/multicall.ts b/balancer-js/src/modules/data/gauge-controller/multicall.ts index 4eee738c1..77295cddb 100644 --- a/balancer-js/src/modules/data/gauge-controller/multicall.ts +++ b/balancer-js/src/modules/data/gauge-controller/multicall.ts @@ -7,23 +7,40 @@ const gaugeControllerInterface = new Interface([ 'function gauge_relative_weight(address gauge, uint timestamp) view returns (uint)', ]); +const gaugeControllerCheckpointerInterface = new Interface([ + 'function gauge_relative_weight(address gauge) view returns (uint)', +]); + export class GaugeControllerMulticallRepository { constructor( private multicall: Multicall, - private gaugeControllerAddress: string + private gaugeControllerAddress: string, + private gaugeControllerCheckpointerAddress?: string ) {} async getRelativeWeights( gaugeAddresses: string[], timestamp?: number ): Promise<{ [gaugeAddress: string]: number }> { - const payload = gaugeAddresses.map((gaugeAddress) => ({ - target: this.gaugeControllerAddress, - callData: gaugeControllerInterface.encodeFunctionData( - 'gauge_relative_weight', - [getAddress(gaugeAddress), timestamp || Math.floor(Date.now() / 1000)] - ), - })); + const payload = gaugeAddresses.map((gaugeAddress) => { + // The checkpointer only exists for mainnet, if the network is a testnet, it'll use the regular gauge controller + if (this.gaugeControllerCheckpointerAddress && !timestamp) { + return { + target: this.gaugeControllerCheckpointerAddress, + callData: gaugeControllerCheckpointerInterface.encodeFunctionData( + 'gauge_relative_weight', + [getAddress(gaugeAddress)] + ), + }; + } + return { + target: this.gaugeControllerAddress, + callData: gaugeControllerInterface.encodeFunctionData( + 'gauge_relative_weight', + [getAddress(gaugeAddress), timestamp || Math.floor(Date.now() / 1000)] + ), + }; + }); const [, res] = await this.multicall.callStatic.aggregate(payload); const weights = gaugeAddresses.reduce( diff --git a/balancer-js/src/modules/data/gyro-config/repository.ts b/balancer-js/src/modules/data/gyro-config/repository.ts new file mode 100644 index 000000000..a9edbd8aa --- /dev/null +++ b/balancer-js/src/modules/data/gyro-config/repository.ts @@ -0,0 +1,91 @@ +import { formatBytes32String } from '@ethersproject/strings'; +import { keccak256 } from '@ethersproject/solidity'; +import { defaultAbiCoder } from '@ethersproject/abi'; +import { BigNumber, formatFixed } from '@ethersproject/bignumber'; +import { Provider } from '@ethersproject/providers'; +import { GyroConfig, GyroConfig__factory, Multicall } from '@/contracts'; +import { GyroConfigInterface } from '@/contracts/GyroConfig'; + +export interface GyroConfigRepository { + getGyroProtocolFee(poolAddress: string): Promise; +} + +const protocolFeePercKey = formatBytes32String('PROTOCOL_SWAP_FEE_PERC'); +const gyroPoolTypeKey = formatBytes32String('E-CLP'); +const encodedPoolTypeKey = keccak256( + ['bytes'], + [ + defaultAbiCoder.encode( + ['bytes32', 'bytes32'], + [protocolFeePercKey, gyroPoolTypeKey] + ), + ] +); + +export class GyroConfigRepositoryImpl implements GyroConfigRepository { + gyroConfigInterface: GyroConfigInterface; + gyroConfig: GyroConfig; + + constructor( + private gyroConfigAddress: string, + private multicall: Multicall, + provider: Provider + ) { + this.gyroConfigInterface = GyroConfig__factory.createInterface(); + this.gyroConfig = GyroConfig__factory.connect(gyroConfigAddress, provider); + } + + async getGyroProtocolFee(poolAddress: string): Promise { + let fee; + const encodedPoolSpecificKey = keccak256( + ['bytes'], + [ + defaultAbiCoder.encode( + ['bytes32', 'uint256'], + [protocolFeePercKey, poolAddress] + ), + ] + ); + const payload = [ + { + target: this.gyroConfigAddress, + callData: this.gyroConfigInterface.encodeFunctionData('hasKey', [ + encodedPoolSpecificKey, + ]), + }, + { + target: this.gyroConfigAddress, + callData: this.gyroConfigInterface.encodeFunctionData('hasKey', [ + encodedPoolTypeKey, + ]), + }, + { + target: this.gyroConfigAddress, + callData: this.gyroConfigInterface.encodeFunctionData('hasKey', [ + protocolFeePercKey, + ]), + }, + ]; + const [, [hasSpecificKeyHex, hasPoolTypeKeyHex, hasDefaultKeyHex]] = + await this.multicall.callStatic.aggregate(payload); + const hasSpecificKey = BigNumber.from(hasSpecificKeyHex).eq(1); + const hasPoolTypeKey = BigNumber.from(hasPoolTypeKeyHex).eq(1); + const hasDefaultKey = BigNumber.from(hasDefaultKeyHex).eq(1); + if (hasSpecificKey) { + fee = parseFloat( + formatFixed(await this.gyroConfig.getUint(encodedPoolSpecificKey), 18) + ); + } else if (hasPoolTypeKey) { + fee = parseFloat( + formatFixed(await this.gyroConfig.getUint(encodedPoolTypeKey), 18) + ); + } else if (hasDefaultKey) { + fee = parseFloat( + formatFixed(await this.gyroConfig.getUint(protocolFeePercKey), 18) + ); + } else { + fee = 0; + } + return fee; + } +} diff --git a/balancer-js/src/modules/data/index.ts b/balancer-js/src/modules/data/index.ts index 04fb521b5..c7b83dae0 100644 --- a/balancer-js/src/modules/data/index.ts +++ b/balancer-js/src/modules/data/index.ts @@ -1,3 +1,5 @@ +import { GyroConfigRepositoryImpl } from '@/modules/data/gyro-config/repository'; + export * as balEmissions from './bal/emissions'; export * from './gauge-controller/multicall'; export * from './gauge-shares'; @@ -67,6 +69,7 @@ export class Data implements BalancerDataRepositories { tokenYields; blockNumbers; poolJoinExits; + gyroConfigRepository; constructor( networkConfig: BalancerNetworkConfig, @@ -196,7 +199,8 @@ export class Data implements BalancerDataRepositories { networkConfig.urls.gaugesSubgraph, contracts.contracts.multicall, networkConfig.addresses.contracts.gaugeController || '', - networkConfig.chainId + networkConfig.chainId, + networkConfig.addresses.contracts.gaugeControllerCheckpointer ); } @@ -229,5 +233,13 @@ export class Data implements BalancerDataRepositories { } this.tokenYields = new TokenYieldsRepository(); + + if (networkConfig.addresses.contracts.gyroConfigProxy) { + this.gyroConfigRepository = new GyroConfigRepositoryImpl( + networkConfig.addresses.contracts.gyroConfigProxy, + contracts.contracts.multicall, + provider + ); + } } } diff --git a/balancer-js/src/modules/data/liquidity-gauges/provider.ts b/balancer-js/src/modules/data/liquidity-gauges/provider.ts index e74480562..126b01559 100644 --- a/balancer-js/src/modules/data/liquidity-gauges/provider.ts +++ b/balancer-js/src/modules/data/liquidity-gauges/provider.ts @@ -43,12 +43,14 @@ export class LiquidityGaugeSubgraphRPCProvider subgraphUrl: string, multicall: Multicall, gaugeControllerAddress: string, - private chainId: Network + private chainId: Network, + gaugeControllerCheckpointerAddress?: string ) { if (gaugeControllerAddress) { this.gaugeController = new GaugeControllerMulticallRepository( multicall, - gaugeControllerAddress + gaugeControllerAddress, + gaugeControllerCheckpointerAddress ); } this.multicall = new LiquidityGaugesMulticallRepository(multicall, chainId); @@ -71,6 +73,7 @@ export class LiquidityGaugeSubgraphRPCProvider '0x22625eedd92c81a219a83e1dc48f88d54786b017', // Polygon '0x6817149cb753bf529565b4d023d7507ed2ff4bc0', // Arbitrum '0x83e443ef4f9963c77bd860f94500075556668cb8', // Gnosis + '0x2498a2b0d6462d2260eac50ae1c3e03f4829ba95', // zkEVM ]; const childGaugeAddresses = gauges diff --git a/balancer-js/src/modules/data/pool/balancer-api.ts b/balancer-js/src/modules/data/pool/balancer-api.ts index f89ad2618..8f3ec9dca 100644 --- a/balancer-js/src/modules/data/pool/balancer-api.ts +++ b/balancer-js/src/modules/data/pool/balancer-api.ts @@ -14,8 +14,16 @@ interface PoolsBalancerAPIOptions { query?: GraphQLQuery; } +const MAX_POOLS_PER_REQUEST = 1000; const DEFAULT_SKIP = 0; const DEFAULT_FIRST = 10; +const CHECK_TIMEOUT_SECONDS = 10; +const CHECK_INTERVAL_MS = 10; +const MAX_CHECKS = (CHECK_TIMEOUT_SECONDS * 1000) / CHECK_INTERVAL_MS; + +function timeout(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} /** * Access pools using the Balancer GraphQL Api. @@ -30,6 +38,8 @@ export class PoolsBalancerAPIRepository public skip = 0; // Keep track of how many pools to skip on next fetch, so this functions similar to subgraph repository. public nextToken: string | undefined | null; // A token to pass to the next query to retrieve the next page of results. Undefined initially, null when there are no more results. private query: GraphQLQuery; + private hasFetched = false; + private isFetching = false; constructor(options: PoolsBalancerAPIOptions) { this.client = new BalancerAPIClient(options.url, options.apiKey); @@ -62,34 +72,32 @@ export class PoolsBalancerAPIRepository delete this.query.args.skip; } - private fetchFromCache(options?: PoolsRepositoryFetchOptions): Pool[] { - const first = options?.first || DEFAULT_FIRST; - const skip = options?.skip || DEFAULT_SKIP; - + private fetchFromCache(first: number, skip: number): Pool[] { const pools = this.pools.slice(skip, first + skip); - this.skip = skip + first; return pools; } async fetch(options?: PoolsRepositoryFetchOptions): Promise { - if ( - this.nextToken === null || - this.pools.length > - (options?.first || DEFAULT_FIRST) + (options?.skip || DEFAULT_SKIP) - ) { - return this.fetchFromCache(options); + const first = options?.first || DEFAULT_FIRST; + const skip = options?.skip || DEFAULT_SKIP; + + if (!this.hasFetched) { + this.fetchAll(options); } + await this.awaitEnoughPoolsFetched(first, skip); + return this.fetchFromCache(first, skip); + } + + // Fetches all pools from the API in a loop and saves them to the cache. + async fetchAll(options?: PoolsRepositoryFetchOptions): Promise { + this.isFetching = true; + this.hasFetched = true; if (this.nextToken) { this.query.args.nextToken = this.nextToken; } - if (options?.first) { - // We need to request more than they specified because filtering is done post limit - // e.g. if we ask for 10 we may get 7 because 3 were filtered out. - this.query.args.first = options.first * 2; - } - + this.query.args.first = MAX_POOLS_PER_REQUEST; const formattedArgs = new GraphQLArgsBuilder(this.query.args).format( new BalancerAPIArgsFormatter() ); @@ -109,9 +117,27 @@ export class PoolsBalancerAPIRepository this.nextToken = apiResponseData.nextToken; this.pools = this.pools.concat(apiResponseData.pools.map(this.format)); - this.skip = this.pools.length; - return this.fetchFromCache(options); + if (this.nextToken) return await this.fetchAll(options); + + this.isFetching = false; + } + + // A function that waits until enough pools have been loaded into the cache + // or fetching has finished. Used so that all pools can be fetched in the + // background, while fetch returns the first results to the user quickly. + async awaitEnoughPoolsFetched(first: number, skip: number): Promise { + for (let totalChecks = 0; totalChecks < MAX_CHECKS; totalChecks++) { + if (this.pools.length > first + skip) { + return; + } + if (!this.isFetching) { + return; + } + await timeout(CHECK_INTERVAL_MS); + } + + return; } async find(id: string): Promise { diff --git a/balancer-js/src/modules/data/pool/subgraph.ts b/balancer-js/src/modules/data/pool/subgraph.ts index 4f5e5c688..cfc963b7a 100644 --- a/balancer-js/src/modules/data/pool/subgraph.ts +++ b/balancer-js/src/modules/data/pool/subgraph.ts @@ -15,6 +15,7 @@ import { GraphQLQuery, Pool } from '@/types'; import { Network } from '@/lib/constants/network'; import { PoolsQueryVariables } from '../../subgraph/subgraph'; import { mapType } from './subgraph-helpers'; +import { Logger } from '@/lib/utils/logger'; interface PoolsSubgraphRepositoryOptions { url: string; @@ -76,7 +77,8 @@ export class PoolsSubgraphRepository * @returns Promise resolving to pools list */ private async fetchAllPools(): Promise { - console.time('fetching pools'); + const logger = Logger.getInstance(); + logger.time('fetching pools'); if (this.blockHeight) { this.query.args.block = { number: await this.blockHeight() }; @@ -88,7 +90,7 @@ export class PoolsSubgraphRepository const { pool0, pool1000, pool2000 } = await this.client.AllPools( formattedQuery ); - console.timeEnd('fetching pools'); + logger.timeEnd('fetching pools'); return [...pool0, ...pool1000, ...pool2000].map((pool) => mapType(pool, this.chainId) @@ -96,6 +98,8 @@ export class PoolsSubgraphRepository } async fetch(options?: PoolsRepositoryFetchOptions): Promise { + const logger = Logger.getInstance(); + logger.time('fetching pools'); if (options?.skip) { this.query.args.skip = options.skip; } @@ -112,6 +116,7 @@ export class PoolsSubgraphRepository const { pools } = await this.client.Pools(formattedQuery); this.skip = (options?.skip || 0) + pools.length; + logger.timeEnd('fetching pools'); return pools.map((pool) => mapType(pool, this.chainId)); } diff --git a/balancer-js/src/modules/data/pool/subgraphOnChain.ts b/balancer-js/src/modules/data/pool/subgraphOnChain.ts index c5b2b1ed6..b74c752dc 100644 --- a/balancer-js/src/modules/data/pool/subgraphOnChain.ts +++ b/balancer-js/src/modules/data/pool/subgraphOnChain.ts @@ -5,6 +5,7 @@ import { Pool } from '@/types'; import { getOnChainBalances } from '../../../modules/sor/pool-data/onChainData'; import { PoolsSubgraphRepository } from './subgraph'; import { isSameAddress } from '@/lib/utils'; +import { Logger } from '@/lib/utils/logger'; interface PoolsSubgraphOnChainRepositoryOptions { provider: Provider; @@ -59,35 +60,40 @@ export class PoolsSubgraphOnChainRepository * @returns Promise resolving to pools list */ private async fetchDefault(): Promise { - console.time('fetching pools SG'); const pools = await this.poolsSubgraph.all(); - console.timeEnd('fetching pools SG'); const filteredPools = this.filterPools(pools); - console.time(`fetching onchain ${filteredPools.length} pools`); + + const logger = Logger.getInstance(); + logger.time(`fetching onchain ${filteredPools.length} pools`); + const onchainPools = await getOnChainBalances( filteredPools, this.multicall, this.vault, this.provider ); - console.timeEnd(`fetching onchain ${filteredPools.length} pools`); + + logger.timeEnd(`fetching onchain ${filteredPools.length} pools`); return onchainPools; } async fetch(options?: PoolsRepositoryFetchOptions): Promise { - console.time('fetching pools SG'); const pools = await this.poolsSubgraph.fetch(options); - console.timeEnd('fetching pools SG'); const filteredPools = this.filterPools(pools); - console.time(`fetching onchain ${filteredPools.length} pools`); + + const logger = Logger.getInstance(); + logger.time(`fetching onchain ${filteredPools.length} pools`); + const onchainPools = await getOnChainBalances( filteredPools, this.multicall, this.vault, this.provider ); - console.timeEnd(`fetching onchain ${filteredPools.length} pools`); + + logger.timeEnd(`fetching onchain ${filteredPools.length} pools`); + return onchainPools; } diff --git a/balancer-js/src/modules/data/token-prices/aave-rates.ts b/balancer-js/src/modules/data/token-prices/aave-rates.ts index 7e6e6a0e5..e891abc53 100644 --- a/balancer-js/src/modules/data/token-prices/aave-rates.ts +++ b/balancer-js/src/modules/data/token-prices/aave-rates.ts @@ -86,8 +86,10 @@ export class AaveRates implements IAaveRates { return rates; } + // eslint-disable-next-line @typescript-eslint/no-unused-vars async getRate(wrappedAToken: string): Promise { - if (this.network != Network.MAINNET && this.network != Network.POLYGON) { + //To prevent bricked linear pools from effecting this call + /*if (this.network != Network.MAINNET && this.network != Network.POLYGON) { return 1; } if (!Object.values(yieldTokens[this.network]).includes(wrappedAToken)) { @@ -97,6 +99,8 @@ export class AaveRates implements IAaveRates { this.rates = this.fetch(this.network); } - return (await this.rates)[wrappedAToken]; + return (await this.rates)[wrappedAToken];*/ + + return 1; } } diff --git a/balancer-js/src/modules/exits/exits.module.integration-mainnet.spec.ts b/balancer-js/src/modules/exits/exits.module.integration-mainnet.spec.ts index cfdfce69f..18e96277d 100644 --- a/balancer-js/src/modules/exits/exits.module.integration-mainnet.spec.ts +++ b/balancer-js/src/modules/exits/exits.module.integration-mainnet.spec.ts @@ -13,8 +13,37 @@ const TEST_BBAUSD3 = true; const blockNo = TEST_BLOCK[Network.MAINNET]; +// The tests that are skipped are because wrappedToken rate can no longer be fetched onchain describe('generalised exit execution', async function () { this.timeout(120000); // Sets timeout for all tests within this scope to 2 minutes + context('aaveLinear V1 - bbausd', async () => { + const network = Network.MAINNET; + const pool = ADDRESSES[network].bbausd; + const slippage = '10'; // 10 bps = 0.1% + const poolAddresses = Object.values(ADDRESSES[network]).map( + (address) => address.address + ); + + const amountRatio = 10; + // Amount greater than the underlying main token balance, which will cause the exit to be unwrapped + const unwrapExitAmount = parseFixed('1273000', pool.decimals); + // Amount smaller than the underlying main token balance, which will cause the exit to be done directly + const mainExitAmount = unwrapExitAmount.div(amountRatio); + + context('exit to main tokens directly', async () => { + it('should exit to main tokens directly', async () => { + await testFlow( + pool, + slippage, + mainExitAmount.toString(), + [], + network, + blockNo, + poolAddresses + ); + }); + }); + }); context('ERC4626 - bbausd3', async () => { if (!TEST_BBAUSD3) return true; @@ -36,7 +65,7 @@ describe('generalised exit execution', async function () { const mainExitAmount = unwrapExitAmount.div(amountRatio); context('exit by unwrapping tokens', async () => { - it('should exit via unwrapping', async () => { + it.skip('should exit via unwrapping', async () => { const { expectedAmountsOut, gasUsed } = await testFlow( pool, slippage, @@ -67,7 +96,7 @@ describe('generalised exit execution', async function () { }); }); - context('exit by unwrapping vs exit to main tokens', async () => { + context.skip('exit by unwrapping vs exit to main tokens', async () => { it('should return similar amounts (proportional to the input)', async () => { mainTokensAmountsOut.forEach((amount, i) => { const unwrappedAmount = BigNumber.from( @@ -102,7 +131,7 @@ describe('generalised exit execution', async function () { // Amount smaller than the underlying main token balance, which will cause the exit to be done directly const mainExitAmount = unwrapExitAmount.div(amountRatio); - context('exit by unwrapping tokens', async () => { + context.skip('exit by unwrapping tokens', async () => { it('should exit via unwrapping', async () => { const { expectedAmountsOut, gasUsed } = await testFlow( pool, @@ -134,7 +163,7 @@ describe('generalised exit execution', async function () { }); }); - context('exit by unwrapping vs exit to main tokens', async () => { + context.skip('exit by unwrapping vs exit to main tokens', async () => { it('should return similar amounts (proportional to the input)', async () => { mainTokensAmountsOut.forEach((amount, i) => { const unwrappedAmount = BigNumber.from( @@ -151,7 +180,7 @@ describe('generalised exit execution', async function () { }); }); - context('AaveLinear - bbausd', async () => { + context('AaveLinear - bbausd2', async () => { const network = Network.MAINNET; const pool = ADDRESSES[network].bbausd2; const slippage = '10'; // 10 bps = 0.1% @@ -169,7 +198,7 @@ describe('generalised exit execution', async function () { // Amount smaller than the underlying main token balance, which will cause the exit to be done directly const mainExitAmount = unwrapExitAmount.div(amountRatio); - context('exit by unwrapping tokens', async () => { + context.skip('exit by unwrapping tokens', async () => { it('should exit via unwrapping', async () => { const { expectedAmountsOut, gasUsed } = await testFlow( pool, @@ -201,7 +230,7 @@ describe('generalised exit execution', async function () { }); }); - context('exit by unwrapping vs exit to main tokens', async () => { + context.skip('exit by unwrapping vs exit to main tokens', async () => { it('should return similar amounts (proportional to the input)', async () => { mainTokensAmountsOut.forEach((amount, i) => { const unwrappedAmount = BigNumber.from( diff --git a/balancer-js/src/modules/exits/exits.module.integration.spec.ts b/balancer-js/src/modules/exits/exits.module.integration.spec.ts index 0cbf58891..bd35a25c9 100644 --- a/balancer-js/src/modules/exits/exits.module.integration.spec.ts +++ b/balancer-js/src/modules/exits/exits.module.integration.spec.ts @@ -25,7 +25,7 @@ const poolAddresses = Object.values(ADDRESSES[network]).map( ); const addresses = ADDRESSES[network]; -describe.skip('generalised exit execution', async function () { +describe('generalised exit execution', async function () { this.timeout(120000); // Sets timeout for all tests within this scope to 2 minutes /* diff --git a/balancer-js/src/modules/exits/exits.module.ts b/balancer-js/src/modules/exits/exits.module.ts index abb15bc2d..7a4ce1f11 100644 --- a/balancer-js/src/modules/exits/exits.module.ts +++ b/balancer-js/src/modules/exits/exits.module.ts @@ -10,7 +10,12 @@ import { AssetHelpers, subSlippage } from '@/lib/utils'; import { PoolGraph, Node } from '@/modules/graph/graph'; import { Join } from '@/modules/joins/joins.module'; import { calcPriceImpact } from '@/modules/pricing/priceImpact'; -import { EncodeUnwrapInput, Relayer } from '@/modules/relayer/relayer.module'; +import { + EncodeUnwrapInput, + OutputReference, + Relayer, + EncodeBatchSwapInput, +} from '@/modules/relayer/relayer.module'; import { Simulation, SimulationType, @@ -20,9 +25,13 @@ import { SingleSwap, Swap, SwapType, + BatchSwapStep, } from '@/modules/swaps/types'; import { ExitPoolRequest as ExitPoolModelRequest } from '@/modules/vaultModel/poolModel/exit'; -import { SwapRequest } from '@/modules/vaultModel/poolModel/swap'; +import { + BatchSwapRequest, + SwapRequest, +} from '@/modules/vaultModel/poolModel/swap'; import { UnwrapRequest } from '@/modules/vaultModel/poolModel/unwrap'; import { Requests, VaultModel } from '@/modules/vaultModel/vaultModel.module'; import { ComposableStablePoolEncoder } from '@/pool-composable-stable'; @@ -858,6 +867,104 @@ export class Exit { return { modelRequest, encodedCall, assets, amounts }; } + private createBatchSwap( + node: Node, + exitChildren: Node[], + exitPathIndex: number, + minAmountsOut: string[], + sender: string, + recipient: string + ): { + modelRequest: BatchSwapRequest; + encodedCall: string; + assets: string[]; + amounts: string[]; + } { + const isRootNode = !node.parent; + const amountIn = isRootNode + ? node.index + : Relayer.toChainedReference( + this.getOutputRef(exitPathIndex, node.index) + ).toString(); + + const tokensOut = exitChildren.map((n) => n.address); + const assets = [...tokensOut, node.address]; + // TODO - setting these right? + const limits = [...minAmountsOut]; + limits.push(amountIn); + const batchSwapSteps: BatchSwapStep[] = []; + const outputReferences: OutputReference[] = []; + exitChildren.forEach((child, i) => { + // TODO - Is this correct? + const amount = child.proportionOfParent + .mul(amountIn) + .div(WeiPerEther) + .toString(); + const swapStep: BatchSwapStep = { + poolId: node.id, + assetInIndex: assets.length - 1, + assetOutIndex: i, + amount, + userData: '0x', + }; + batchSwapSteps.push(swapStep); + // TODO - Is this right? + outputReferences.push({ + index: i, + key: Relayer.toChainedReference(this.getOutputRef(0, child.index)), + }); + }); + + const total = batchSwapSteps.reduce((acc, swap) => { + return acc.add(swap.amount); + }, BigNumber.from(0)); + const dust = BigNumber.from(amountIn).sub(total); + batchSwapSteps[0].amount = dust.add(batchSwapSteps[0].amount).toString(); + + const fromInternalBalance = this.receivesFromInternal(node); + // TODO - This is assuming that all exit to same, is this right? + const toInternalBalance = this.receivesFromInternal(exitChildren[0]); + + const funds: FundManagement = { + sender, + recipient, + fromInternalBalance, + toInternalBalance, + }; + + const call: EncodeBatchSwapInput = { + swapType: SwapType.SwapExactIn, + swaps: batchSwapSteps, + assets, + funds, + limits, + deadline: BigNumber.from(Math.ceil(Date.now() / 1000) + 3600), // 1 hour from now + value: '0', // TODO: check if swap with ETH is possible in this case and handle it + outputReferences, + }; + debugLog('\nBatchSwap:'); + debugLog(JSON.stringify(call)); + + const encodedCall = Relayer.encodeBatchSwap(call); + + const modelRequest = VaultModel.mapBatchSwapRequest(call); + + // If node isn't rootNode, the swap is part of a chain and shouldn't be considered for user deltas + const bptIn = !isRootNode ? '0' : amountIn; + // If child exit action is not output, the swap is part of a chain and shouldn't be considered for user deltas + const userTokensOut = exitChildren.map((child, i) => { + const userTokenOutAmount = + child.exitAction !== 'output' + ? '0' + : BigNumber.from(minAmountsOut[i]).mul(-1).toString(); // needs to be negative because it's handled by the vault model as an amount going out of the vault + return userTokenOutAmount; + }); + + const amounts = [...userTokensOut, bptIn]; + + return { modelRequest, encodedCall, assets, amounts }; + } + private createExitPool( node: Node, exitChild: Node, diff --git a/balancer-js/src/modules/graph/graph.ts b/balancer-js/src/modules/graph/graph.ts index 24e8b88a4..69a087079 100644 --- a/balancer-js/src/modules/graph/graph.ts +++ b/balancer-js/src/modules/graph/graph.ts @@ -392,7 +392,7 @@ export class PoolGraph { */ static isProportionalPools(nodes: Node[]): boolean { return nodes.every((node) => { - if (node.exitAction === 'exitPool') return node.isProportionalExit; + if (node.children.length > 1) return node.isProportionalExit; else return true; }); } diff --git a/balancer-js/src/modules/pools/apr/apr.integration.spec.ts b/balancer-js/src/modules/pools/apr/apr.integration.spec.ts index f4afc3147..b48099287 100644 --- a/balancer-js/src/modules/pools/apr/apr.integration.spec.ts +++ b/balancer-js/src/modules/pools/apr/apr.integration.spec.ts @@ -1,10 +1,12 @@ +// yarn test:only ./src/modules/pools/apr/apr-ignored.integration.spec.ts +import dotenv from 'dotenv'; +dotenv.config(); import { expect } from 'chai'; -import MockDate from 'mockdate'; -import { BalancerSDK } from '@/.'; +import { BalancerSDK, Network } from '@/.'; const sdk = new BalancerSDK({ - network: 1, - rpcUrl: 'http://127.0.0.1:8545', + network: Network.MAINNET, + rpcUrl: `${process.env.ALCHEMY_URL}`, }); const { pools } = sdk; @@ -18,72 +20,28 @@ const veBalId = const usdStable = '0xa13a9247ea42d743238089903570127dda72fe4400000000000000000000035d'; -const btcEth = - '0xa6f548df93de924d73be7d25dc02554c6bd66db500020000000000000000000e'; - -// const ethStEthCopy = -// '0x851523a36690bf267bbfec389c823072d82921a90002000000000000000001ed'; - const auraBALveBAL = '0x3dd0843a028c86e0b760b1a76929d1c5ef93a2dd000200000000000000000249'; -const getMondayOfWeek = (today = new Date()) => { - const first = today.getUTCDate() - today.getUTCDay() + 1; - return new Date(today.setUTCDate(first)); -}; - -describe.skip('happy case', () => { - // Time when veBal used to receive procotol revenues - const now = getMondayOfWeek().getTime(); - - before(async function () { - MockDate.set(now); - - await sdk.provider.send('hardhat_reset', [ - { - forking: { - jsonRpcUrl: 'https://rpc.ankr.com/eth', - // blockNumber: 15902899, // 2022-11-05 09:24:11 - }, - }, - ]); - }); - - after(() => { - MockDate.reset(); - }); - - // TODO Removed because duplicate pool is disabled - // describe('duplicated pool with an empty gauge', () => { - // it('has tokenAprs', async () => { - // const pool = await pools.find(ethStEthCopy); - // if (pool) { - // const apr = await pools.apr(pool); - // expect(apr && apr.tokenAprs.total).to.be.greaterThan(1); - // } else { - // throw 'no pool found'; - // } - // }).timeout(120000); - // }); - +describe('APR tests', () => { describe('pool with yield tokens', () => { it('has tokenAprs', async () => { const pool = await pools.find(ethStEth); if (pool) { const apr = await pools.apr(pool); - expect(apr && apr.tokenAprs.total).to.be.greaterThan(1); + expect(apr.tokenAprs.total).to.be.greaterThan(1); } else { throw 'no pool found'; } }).timeout(120000); }); - describe('phantom pool with linear pools', () => { - it('has tokenAprs', async () => { + describe('poold on ignore list', () => { + it('should have APR 0', async () => { const pool = await pools.find(usdStable); if (pool) { const apr = await pools.apr(pool); - expect(apr && apr.tokenAprs.total).to.be.greaterThan(1); + expect(apr.tokenAprs.total).to.eq(0); } else { throw 'no pool found'; } @@ -95,19 +53,7 @@ describe.skip('happy case', () => { const pool = await pools.find(veBalId); if (pool) { const apr = await pools.apr(pool); - expect(apr && apr.protocolApr).to.be.greaterThan(1); - } else { - throw 'no pool found'; - } - }).timeout(120000); - }); - - describe('weighted pool with gauge', () => { - it('receives staking rewards', async () => { - const pool = await pools.find(btcEth); - if (pool) { - const apr = await pools.apr(pool); - expect(apr && apr.stakingApr.min).to.be.greaterThan(1); + expect(apr.protocolApr).to.be.greaterThan(1); } else { throw 'no pool found'; } @@ -119,7 +65,7 @@ describe.skip('happy case', () => { const pool = await pools.find(auraBALveBAL); if (pool) { const apr = await pools.apr(pool); - expect(apr && apr.tokenAprs.total).to.be.greaterThan(1); + expect(apr.tokenAprs.total).to.be.greaterThan(1); } else { throw 'no pool found'; } diff --git a/balancer-js/src/modules/pools/apr/apr.ts b/balancer-js/src/modules/pools/apr/apr.ts index 25e11135d..679dac80d 100644 --- a/balancer-js/src/modules/pools/apr/apr.ts +++ b/balancer-js/src/modules/pools/apr/apr.ts @@ -19,6 +19,8 @@ import { PoolFees } from '../fees/fees'; import { BALANCER_NETWORK_CONFIG } from '@/lib/constants/config'; import { BigNumber } from '@ethersproject/bignumber'; import { Logger } from '@/lib/utils/logger'; +import { GyroConfigRepository } from '@/modules/data/gyro-config/repository'; +import { poolsToIgnore } from '@/lib/constants/poolsToIgnore'; export interface AprBreakdown { swapFees: number; @@ -59,7 +61,8 @@ export class PoolApr { private feeCollector: Findable, private yesterdaysPools?: Findable, private liquidityGauges?: Findable, - private feeDistributor?: BaseFeeDistributor + private feeDistributor?: BaseFeeDistributor, + private gyroConfigRepository?: GyroConfigRepository ) {} /** @@ -110,11 +113,13 @@ export class PoolApr { bptFreeTokens.map(async (token) => { let apr = 0; const tokenYield = await this.tokenYields.find(token.address); - if (tokenYield) { // metastable pools incorrectly apply the swap fee to the yield earned. // they don't have the concept of a yield fee like the newer pools do. - if (pool.poolType === 'MetaStable') { + if ( + pool.poolType === 'MetaStable' || + pool.poolType.includes('Gyro') + ) { apr = tokenYield * (1 - (await this.protocolSwapFeePercentage(pool))); } else if ( @@ -408,6 +413,26 @@ export class PoolApr { * @returns pool APR split [bsp] */ async apr(pool: Pool): Promise { + if (poolsToIgnore.includes(pool.id)) { + return { + swapFees: 0, + tokenAprs: { + total: 0, + breakdown: {}, + }, + stakingApr: { + min: 0, + max: 0, + }, + rewardAprs: { + total: 0, + breakdown: {}, + }, + protocolApr: 0, + min: 0, + max: 0, + }; + } const [ swapFees, tokenAprs, @@ -480,19 +505,19 @@ export class PoolApr { } private async protocolSwapFeePercentage(pool: Pool) { - let fee = 0; - + let fee; if ( pool.poolType == 'ComposableStable' || (pool.poolType == 'Weighted' && pool.poolTypeVersion == 2) ) { fee = 0; + } else if (pool.poolType.includes('Gyro') && this.gyroConfigRepository) { + fee = await this.gyroConfigRepository.getGyroProtocolFee(pool.address); } else if (pool.protocolSwapFeeCache) { fee = parseFloat(pool.protocolSwapFeeCache); } else { fee = (await this.feeCollector.find('')) || 0; } - return fee; } diff --git a/balancer-js/src/modules/pools/index.ts b/balancer-js/src/modules/pools/index.ts index 9939244b7..7f91fb36e 100644 --- a/balancer-js/src/modules/pools/index.ts +++ b/balancer-js/src/modules/pools/index.ts @@ -68,7 +68,8 @@ export class Pools implements Findable { this.repositories.feeCollector, this.repositories.yesterdaysPools, this.repositories.liquidityGauges, - this.repositories.feeDistributor + this.repositories.feeDistributor, + this.repositories.gyroConfigRepository ); this.liquidityService = new Liquidity( repositories.pools, @@ -392,6 +393,32 @@ export class Pools implements Findable { }); } + buildRecoveryExit({ + pool, + bptAmount, + userAddress, + slippage, + toInternalBalance, + }: { + pool: Pool; + bptAmount: string; + userAddress: string; + slippage: string; + toInternalBalance?: boolean; + }): ExitExactBPTInAttributes { + const concerns = PoolTypeConcerns.from(pool.poolType); + if (!concerns || !concerns.exit.buildRecoveryExit) + throw `buildRecoveryExit for poolType ${pool.poolType} not implemented`; + + return concerns.exit.buildRecoveryExit({ + exiter: userAddress, + pool, + bptIn: bptAmount, + slippage, + toInternalBalance: !!toInternalBalance, + }); + } + /** * Builds generalised join transaction * diff --git a/balancer-js/src/modules/pools/pool-types/concerns/composableStable/exit.concern.ts b/balancer-js/src/modules/pools/pool-types/concerns/composableStable/exit.concern.ts index b73a1eba4..2d3e9e936 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/composableStable/exit.concern.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/composableStable/exit.concern.ts @@ -223,6 +223,8 @@ export class ComposableStablePoolExit implements ExitConcern { bptIn, pool, }); + // Recovery exits don't use rates. We use them as part of scalingFactor so default to 1 incase of issues + pool.tokens.forEach((t) => (t.priceRate = '1')); const sortedValues = parsePoolInfo(pool); diff --git a/balancer-js/src/modules/pools/pool-types/concerns/composableStable/recovery.integration.spec.ts b/balancer-js/src/modules/pools/pool-types/concerns/composableStable/recovery.integration.spec.ts new file mode 100644 index 000000000..da92e61c4 --- /dev/null +++ b/balancer-js/src/modules/pools/pool-types/concerns/composableStable/recovery.integration.spec.ts @@ -0,0 +1,370 @@ +// yarn test:only ./src/modules/pools/pool-types/concerns/composableStable/recovery.integration.spec.ts +import dotenv from 'dotenv'; +import { ethers } from 'hardhat'; +import { parseFixed } from '@ethersproject/bignumber'; +import { + BalancerSDK, + getPoolAddress, + Network, + GraphQLArgs, + GraphQLQuery, +} from '@/.'; +import { forkSetup } from '@/test/lib/utils'; +import { assertRecoveryExit } from '@/test/lib/exitHelper'; + +dotenv.config(); + +const network = Network.POLYGON; +const { ALCHEMY_URL_POLYGON: jsonRpcUrl } = process.env; +const rpcUrl = 'http://127.0.0.1:8137'; +const provider = new ethers.providers.JsonRpcProvider(rpcUrl, network); +const signer = provider.getSigner(); +const blockNumber = 46572274; +let balancer: BalancerSDK; + +describe('ComposableStable - recovery', () => { + context('V1', async () => { + const poolId = + '0x02d2e2d7a89d6c5cb3681cfcb6f7dac02a55eda400000000000000000000088f'; + // We have to reset the fork between each test as pool value changes after tx is submitted + beforeEach(async () => { + // Setup forked network, set initial token balances and allowances + await forkSetup( + signer, + [getPoolAddress(poolId)], + [0], + [parseFixed('10000', 18).toString()], + jsonRpcUrl as string, + blockNumber + ); + const subgraphArgs: GraphQLArgs = { + where: { + id: { + in: [poolId], + }, + }, + block: { number: blockNumber }, + }; + + const subgraphQuery: GraphQLQuery = { args: subgraphArgs, attrs: {} }; + balancer = new BalancerSDK({ + network, + rpcUrl, + subgraphQuery, + }); + }); + + context('buildRecoveryExit', async () => { + context('PoolWithMethods', async () => { + it('should recovery exit', async () => { + const bptAmount = parseFixed('1.34', 18).toString(); + const slippage = '10'; // 10 bps = 0.1% + const pool = await balancer.pools.find(poolId); + if (!pool) throw Error('Pool not found'); + const signerAddr = await signer.getAddress(); + const { to, data, minAmountsOut, expectedAmountsOut, priceImpact } = + pool.buildRecoveryExit(signerAddr, bptAmount, slippage); + await assertRecoveryExit( + signerAddr, + slippage, + to, + data, + minAmountsOut, + expectedAmountsOut, + priceImpact, + pool, + signer, + bptAmount + ); + }); + }); + context('Pool & refresh', async () => { + it('should recovery exit', async () => { + const bptAmount = parseFixed('1.34', 18).toString(); + const slippage = '10'; // 10 bps = 0.1% + let pool = await balancer.data.pools.find(poolId); + if (!pool) throw Error('Pool not found'); + const signerAddr = await signer.getAddress(); + pool = await balancer.data.poolsOnChain.refresh(pool); + const { to, data, expectedAmountsOut, minAmountsOut, priceImpact } = + balancer.pools.buildRecoveryExit({ + pool, + bptAmount, + userAddress: signerAddr, + slippage, + }); + await assertRecoveryExit( + signerAddr, + slippage, + to, + data, + minAmountsOut, + expectedAmountsOut, + priceImpact, + pool, + signer, + bptAmount + ); + }); + }); + }); + }); + context('V2', async () => { + const poolId = + '0xe2dc0e0f2c358d6e31836dee69a558ab8d1390e70000000000000000000009fa'; + // We have to reset the fork between each test as pool value changes after tx is submitted + beforeEach(async () => { + // Setup forked network, set initial token balances and allowances + await forkSetup( + signer, + [getPoolAddress(poolId)], + [0], + [parseFixed('10000', 18).toString()], + jsonRpcUrl as string, + blockNumber + ); + const subgraphArgs: GraphQLArgs = { + where: { + id: { + in: [poolId], + }, + }, + block: { number: blockNumber }, + }; + + const subgraphQuery: GraphQLQuery = { args: subgraphArgs, attrs: {} }; + balancer = new BalancerSDK({ + network, + rpcUrl, + subgraphQuery, + }); + }); + + context('buildRecoveryExit', async () => { + context('PoolWithMethods', async () => { + it('should recovery exit', async () => { + const bptAmount = parseFixed('1.34', 18).toString(); + const slippage = '10'; // 10 bps = 0.1% + const pool = await balancer.pools.find(poolId); + if (!pool) throw Error('Pool not found'); + const signerAddr = await signer.getAddress(); + const { to, data, minAmountsOut, expectedAmountsOut, priceImpact } = + pool.buildRecoveryExit(signerAddr, bptAmount, slippage); + await assertRecoveryExit( + signerAddr, + slippage, + to, + data, + minAmountsOut, + expectedAmountsOut, + priceImpact, + pool, + signer, + bptAmount + ); + }); + }); + context('Pool & refresh', async () => { + it('should recovery exit', async () => { + const bptAmount = parseFixed('1.34', 18).toString(); + const slippage = '10'; // 10 bps = 0.1% + let pool = await balancer.data.pools.find(poolId); + if (!pool) throw Error('Pool not found'); + const signerAddr = await signer.getAddress(); + pool = await balancer.data.poolsOnChain.refresh(pool); + const { to, data, expectedAmountsOut, minAmountsOut, priceImpact } = + balancer.pools.buildRecoveryExit({ + pool, + bptAmount, + userAddress: signerAddr, + slippage, + }); + await assertRecoveryExit( + signerAddr, + slippage, + to, + data, + minAmountsOut, + expectedAmountsOut, + priceImpact, + pool, + signer, + bptAmount + ); + }); + }); + }); + }); + context('V3', async () => { + const poolId = + '0x10b040038f87219d9b42e025e3bd9b8095c87dd9000000000000000000000b11'; + // We have to reset the fork between each test as pool value changes after tx is submitted + beforeEach(async () => { + // Setup forked network, set initial token balances and allowances + await forkSetup( + signer, + [getPoolAddress(poolId)], + [0], + [parseFixed('10000', 18).toString()], + jsonRpcUrl as string, + blockNumber + ); + const subgraphArgs: GraphQLArgs = { + where: { + id: { + in: [poolId], + }, + }, + block: { number: blockNumber }, + }; + + const subgraphQuery: GraphQLQuery = { args: subgraphArgs, attrs: {} }; + balancer = new BalancerSDK({ + network, + rpcUrl, + subgraphQuery, + }); + }); + + context('buildRecoveryExit', async () => { + context('PoolWithMethods', async () => { + it('should recovery exit', async () => { + const bptAmount = parseFixed('1.34', 18).toString(); + const slippage = '10'; // 10 bps = 0.1% + const pool = await balancer.pools.find(poolId); + if (!pool) throw Error('Pool not found'); + const signerAddr = await signer.getAddress(); + const { to, data, minAmountsOut, expectedAmountsOut, priceImpact } = + pool.buildRecoveryExit(signerAddr, bptAmount, slippage); + await assertRecoveryExit( + signerAddr, + slippage, + to, + data, + minAmountsOut, + expectedAmountsOut, + priceImpact, + pool, + signer, + bptAmount + ); + }); + }); + context('Pool & refresh', async () => { + it('should recovery exit', async () => { + const bptAmount = parseFixed('1.34', 18).toString(); + const slippage = '10'; // 10 bps = 0.1% + let pool = await balancer.data.pools.find(poolId); + if (!pool) throw Error('Pool not found'); + const signerAddr = await signer.getAddress(); + pool = await balancer.data.poolsOnChain.refresh(pool); + const { to, data, expectedAmountsOut, minAmountsOut, priceImpact } = + balancer.pools.buildRecoveryExit({ + pool, + bptAmount, + userAddress: signerAddr, + slippage, + }); + await assertRecoveryExit( + signerAddr, + slippage, + to, + data, + minAmountsOut, + expectedAmountsOut, + priceImpact, + pool, + signer, + bptAmount + ); + }); + }); + }); + }); + context('V4', async () => { + const poolId = + '0xa2ccad543fbe9332b87910beabd941b86dd5f762000000000000000000000b5c'; + // We have to reset the fork between each test as pool value changes after tx is submitted + beforeEach(async () => { + // Setup forked network, set initial token balances and allowances + await forkSetup( + signer, + [getPoolAddress(poolId)], + [0], + [parseFixed('10000', 18).toString()], + jsonRpcUrl as string, + blockNumber + ); + const subgraphArgs: GraphQLArgs = { + where: { + id: { + in: [poolId], + }, + }, + block: { number: blockNumber }, + }; + + const subgraphQuery: GraphQLQuery = { args: subgraphArgs, attrs: {} }; + balancer = new BalancerSDK({ + network, + rpcUrl, + subgraphQuery, + }); + }); + + context('buildRecoveryExit', async () => { + context('PoolWithMethods', async () => { + it('should recovery exit', async () => { + const bptAmount = parseFixed('0.34', 18).toString(); + const slippage = '10'; // 10 bps = 0.1% + const pool = await balancer.pools.find(poolId); + if (!pool) throw Error('Pool not found'); + const signerAddr = await signer.getAddress(); + const { to, data, minAmountsOut, expectedAmountsOut, priceImpact } = + pool.buildRecoveryExit(signerAddr, bptAmount, slippage); + await assertRecoveryExit( + signerAddr, + slippage, + to, + data, + minAmountsOut, + expectedAmountsOut, + priceImpact, + pool, + signer, + bptAmount + ); + }); + }); + context('Pool & refresh', async () => { + it('should recovery exit', async () => { + const bptAmount = parseFixed('0.34', 18).toString(); + const slippage = '10'; // 10 bps = 0.1% + let pool = await balancer.data.pools.find(poolId); + if (!pool) throw Error('Pool not found'); + const signerAddr = await signer.getAddress(); + pool = await balancer.data.poolsOnChain.refresh(pool); + const { to, data, expectedAmountsOut, minAmountsOut, priceImpact } = + balancer.pools.buildRecoveryExit({ + pool, + bptAmount, + userAddress: signerAddr, + slippage, + }); + await assertRecoveryExit( + signerAddr, + slippage, + to, + data, + minAmountsOut, + expectedAmountsOut, + priceImpact, + pool, + signer, + bptAmount + ); + }); + }); + }); + }); +}); diff --git a/balancer-js/src/modules/pools/pool-types/concerns/linear/exit.concern.ts b/balancer-js/src/modules/pools/pool-types/concerns/linear/exit.concern.ts index 6b0946a6c..da79d4e06 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/linear/exit.concern.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/linear/exit.concern.ts @@ -85,6 +85,8 @@ export class LinearPoolExit implements ExitConcern { pool, shouldUnwrapNativeAsset: false, }); + // Recovery exits don't use rates. We use them as part of scalingFactor so default to 1 incase of issues + pool.tokens.forEach((t) => (t.priceRate = '1')); const sortedValues = parsePoolInfo(pool); const { minAmountsOut, expectedAmountsOut } = this.calcTokensOutGivenExactBptIn({ diff --git a/balancer-js/src/modules/pools/pool-types/concerns/linear/recovery.integration.spec.ts b/balancer-js/src/modules/pools/pool-types/concerns/linear/recovery.integration.spec.ts new file mode 100644 index 000000000..506e16ea4 --- /dev/null +++ b/balancer-js/src/modules/pools/pool-types/concerns/linear/recovery.integration.spec.ts @@ -0,0 +1,213 @@ +// yarn test:only ./src/modules/pools/pool-types/concerns/linear/recovery.integration.spec.ts +import dotenv from 'dotenv'; +import { ethers } from 'hardhat'; +import { parseFixed } from '@ethersproject/bignumber'; +import { + BalancerSDK, + getPoolAddress, + Network, + GraphQLArgs, + GraphQLQuery, + ERC4626LinearPool__factory, + Authoriser__factory, +} from '@/.'; +import { forkSetup } from '@/test/lib/utils'; +import { testRecoveryExit, assertRecoveryExit } from '@/test/lib/exitHelper'; + +dotenv.config(); + +const network = Network.MAINNET; +const { ALCHEMY_URL: jsonRpcUrl } = process.env; +const rpcUrl = 'http://127.0.0.1:8545'; +const provider = new ethers.providers.JsonRpcProvider(rpcUrl, network); +const blockNumber = 17920684; +const signer = provider.getSigner(); + +describe('Linear - recovery exits', () => { + context('buildRecoveryExit', async () => { + context('ERC4626', async () => { + context('PoolWithMethods', async () => { + it('should recovery exit', async () => { + const bptIn = parseFixed('1000000', 18).toString(); + const poolId = + '0xcbfa4532d8b2ade2c261d3dd5ef2a2284f7926920000000000000000000004fa'; + // Setup forked network, set initial token balances and allowances + await forkSetup( + signer, + [getPoolAddress(poolId)], + [0], + [bptIn], + jsonRpcUrl as string, + blockNumber + ); + const balancer = await setupSDK(poolId, blockNumber); + const pool = await balancer.pools.find(poolId); + if (!pool) throw Error('Pool not found'); + // enableRecoveryMode() role (see deployments repo for confirmation) + const actionId = + '0x650376aebfe02b35334f3ae96d46b9e5659baa84220d58312d9d2e2920ec9f1d'; + await setRecovery(pool.address, actionId); + await testRecoveryExit(pool, signer, bptIn); + }); + }); + context('Pool & refresh', async () => { + it('should recovery exit', async () => { + const bptIn = parseFixed('1000000', 18).toString(); + const poolId = + '0xcbfa4532d8b2ade2c261d3dd5ef2a2284f7926920000000000000000000004fa'; + // Setup forked network, set initial token balances and allowances + await forkSetup( + signer, + [getPoolAddress(poolId)], + [0], + [bptIn], + jsonRpcUrl as string, + blockNumber + ); + const balancer = await setupSDK(poolId, blockNumber); + const slippage = '10'; // 10 bps = 0.1% + let pool = await balancer.data.pools.find(poolId); + if (!pool) throw Error('Pool not found'); + // enableRecoveryMode() role (see deployments repo for confirmation) + const actionId = + '0x650376aebfe02b35334f3ae96d46b9e5659baa84220d58312d9d2e2920ec9f1d'; + await setRecovery(pool.address, actionId); + const signerAddr = await signer.getAddress(); + pool = await balancer.data.poolsOnChain.refresh(pool); + const { to, data, expectedAmountsOut, minAmountsOut, priceImpact } = + balancer.pools.buildRecoveryExit({ + pool, + bptAmount: bptIn, + userAddress: signerAddr, + slippage, + }); + await assertRecoveryExit( + signerAddr, + slippage, + to, + data, + minAmountsOut, + expectedAmountsOut, + priceImpact, + pool, + signer, + bptIn + ); + }); + }); + }); + it.skip('AaveLinear V1 - should recovery exit', async () => { + // These don't seem to have recovery exit? + const poolId = + '0x2bbf681cc4eb09218bee85ea2a5d3d13fa40fc0c0000000000000000000000fd'; + // Setup forked network, set initial token balances and allowances + await forkSetup( + signer, + [getPoolAddress(poolId)], + [0], + [parseFixed('123', 18).toString()], + jsonRpcUrl as string, + blockNumber + ); + const balancer = await setupSDK(poolId, blockNumber); + const pool = await balancer.pools.find(poolId); + if (!pool) throw Error('Pool not found'); + const actionId = + '0x650376aebfe02b35334f3ae96d46b9e5659baa84220d58312d9d2e2920ec9f1d'; + await setRecovery(pool.address, actionId); + const bptIn = parseFixed('123', 18).toString(); + await testRecoveryExit(pool, signer, bptIn); + }); + it('AaveLinear V2 - should recovery exit', async () => { + const poolId = + '0x2f4eb100552ef93840d5adc30560e5513dfffacb000000000000000000000334'; + // Setup forked network, set initial token balances and allowances + await forkSetup( + signer, + [getPoolAddress(poolId)], + [0], + [parseFixed('1234', 18).toString()], + jsonRpcUrl as string, + blockNumber + ); + const balancer = await setupSDK(poolId, blockNumber); + const pool = await balancer.pools.find(poolId); + if (!pool) throw Error('Pool not found'); + const actionId = + '0xd6a9f64d81e7c22127c3fb002e0a42508528898232c353fc3d33cd259ea1de7b'; + await setRecovery(pool.address, actionId); + const bptIn = parseFixed('1234', 18).toString(); + await testRecoveryExit(pool, signer, bptIn); + }); + it('GearBoxLinear - should recovery exit', async () => { + // 0x54a844ba + const poolId = + '0x4a82b580365cff9b146281ab72500957a849abdc000000000000000000000494'; + // Setup forked network, set initial token balances and allowances + await forkSetup( + signer, + [getPoolAddress(poolId)], + [0], + [parseFixed('123423', 18).toString()], + jsonRpcUrl as string, + blockNumber + ); + const balancer = await setupSDK(poolId, blockNumber); + const pool = await balancer.pools.find(poolId); + if (!pool) throw Error('Pool not found'); + const actionId = + '0xa32a74d0340eda2bd1a7c5ec04d47e7a95f472e66d159ecf6e21d957a5a003a9'; + await setRecovery(pool.address, actionId); + const bptIn = parseFixed('123423', 18).toString(); + await testRecoveryExit(pool, signer, bptIn); + }); + }); +}); + +async function setupSDK( + poolId: string, + blockNumber: number +): Promise { + const subgraphArgs: GraphQLArgs = { + where: { + id: { + in: [poolId], + }, + }, + block: { number: blockNumber }, + }; + + const subgraphQuery: GraphQLQuery = { args: subgraphArgs, attrs: {} }; + const balancer = new BalancerSDK({ + network, + rpcUrl, + subgraphQuery, + }); + + return balancer; +} + +async function setRecovery(poolAddr: string, role: string) { + // Governance has permissions to set roles on pools + const governanceSafeAddr = '0x10A19e7eE7d7F8a52822f6817de8ea18204F2e4f'; + // Authoriser contract is used to set the roles + const authoriserAddr = '0xA331D84eC860Bf466b4CdCcFb4aC09a1B43F3aE6'; + await provider.send('hardhat_impersonateAccount', [governanceSafeAddr]); + const signerGovernance = provider.getSigner(governanceSafeAddr); + const authoriser = Authoriser__factory.connect( + authoriserAddr, + signerGovernance + ); + + // Grant governance permission to set pools into recovery + let tx = await authoriser.grantRole(role, governanceSafeAddr); + await tx.wait(); + const poolContract = ERC4626LinearPool__factory.connect( + poolAddr, + signerGovernance + ); + // Set pool into recovery + tx = await poolContract.enableRecoveryMode(); + await tx.wait(); + // const isRecovery = await poolContract.inRecoveryMode(); +} diff --git a/balancer-js/src/modules/pools/pool-types/concerns/weighted/exit.concern.ts b/balancer-js/src/modules/pools/pool-types/concerns/weighted/exit.concern.ts index 8d76b66c2..d25f7e7ad 100644 --- a/balancer-js/src/modules/pools/pool-types/concerns/weighted/exit.concern.ts +++ b/balancer-js/src/modules/pools/pool-types/concerns/weighted/exit.concern.ts @@ -207,6 +207,8 @@ export class WeightedPoolExit implements ExitConcern { pool, shouldUnwrapNativeAsset: false, }); + // Recovery exits don't use rates. We use them as part of scalingFactor so default to 1 incase of issues + pool.tokens.forEach((t) => (t.priceRate = '1')); const sortedValues = parsePoolInfo(pool); const { minAmountsOut, expectedAmountsOut } = this.calcTokensOutGivenExactBptIn({ diff --git a/balancer-js/src/modules/pools/pool-types/concerns/weighted/recovery.integration.spec.ts b/balancer-js/src/modules/pools/pool-types/concerns/weighted/recovery.integration.spec.ts new file mode 100644 index 000000000..bef70a3c4 --- /dev/null +++ b/balancer-js/src/modules/pools/pool-types/concerns/weighted/recovery.integration.spec.ts @@ -0,0 +1,199 @@ +// yarn test:only ./src/modules/pools/pool-types/concerns/weighted/recovery.integration.spec.ts +import dotenv from 'dotenv'; +import { ethers } from 'hardhat'; +import { parseFixed } from '@ethersproject/bignumber'; +import { + BalancerSDK, + getPoolAddress, + Network, + GraphQLArgs, + GraphQLQuery, +} from '@/.'; +import { forkSetup } from '@/test/lib/utils'; +import { assertRecoveryExit } from '@/test/lib/exitHelper'; +import { TEST_BLOCK } from '@/test/lib/constants'; + +dotenv.config(); + +const network = Network.MAINNET; +const { ALCHEMY_URL: jsonRpcUrl } = process.env; +const rpcUrl = 'http://127.0.0.1:8545'; +const provider = new ethers.providers.JsonRpcProvider(rpcUrl, network); +const signer = provider.getSigner(); +const blockNumber = TEST_BLOCK[network]; +let balancer: BalancerSDK; + +describe('Weighted - recovery', () => { + context('V2', async () => { + const poolId = + '0x0fd5663d4893ae0d579d580584806aadd2dd0b8b000200000000000000000367'; + // We have to reset the fork between each test as pool value changes after tx is submitted + beforeEach(async () => { + // Setup forked network, set initial token balances and allowances + await forkSetup( + signer, + [getPoolAddress(poolId)], + [0], + [parseFixed('10000', 18).toString()], + jsonRpcUrl as string, + blockNumber + ); + const subgraphArgs: GraphQLArgs = { + where: { + id: { + in: [poolId], + }, + }, + block: { number: blockNumber }, + }; + + const subgraphQuery: GraphQLQuery = { args: subgraphArgs, attrs: {} }; + balancer = new BalancerSDK({ + network, + rpcUrl, + subgraphQuery, + }); + }); + + context('buildRecoveryExit', async () => { + context('PoolWithMethods', async () => { + it('should recovery exit', async () => { + const bptAmount = parseFixed('1.34', 18).toString(); + const slippage = '10'; // 10 bps = 0.1% + const pool = await balancer.pools.find(poolId); + if (!pool) throw Error('Pool not found'); + const signerAddr = await signer.getAddress(); + const { to, data, minAmountsOut, expectedAmountsOut, priceImpact } = + pool.buildRecoveryExit(signerAddr, bptAmount, slippage); + await assertRecoveryExit( + signerAddr, + slippage, + to, + data, + minAmountsOut, + expectedAmountsOut, + priceImpact, + pool, + signer, + bptAmount + ); + }); + }); + context('Pool & refresh', async () => { + it('should recovery exit', async () => { + const bptAmount = parseFixed('1.34', 18).toString(); + const slippage = '10'; // 10 bps = 0.1% + let pool = await balancer.data.pools.find(poolId); + if (!pool) throw Error('Pool not found'); + const signerAddr = await signer.getAddress(); + pool = await balancer.data.poolsOnChain.refresh(pool); + const { to, data, expectedAmountsOut, minAmountsOut, priceImpact } = + balancer.pools.buildRecoveryExit({ + pool, + bptAmount, + userAddress: signerAddr, + slippage, + }); + await assertRecoveryExit( + signerAddr, + slippage, + to, + data, + minAmountsOut, + expectedAmountsOut, + priceImpact, + pool, + signer, + bptAmount + ); + }); + }); + }); + }); + context('V3', async () => { + const poolId = + '0xa718042e5622099e5f0ace4e7122058ab39e1bbe000200000000000000000475'; + // We have to reset the fork between each test as pool value changes after tx is submitted + beforeEach(async () => { + // Setup forked network, set initial token balances and allowances + await forkSetup( + signer, + [getPoolAddress(poolId)], + [0], + [parseFixed('10000', 18).toString()], + jsonRpcUrl as string, + blockNumber + ); + const subgraphArgs: GraphQLArgs = { + where: { + id: { + in: [poolId], + }, + }, + block: { number: blockNumber }, + }; + + const subgraphQuery: GraphQLQuery = { args: subgraphArgs, attrs: {} }; + balancer = new BalancerSDK({ + network, + rpcUrl, + subgraphQuery, + }); + }); + + context('buildRecoveryExit', async () => { + context('PoolWithMethods', async () => { + it('should recovery exit', async () => { + const bptAmount = parseFixed('1.34', 18).toString(); + const slippage = '10'; // 10 bps = 0.1% + const pool = await balancer.pools.find(poolId); + if (!pool) throw Error('Pool not found'); + const signerAddr = await signer.getAddress(); + const { to, data, minAmountsOut, expectedAmountsOut, priceImpact } = + pool.buildRecoveryExit(signerAddr, bptAmount, slippage); + await assertRecoveryExit( + signerAddr, + slippage, + to, + data, + minAmountsOut, + expectedAmountsOut, + priceImpact, + pool, + signer, + bptAmount + ); + }); + }); + context('Pool & refresh', async () => { + it('should recovery exit', async () => { + const bptAmount = parseFixed('1.34', 18).toString(); + const slippage = '10'; // 10 bps = 0.1% + let pool = await balancer.data.pools.find(poolId); + if (!pool) throw Error('Pool not found'); + const signerAddr = await signer.getAddress(); + pool = await balancer.data.poolsOnChain.refresh(pool); + const { to, data, expectedAmountsOut, minAmountsOut, priceImpact } = + balancer.pools.buildRecoveryExit({ + pool, + bptAmount, + userAddress: signerAddr, + slippage, + }); + await assertRecoveryExit( + signerAddr, + slippage, + to, + data, + minAmountsOut, + expectedAmountsOut, + priceImpact, + pool, + signer, + bptAmount + ); + }); + }); + }); + }); +}); diff --git a/balancer-js/src/modules/sor/pool-data/onChainData.spec.ts b/balancer-js/src/modules/sor/pool-data/onChainData.spec.ts index 43eb949bc..592438d64 100644 --- a/balancer-js/src/modules/sor/pool-data/onChainData.spec.ts +++ b/balancer-js/src/modules/sor/pool-data/onChainData.spec.ts @@ -2,22 +2,36 @@ import dotenv from 'dotenv'; dotenv.config(); import { expect } from 'chai'; import { cloneDeep } from 'lodash'; -import { BALANCER_NETWORK_CONFIG, Network, PoolsSubgraphRepository } from '@/.'; -import { getOnChainPools } from './onChainData'; import { JsonRpcProvider } from '@ethersproject/providers'; +import { + BALANCER_NETWORK_CONFIG, + Network, + Pool, + PoolsSubgraphRepository, +} from '@/.'; + +import { getOnChainBalances, getOnChainPools } from './onChainData'; + // yarn test:only ./src/modules/sor/pool-data/onChainData.spec.ts describe('getOnChainPools', async function () { - it('should fetch onchain pools', async function () { - const network = Network.POLYGON; - const rpcUrl = `https://polygon-mainnet.infura.io/v3/${process.env.INFURA}`; - const provider = new JsonRpcProvider(rpcUrl); - const url = BALANCER_NETWORK_CONFIG[network].urls.subgraph; + this.timeout(40000); + let pools: Pool[]; + let provider: JsonRpcProvider; + const network = Network.POLYGON; + const url = BALANCER_NETWORK_CONFIG[network].urls.subgraph; + const rpcUrl = `https://polygon-mainnet.infura.io/v3/${process.env.INFURA}`; + + before(async function () { + provider = new JsonRpcProvider(rpcUrl); const poolsRepo = new PoolsSubgraphRepository({ url, chainId: network, }); - const pools = await poolsRepo.all(); + pools = await poolsRepo.all(); + }); + + it('should fetch onchain pools using queries helper contract', async function () { const onChainPools = await getOnChainPools( cloneDeep(pools), BALANCER_NETWORK_CONFIG[network].addresses.contracts.poolDataQueries, @@ -25,5 +39,15 @@ describe('getOnChainPools', async function () { provider ); expect(onChainPools.length).to.be.gt(0); - }).timeout(40000); + }); + + it('should fetch onchain pools using multicall', async function () { + const onChainBalances = await getOnChainBalances( + cloneDeep(pools), + BALANCER_NETWORK_CONFIG[network].addresses.contracts.multicall, + BALANCER_NETWORK_CONFIG[network].addresses.contracts.vault, + provider + ); + expect(onChainBalances.length).to.be.gt(0); + }); }); diff --git a/balancer-js/src/modules/sor/pool-data/onChainData.ts b/balancer-js/src/modules/sor/pool-data/onChainData.ts index 97d5d423c..1f31ed957 100644 --- a/balancer-js/src/modules/sor/pool-data/onChainData.ts +++ b/balancer-js/src/modules/sor/pool-data/onChainData.ts @@ -9,6 +9,7 @@ import { formatFixed } from '@ethersproject/bignumber'; import { ComposableStablePool__factory, ConvergentCurvePool__factory, + FXPool__factory, GyroEV2__factory, LinearPool__factory, Multicall__factory, @@ -79,6 +80,7 @@ export async function getOnChainBalances< ...(LinearPool__factory.abi as readonly JsonFragment[]), ...(ComposableStablePool__factory.abi as readonly JsonFragment[]), ...(GyroEV2__factory.abi as readonly JsonFragment[]), + ...(FXPool__factory.abi as readonly JsonFragment[]), ].map((row) => [row.name, row]) ) ); @@ -183,6 +185,13 @@ export async function getOnChainBalances< case 'Element': multiPool.call(`${pool.id}.swapFee`, pool.address, 'percentFee'); break; + case 'FX': + multiPool.call( + `${pool.id}.swapFee`, + pool.address, + 'protocolPercentFee' + ); + break; case 'Gyro2': case 'Gyro3': multiPool.call(`${pool.id}.poolTokens`, vaultAddress, 'getPoolTokens', [ @@ -223,11 +232,14 @@ export async function getOnChainBalances< 'getSwapFeePercentage' ); multiPool.call(`${pool.id}.targets`, pool.address, 'getTargets'); - multiPool.call( - `${pool.id}.rate`, - pool.address, - 'getWrappedTokenRate' - ); + // AaveLinear pools with version === 1 rates will still work + if (pool.poolType === 'AaveLinear' && pool.poolTypeVersion === 1) { + multiPool.call( + `${pool.id}.rate`, + pool.address, + 'getWrappedTokenRate' + ); + } } break; } @@ -319,22 +331,26 @@ export async function getOnChainBalances< ); } - const wrappedIndex = subgraphPools[index].wrappedIndex; - if (wrappedIndex === undefined || onchainData.rate === undefined) { - console.error( - `Linear Pool Missing WrappedIndex or PriceRate: ${poolId}` + if ( + subgraphPools[index].poolType === 'AaveLinear' && + subgraphPools[index].poolTypeVersion === 1 + ) { + const wrappedIndex = subgraphPools[index].wrappedIndex; + if (wrappedIndex === undefined || onchainData.rate === undefined) { + console.error( + `Linear Pool Missing WrappedIndex or PriceRate: ${poolId}` + ); + return; + } + // Update priceRate of wrappedToken + subgraphPools[index].tokens[wrappedIndex].priceRate = formatFixed( + onchainData.rate, + 18 ); - return; } - // Update priceRate of wrappedToken - subgraphPools[index].tokens[wrappedIndex].priceRate = formatFixed( - onchainData.rate, - 18 - ); } - if (subgraphPools[index].poolType !== 'FX') - subgraphPools[index].swapFee = formatFixed(swapFee, 18); + subgraphPools[index].swapFee = formatFixed(swapFee, 18); poolTokens.tokens.forEach((token, i) => { const tokens = subgraphPools[index].tokens; diff --git a/balancer-js/src/modules/sor/pool-data/subgraphPoolDataService.ts b/balancer-js/src/modules/sor/pool-data/subgraphPoolDataService.ts index 4b94e8fac..86dbcef33 100644 --- a/balancer-js/src/modules/sor/pool-data/subgraphPoolDataService.ts +++ b/balancer-js/src/modules/sor/pool-data/subgraphPoolDataService.ts @@ -20,6 +20,7 @@ import { } from '@/lib/graphql/args-builder'; import { isSameAddress } from '@/lib/utils'; +import { Logger } from '@/lib/utils/logger'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export function mapPools(pools: any[]): SubgraphPoolBase[] { @@ -74,7 +75,6 @@ export class SubgraphPoolDataService implements PoolDataService { const pools = await this.getSubgraphPools(queryArgs); const filteredPools = pools.filter((p) => { - if (p.poolType === 'FX') return false; if (!this.network.poolsToIgnore) return true; const index = this.network.poolsToIgnore.findIndex((addr) => isSameAddress(addr, p.address) @@ -88,14 +88,17 @@ export class SubgraphPoolDataService implements PoolDataService { return mapped; } - console.time(`fetching on-chain balances for ${mapped.length} pools`); + const logger = Logger.getInstance(); + logger.time(`fetching on-chain balances for ${mapped.length} pools`); + const onChainBalances = await getOnChainBalances( mapped, this.network.addresses.contracts.multicall, this.network.addresses.contracts.vault, this.provider ); - console.timeEnd(`fetching on-chain balances for ${mapped.length} pools`); + + logger.timeEnd(`fetching on-chain balances for ${mapped.length} pools`); return onChainBalances; } diff --git a/balancer-js/src/modules/sor/sor.module.ts b/balancer-js/src/modules/sor/sor.module.ts index 4d359ed94..cdb91063d 100644 --- a/balancer-js/src/modules/sor/sor.module.ts +++ b/balancer-js/src/modules/sor/sor.module.ts @@ -60,6 +60,7 @@ export class Sor extends SOR { lbpRaisingTokens: network.addresses.tokens?.lbpRaisingTokens, wETHwstETH: network.pools.wETHwstETH, connectingTokens: network.sorConnectingTokens, + triPathMidPoolIds: network.sorTriPathMidPoolIds, }; } diff --git a/balancer-js/src/test/lib/constants.ts b/balancer-js/src/test/lib/constants.ts index e3a529998..48f89bf1a 100644 --- a/balancer-js/src/test/lib/constants.ts +++ b/balancer-js/src/test/lib/constants.ts @@ -122,6 +122,24 @@ export const ADDRESSES = { symbol: 'bbausd', slot: 0, }, + bbausdcV1: { + address: '0x9210F1204b5a24742Eba12f710636D76240dF3d0', + decimals: 18, + symbol: 'bbausdc', + slot: 0, + }, + bbausdtV1: { + address: '0x2BBf681cC4eb09218BEe85EA2a5d3D13Fa40fC0C', + decimals: 18, + symbol: 'bbausdt', + slot: 0, + }, + bbadaiV1: { + address: '0x804CdB9116a10bB78768D3252355a1b18067bF8f', + decimals: 18, + symbol: 'bbadai', + slot: 0, + }, bbausd2: { id: '0xa13a9247ea42d743238089903570127dda72fe4400000000000000000000035d', address: '0xa13a9247ea42d743238089903570127dda72fe44', diff --git a/balancer-js/src/test/lib/exitHelper.ts b/balancer-js/src/test/lib/exitHelper.ts index 0f5d64f9c..64ca3c71a 100644 --- a/balancer-js/src/test/lib/exitHelper.ts +++ b/balancer-js/src/test/lib/exitHelper.ts @@ -5,7 +5,7 @@ import { expect } from 'chai'; import { insert, addSlippage, subSlippage } from '@/lib/utils'; import { accuracy, sendTransactionGetBalances } from '@/test/lib/utils'; -import { PoolWithMethods } from '@/types'; +import { PoolWithMethods, Pool } from '@/types'; export const testExactBptIn = async ( pool: PoolWithMethods, @@ -124,17 +124,52 @@ export const testRecoveryExit = async ( const { to, data, minAmountsOut, expectedAmountsOut, priceImpact } = pool.buildRecoveryExit(signerAddress, bptIn, slippage, toInternalBalance); + await assertRecoveryExit( + signerAddress, + slippage, + to, + data, + minAmountsOut, + expectedAmountsOut, + priceImpact, + pool, + signer, + bptIn, + toInternalBalance + ); +}; + +export const assertRecoveryExit = async ( + signerAddress: string, + slippage: string, + to: string, + data: string, + minAmountsOut: string[], + expectedAmountsOut: string[], + priceImpact: string, + pool: Pool, + signer: JsonRpcSigner, + bptIn: string, + toInternalBalance = false +): Promise => { + console.log(expectedAmountsOut.toString()); + const bptIndex = pool.tokensList.indexOf(pool.address); const { transactionReceipt, balanceDeltas, internalBalanceDeltas } = await sendTransactionGetBalances( - pool.tokensList, + bptIndex === -1 ? [...pool.tokensList, pool.address] : pool.tokensList, signer, signerAddress, to, data ); + expectedAmountsOut.forEach((amount) => expect(BigNumber.from(amount).gt(0))); expect(transactionReceipt.status).to.eq(1); - const expectedDeltas = insert(expectedAmountsOut, pool.bptIndex, bptIn); + const expectedDeltas = + bptIndex === -1 + ? [...expectedAmountsOut, bptIn] + : insert(expectedAmountsOut, bptIndex, bptIn); + // Allow for rounding errors - this has to be fixed on the SOR side in order to be 100% accurate expectedDeltas.forEach((expectedDelta, i) => { const balanceDelta = toInternalBalance diff --git a/balancer-js/src/test/lib/utils.ts b/balancer-js/src/test/lib/utils.ts index dfa28f627..673080934 100644 --- a/balancer-js/src/test/lib/utils.ts +++ b/balancer-js/src/test/lib/utils.ts @@ -471,7 +471,7 @@ export async function findTokenBalanceSlot( const account = await signer.getAddress(); const probeA = encode(['uint256'], [(Math.random() * 10000).toFixed()]); const probeB = encode(['uint256'], [(Math.random() * 10000).toFixed()]); - for (let i = 0; i < 200; i++) { + for (let i = 0; i < 999; i++) { let probedSlot = keccak256(['uint256', 'uint256'], [account, i]); // remove padding for JSON RPC while (probedSlot.startsWith('0x0')) diff --git a/balancer-js/src/types.ts b/balancer-js/src/types.ts index d61f77867..bf6582165 100644 --- a/balancer-js/src/types.ts +++ b/balancer-js/src/types.ts @@ -27,6 +27,7 @@ import type { GraphQLArgs } from './lib/graphql'; import type { AprBreakdown } from '@/modules/pools/apr/apr'; import { SubgraphPoolDataService } from '@/modules/sor/pool-data/subgraphPoolDataService'; import * as Queries from '@/modules/pools/queries/types'; +import { GyroConfigRepository } from '@/modules/data/gyro-config/repository'; export * from '@/modules/data/types'; export { Network, AprBreakdown }; @@ -123,33 +124,35 @@ export interface BalancerNetworkConfig { }; poolsToIgnore?: string[]; sorConnectingTokens?: { symbol: string; address: string }[]; + sorTriPathMidPoolIds?: string[]; } export interface BalancerDataRepositories { + feeDistributor?: BaseFeeDistributor; + feeCollector: Findable; + gaugeShares?: GaugeSharesRepository; + gyroConfigRepository?: GyroConfigRepository; + liquidityGauges?: Findable; + protocolFees?: ProtocolFeesProvider; /** * Why do we need 3 different pools repositories? */ pools: Findable & Searchable; // Does it need to be different from the pools repository? poolsForSor: SubgraphPoolDataService; + poolGauges?: PoolGaugesRepository; + poolJoinExits: PoolJoinExitRepository; // Perhaps better to use a function to get upto date balances when needed. poolsOnChain: Findable & Searchable & Cacheable; - // Replace with a swapFeeRepository, we don't need historic pools for any other reason than to get the swap fee - yesterdaysPools?: Findable & Searchable; - tokenPrices: Findable; + poolShares: PoolSharesRepository; tokenHistoricalPrices: Findable; tokenMeta: Findable; - liquidityGauges?: Findable; - feeDistributor?: BaseFeeDistributor; - feeCollector: Findable; - protocolFees?: ProtocolFeesProvider; + tokenPrices: Findable; tokenYields: Findable; - poolShares: PoolSharesRepository; - poolGauges?: PoolGaugesRepository; - poolJoinExits: PoolJoinExitRepository; - gaugeShares?: GaugeSharesRepository; + // Replace with a swapFeeRepository, we don't need historic pools for any other reason than to get the swap fee + yesterdaysPools?: Findable & Searchable; } export type PoolReference = { diff --git a/balancer-js/tsconfig.json b/balancer-js/tsconfig.json index b0fbd5427..85586e996 100644 --- a/balancer-js/tsconfig.json +++ b/balancer-js/tsconfig.json @@ -12,7 +12,7 @@ "baseUrl": ".", "paths": { "@/*": ["src/*"], - "@balancer-labs/sdk": ["src/index.ts"], + "@balancer-labs/sdk": ["src/index.ts"] } }, "include": ["./src", "./examples", "src/abi/*.json"], diff --git a/balancer-js/tsconfig.testing.json b/balancer-js/tsconfig.testing.json index 55a018125..2c7b28416 100644 --- a/balancer-js/tsconfig.testing.json +++ b/balancer-js/tsconfig.testing.json @@ -1,6 +1,6 @@ { "extends": "./tsconfig.json", "compilerOptions": { - "module": "commonjs", + "module": "commonjs" } } diff --git a/balancer-js/yarn.lock b/balancer-js/yarn.lock index 395b07ed3..afb0ee1af 100644 --- a/balancer-js/yarn.lock +++ b/balancer-js/yarn.lock @@ -507,10 +507,10 @@ "@babel/helper-validator-identifier" "^7.19.1" to-fast-properties "^2.0.0" -"@balancer-labs/sor@4.1.1-beta.13": - version "4.1.1-beta.13" - resolved "https://registry.yarnpkg.com/@balancer-labs/sor/-/sor-4.1.1-beta.13.tgz#b86c7dd693906259e4dd9adf574fafef8e50d048" - integrity sha512-zRezUNHcZ5mzfW8rSZZ83/xmxY//isNMmtdoL5edmFg/GRk3zDoGAO2h7OehksIRm74pqZqwpekzvFCf5pDm8g== +"@balancer-labs/sor@^4.1.1-beta.16": + version "4.1.1-beta.16" + resolved "https://registry.yarnpkg.com/@balancer-labs/sor/-/sor-4.1.1-beta.16.tgz#bee2a863c751ca10320090b4831db26492b1959f" + integrity sha512-EKS7J78r5jKDsGqOs2gzIyhOqYyDAmrwp/nY2bSsfymNXSTr7g3YS418UJl1hSQMBEeN4N2MnH0neM1oJxHHoQ== dependencies: isomorphic-fetch "^2.2.1" @@ -6249,7 +6249,12 @@ webidl-conversions@^3.0.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== -whatwg-fetch@>=0.10.0, whatwg-fetch@^3.4.1: +whatwg-fetch@>=0.10.0: + version "3.6.17" + resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.17.tgz#009bbbfc122b227b74ba1ff31536b3a1a0e0e212" + integrity sha512-c4ghIvG6th0eudYwKZY5keb81wtFz9/WeAHAoy8+r18kcWlitUIrmGFQ2rWEl4UCKUilD3zCLHOIPheHx5ypRQ== + +whatwg-fetch@^3.4.1: version "3.6.2" resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c" integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==