From e4181bb058901890a24db566cb905a1a75ad49c6 Mon Sep 17 00:00:00 2001 From: gmbronco <83549293+gmbronco@users.noreply.github.com> Date: Thu, 5 Oct 2023 17:45:23 +0200 Subject: [PATCH] gyro protocol fees --- modules/network/mainnet.ts | 3 + modules/network/network-config-types.ts | 3 + modules/network/optimism.ts | 3 + modules/network/polygon.ts | 3 + modules/pool/abi/GyroConfig.json | 324 ++++++++++++++++++ .../apr-data-sources/swap-fee-apr.service.ts | 11 +- .../pool/lib/pool-on-chain-data.service.ts | 60 +++- 7 files changed, 402 insertions(+), 5 deletions(-) create mode 100644 modules/pool/abi/GyroConfig.json diff --git a/modules/network/mainnet.ts b/modules/network/mainnet.ts index 0df0e92f4..c41b220c5 100644 --- a/modules/network/mainnet.ts +++ b/modules/network/mainnet.ts @@ -76,6 +76,9 @@ export const mainnetNetworkData: NetworkData = { }, gaugeControllerAddress: '0xc128468b7ce63ea702c1f104d55a2566b13d3abd', gaugeControllerHelperAddress: '0x8e5698dc4897dc12243c8642e77b4f21349db97c', + gyro: { + config: '0xac89cc9d78bbad7eb3a02601b4d65daa1f908aa6' + }, balancer: { vault: '0xba12222222228d8ba445958a75a0704d566bf2c8', tokenAdmin: '0xf302f9f50958c5593770fdf4d4812309ff77414f', diff --git a/modules/network/network-config-types.ts b/modules/network/network-config-types.ts index 29c27374b..a34c742e2 100644 --- a/modules/network/network-config-types.ts +++ b/modules/network/network-config-types.ts @@ -91,6 +91,9 @@ export interface NetworkData { }; gaugeControllerAddress?: string; gaugeControllerHelperAddress?: string; + gyro?: { + config: string; + }; balancer: { vault: string; tokenAdmin?: string; diff --git a/modules/network/optimism.ts b/modules/network/optimism.ts index b34672a9e..815d4c7ad 100644 --- a/modules/network/optimism.ts +++ b/modules/network/optimism.ts @@ -75,6 +75,9 @@ const optimismNetworkData: NetworkData = { address: '0xc128a9954e6c874ea3d62ce62b468ba073093f25', delegationProxy: '0x9da18982a33fd0c7051b19f0d7c76f2d5e7e017c', }, + gyro: { + config: '0x32acb44fc929339b9f16f0449525cc590d2a23f3' + }, balancer: { vault: '0xba12222222228d8ba445958a75a0704d566bf2c8', composableStablePoolFactories: [ diff --git a/modules/network/polygon.ts b/modules/network/polygon.ts index d7d46c1e4..f5041268b 100644 --- a/modules/network/polygon.ts +++ b/modules/network/polygon.ts @@ -66,6 +66,9 @@ const polygonNetworkData: NetworkData = { address: '0xc128a9954e6c874ea3d62ce62b468ba073093f25', delegationProxy: '0x0f08eef2c785aa5e7539684af04755dec1347b7c', }, + gyro: { + config: '0xfdc2e9e03f515804744a40d0f8d25c16e93fbe67' + }, balancer: { vault: '0xba12222222228d8ba445958a75a0704d566bf2c8', composableStablePoolFactories: [ diff --git a/modules/pool/abi/GyroConfig.json b/modules/pool/abi/GyroConfig.json new file mode 100644 index 000000000..2bc687538 --- /dev/null +++ b/modules/pool/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/modules/pool/lib/apr-data-sources/swap-fee-apr.service.ts b/modules/pool/lib/apr-data-sources/swap-fee-apr.service.ts index e7a0bf07b..2b0eaa6e6 100644 --- a/modules/pool/lib/apr-data-sources/swap-fee-apr.service.ts +++ b/modules/pool/lib/apr-data-sources/swap-fee-apr.service.ts @@ -29,13 +29,18 @@ export class SwapFeeAprService implements PoolAprService { ? (pool.dynamicData.fees24h * 365) / pool.dynamicData.totalLiquidity : 0; - let userApr = apr * (1 - this.swapProtocolFeePercentage); - + let protocolFee = this.swapProtocolFeePercentage; + if (pool.type === 'GYROE') { + // Gyro has custom protocol fee structure + protocolFee = parseFloat(pool.dynamicData.protocolYieldFee || '0'); + } if (pool.dynamicData.isInRecoveryMode || pool.type === 'LIQUIDITY_BOOTSTRAPPING') { // pool does not collect any protocol fees - userApr = apr; + protocolFee = 0; } + let userApr = apr * (1 - protocolFee); + // TODO: clean this up if (userApr > MAX_DB_INT) { userApr = 0; diff --git a/modules/pool/lib/pool-on-chain-data.service.ts b/modules/pool/lib/pool-on-chain-data.service.ts index 0e662fc64..66e6bc82f 100644 --- a/modules/pool/lib/pool-on-chain-data.service.ts +++ b/modules/pool/lib/pool-on-chain-data.service.ts @@ -1,3 +1,4 @@ +// TODO: let's make it simpler, it's already refactored in the SDK import ElementPoolAbi from '../abi/ConvergentCurvePool.json'; import LinearPoolAbi from '../abi/LinearPool.json'; import LiquidityBootstrappingPoolAbi from '../abi/LiquidityBootstrappingPool.json'; @@ -19,6 +20,10 @@ import { TokenService } from '../../token/token.service'; import { networkContext } from '../../network/network-context.service'; import { prismaBulkExecuteOperations } from '../../../prisma/prisma-util'; import { Multicaller3 } from '../../web3/multicaller3'; +import { formatBytes32String } from '@ethersproject/strings'; +import { keccak256 } from '@ethersproject/solidity'; +import { defaultAbiCoder } from '@ethersproject/abi'; +import GyroConfigAbi from '../abi/GyroConfig.json'; interface PoolStatusResult { [key: string]: { @@ -49,6 +54,7 @@ interface MulticallExecuteResult { protocolFeePercentageCache?: number | undefined; tokenRates?: BigNumber[] | undefined; metaPriceRateCache?: [BigNumber, BigNumber, BigNumber][] | undefined; + gyroProtocolFee?: BigNumber | undefined; } const SUPPORTED_POOL_TYPES: PrismaPoolType[] = [ @@ -66,6 +72,8 @@ const SUPPORTED_POOL_TYPES: PrismaPoolType[] = [ ]; export class PoolOnChainDataService { + gyroDefaultFee = 0; + gyroPoolTypeFee = 0; constructor(private readonly tokenService: TokenService) {} public async updateOnChainStatus(poolIds: string[]): Promise { @@ -158,6 +166,7 @@ export class PoolOnChainDataService { ...LiquidityBootstrappingPoolAbi, ...ComposableStablePoolAbi, ...GyroEV2Abi, + ...GyroConfigAbi, ...VaultAbi, ...aTokenRateProvider, ...WeightedPoolAbi, @@ -172,6 +181,22 @@ export class PoolOnChainDataService { const multiPool = new Multicaller3(abis); + // Adding Gyro default fee calls + const feeKey = formatBytes32String('PROTOCOL_SWAP_FEE_PERC'); + if (networkContext.data.gyro?.config) { + const eclpKey = keccak256( + ['bytes'], + [ + defaultAbiCoder.encode( + ['bytes32', 'bytes32'], + [feeKey, formatBytes32String('E-CLP')] + ), + ] + ); + multiPool.call('gyro.default', networkContext.data.gyro.config, 'getUint', [feeKey]); + multiPool.call('gyro.eclp', networkContext.data.gyro.config, 'getUint', [eclpKey]); + } + filteredPools.forEach((pool) => { if (!SUPPORTED_POOL_TYPES.includes(pool.type || '')) { console.error(`Unknown pool type: ${pool.type} ${pool.id}`); @@ -217,6 +242,20 @@ export class PoolOnChainDataService { } else if (isGyroPool(pool)) { multiPool.call(`${pool.id}.swapFee`, pool.address, 'getSwapFeePercentage'); multiPool.call(`${pool.id}.rate`, pool.address, 'getRate'); + + // Get fee from Gyro config pool + if (networkContext.data.gyro?.config) { + const poolFeeKey = keccak256( + ['bytes'], + [ + defaultAbiCoder.encode( + ['bytes32', 'uint256'], + [feeKey, pool.address] + ), + ] + ); + multiPool.call(`${pool.id}.gyroProtocolFee`, networkContext.data.gyro.config, 'getUint', [poolFeeKey]); + } } if (pool.type === 'LIQUIDITY_BOOTSTRAPPING' || pool.type === 'INVESTMENT') { @@ -259,9 +298,15 @@ export class PoolOnChainDataService { }); let poolsOnChainData = {} as Record; + let gyroDefaults = { + default: 0, + eclp: 0, + } try { - poolsOnChainData = (await multiPool.execute()) as Record; + const { gyro, ..._poolsOnChainData } = (await multiPool.execute()); + poolsOnChainData = _poolsOnChainData; + gyroDefaults = gyro; } catch (err: any) { console.error(err); throw `Issue with multicall execution. ${err}`; @@ -332,11 +377,22 @@ export class PoolOnChainDataService { ? onchainData.swapEnabled : pool.dynamicData?.swapEnabled; - const yieldProtocolFeePercentage = + let yieldProtocolFeePercentage = typeof onchainData.protocolFeePercentageCache !== 'undefined' ? formatFixed(onchainData.protocolFeePercentageCache, 18) : `${networkContext.data.balancer.yieldProtocolFeePercentage}`; + // Setting Gyro protocol fee + if (onchainData.gyroProtocolFee) { + yieldProtocolFeePercentage = formatFixed(onchainData.gyroProtocolFee, 18); + } else if (pool.type.includes('GYRO')) { + if (pool.type === 'GYROE') { + yieldProtocolFeePercentage = formatFixed(gyroDefaults.eclp, 18) || '0'; + } else { + yieldProtocolFeePercentage = formatFixed(gyroDefaults.default, 18) || '0'; + } + } + if ( pool.dynamicData && (pool.dynamicData.swapFee !== swapFee ||