From b87bad3245edf127342801e780358a5301b1581b Mon Sep 17 00:00:00 2001 From: Michael Xiao Date: Wed, 19 Feb 2025 14:25:51 -0500 Subject: [PATCH] Ether Fi token balance (#3690) --- .changeset/thin-cameras-exist.md | 6 + .../token-balance/src/config/SplitMain.json | 331 ++++++++++++++++++ .../src/config/StrategyBaseTVLLimits.json | 275 +++++++++++++++ .../sources/token-balance/src/config/index.ts | 10 + .../token-balance/src/endpoint/etherFi.ts | 55 +++ .../token-balance/src/endpoint/index.ts | 1 + packages/sources/token-balance/src/index.ts | 4 +- .../token-balance/src/transport/etherFi.ts | 99 ++++++ .../test-payload.json | 2 +- 9 files changed, 780 insertions(+), 3 deletions(-) create mode 100644 .changeset/thin-cameras-exist.md create mode 100644 packages/sources/token-balance/src/config/SplitMain.json create mode 100644 packages/sources/token-balance/src/config/StrategyBaseTVLLimits.json create mode 100644 packages/sources/token-balance/src/endpoint/etherFi.ts create mode 100644 packages/sources/token-balance/src/transport/etherFi.ts diff --git a/.changeset/thin-cameras-exist.md b/.changeset/thin-cameras-exist.md new file mode 100644 index 0000000000..ddaf8b7447 --- /dev/null +++ b/.changeset/thin-cameras-exist.md @@ -0,0 +1,6 @@ +--- +'@chainlink/view-function-multi-chain-adapter': patch +'@chainlink/token-balance-adapter': patch +--- + +Ether Fi diff --git a/packages/sources/token-balance/src/config/SplitMain.json b/packages/sources/token-balance/src/config/SplitMain.json new file mode 100644 index 0000000000..e445b5dd6a --- /dev/null +++ b/packages/sources/token-balance/src/config/SplitMain.json @@ -0,0 +1,331 @@ +[ + { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, + { "inputs": [], "name": "Create2Error", "type": "error" }, + { "inputs": [], "name": "CreateError", "type": "error" }, + { + "inputs": [{ "internalType": "address", "name": "newController", "type": "address" }], + "name": "InvalidNewController", + "type": "error" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "accountsLength", "type": "uint256" }, + { "internalType": "uint256", "name": "allocationsLength", "type": "uint256" } + ], + "name": "InvalidSplit__AccountsAndAllocationsMismatch", + "type": "error" + }, + { + "inputs": [{ "internalType": "uint256", "name": "index", "type": "uint256" }], + "name": "InvalidSplit__AccountsOutOfOrder", + "type": "error" + }, + { + "inputs": [{ "internalType": "uint256", "name": "index", "type": "uint256" }], + "name": "InvalidSplit__AllocationMustBePositive", + "type": "error" + }, + { + "inputs": [{ "internalType": "uint32", "name": "allocationsSum", "type": "uint32" }], + "name": "InvalidSplit__InvalidAllocationsSum", + "type": "error" + }, + { + "inputs": [{ "internalType": "uint32", "name": "distributorFee", "type": "uint32" }], + "name": "InvalidSplit__InvalidDistributorFee", + "type": "error" + }, + { + "inputs": [{ "internalType": "bytes32", "name": "hash", "type": "bytes32" }], + "name": "InvalidSplit__InvalidHash", + "type": "error" + }, + { + "inputs": [{ "internalType": "uint256", "name": "accountsLength", "type": "uint256" }], + "name": "InvalidSplit__TooFewAccounts", + "type": "error" + }, + { + "inputs": [{ "internalType": "address", "name": "sender", "type": "address" }], + "name": "Unauthorized", + "type": "error" + }, + { + "anonymous": false, + "inputs": [{ "indexed": true, "internalType": "address", "name": "split", "type": "address" }], + "name": "CancelControlTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "split", "type": "address" }, + { + "indexed": true, + "internalType": "address", + "name": "previousController", + "type": "address" + }, + { "indexed": true, "internalType": "address", "name": "newController", "type": "address" } + ], + "name": "ControlTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": true, "internalType": "address", "name": "split", "type": "address" }], + "name": "CreateSplit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "split", "type": "address" }, + { "indexed": true, "internalType": "contract ERC20", "name": "token", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, + { + "indexed": true, + "internalType": "address", + "name": "distributorAddress", + "type": "address" + } + ], + "name": "DistributeERC20", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "split", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "amount", "type": "uint256" }, + { + "indexed": true, + "internalType": "address", + "name": "distributorAddress", + "type": "address" + } + ], + "name": "DistributeETH", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "split", "type": "address" }, + { + "indexed": true, + "internalType": "address", + "name": "newPotentialController", + "type": "address" + } + ], + "name": "InitiateControlTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": true, "internalType": "address", "name": "split", "type": "address" }], + "name": "UpdateSplit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "account", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "ethAmount", "type": "uint256" }, + { + "indexed": false, + "internalType": "contract ERC20[]", + "name": "tokens", + "type": "address[]" + }, + { "indexed": false, "internalType": "uint256[]", "name": "tokenAmounts", "type": "uint256[]" } + ], + "name": "Withdrawal", + "type": "event" + }, + { + "inputs": [], + "name": "PERCENTAGE_SCALE", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "split", "type": "address" }], + "name": "acceptControl", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "split", "type": "address" }], + "name": "cancelControlTransfer", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address[]", "name": "accounts", "type": "address[]" }, + { "internalType": "uint32[]", "name": "percentAllocations", "type": "uint32[]" }, + { "internalType": "uint32", "name": "distributorFee", "type": "uint32" }, + { "internalType": "address", "name": "controller", "type": "address" } + ], + "name": "createSplit", + "outputs": [{ "internalType": "address", "name": "split", "type": "address" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "split", "type": "address" }, + { "internalType": "contract ERC20", "name": "token", "type": "address" }, + { "internalType": "address[]", "name": "accounts", "type": "address[]" }, + { "internalType": "uint32[]", "name": "percentAllocations", "type": "uint32[]" }, + { "internalType": "uint32", "name": "distributorFee", "type": "uint32" }, + { "internalType": "address", "name": "distributorAddress", "type": "address" } + ], + "name": "distributeERC20", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "split", "type": "address" }, + { "internalType": "address[]", "name": "accounts", "type": "address[]" }, + { "internalType": "uint32[]", "name": "percentAllocations", "type": "uint32[]" }, + { "internalType": "uint32", "name": "distributorFee", "type": "uint32" }, + { "internalType": "address", "name": "distributorAddress", "type": "address" } + ], + "name": "distributeETH", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "split", "type": "address" }], + "name": "getController", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" }, + { "internalType": "contract ERC20", "name": "token", "type": "address" } + ], + "name": "getERC20Balance", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "account", "type": "address" }], + "name": "getETHBalance", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "split", "type": "address" }], + "name": "getHash", + "outputs": [{ "internalType": "bytes32", "name": "", "type": "bytes32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "split", "type": "address" }], + "name": "getNewPotentialController", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "split", "type": "address" }], + "name": "makeSplitImmutable", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address[]", "name": "accounts", "type": "address[]" }, + { "internalType": "uint32[]", "name": "percentAllocations", "type": "uint32[]" }, + { "internalType": "uint32", "name": "distributorFee", "type": "uint32" } + ], + "name": "predictImmutableSplitAddress", + "outputs": [{ "internalType": "address", "name": "split", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "split", "type": "address" }, + { "internalType": "address", "name": "newController", "type": "address" } + ], + "name": "transferControl", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "split", "type": "address" }, + { "internalType": "contract ERC20", "name": "token", "type": "address" }, + { "internalType": "address[]", "name": "accounts", "type": "address[]" }, + { "internalType": "uint32[]", "name": "percentAllocations", "type": "uint32[]" }, + { "internalType": "uint32", "name": "distributorFee", "type": "uint32" }, + { "internalType": "address", "name": "distributorAddress", "type": "address" } + ], + "name": "updateAndDistributeERC20", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "split", "type": "address" }, + { "internalType": "address[]", "name": "accounts", "type": "address[]" }, + { "internalType": "uint32[]", "name": "percentAllocations", "type": "uint32[]" }, + { "internalType": "uint32", "name": "distributorFee", "type": "uint32" }, + { "internalType": "address", "name": "distributorAddress", "type": "address" } + ], + "name": "updateAndDistributeETH", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "split", "type": "address" }, + { "internalType": "address[]", "name": "accounts", "type": "address[]" }, + { "internalType": "uint32[]", "name": "percentAllocations", "type": "uint32[]" }, + { "internalType": "uint32", "name": "distributorFee", "type": "uint32" } + ], + "name": "updateSplit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "walletImplementation", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" }, + { "internalType": "uint256", "name": "withdrawETH", "type": "uint256" }, + { "internalType": "contract ERC20[]", "name": "tokens", "type": "address[]" } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { "stateMutability": "payable", "type": "receive" } +] diff --git a/packages/sources/token-balance/src/config/StrategyBaseTVLLimits.json b/packages/sources/token-balance/src/config/StrategyBaseTVLLimits.json new file mode 100644 index 0000000000..670f3b98e7 --- /dev/null +++ b/packages/sources/token-balance/src/config/StrategyBaseTVLLimits.json @@ -0,0 +1,275 @@ +[ + { + "inputs": [ + { "internalType": "contract IStrategyManager", "name": "_strategyManager", "type": "address" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "internalType": "uint8", "name": "version", "type": "uint8" }], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "uint256", "name": "previousValue", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "newValue", "type": "uint256" } + ], + "name": "MaxPerDepositUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "uint256", "name": "previousValue", "type": "uint256" }, + { "indexed": false, "internalType": "uint256", "name": "newValue", "type": "uint256" } + ], + "name": "MaxTotalDepositsUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "account", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "newPausedStatus", "type": "uint256" } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "contract IPauserRegistry", + "name": "pauserRegistry", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract IPauserRegistry", + "name": "newPauserRegistry", + "type": "address" + } + ], + "name": "PauserRegistrySet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "internalType": "address", "name": "account", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "newPausedStatus", "type": "uint256" } + ], + "name": "Unpaused", + "type": "event" + }, + { + "inputs": [ + { "internalType": "contract IERC20", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "deposit", + "outputs": [{ "internalType": "uint256", "name": "newShares", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "explanation", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getTVLLimits", + "outputs": [ + { "internalType": "uint256", "name": "", "type": "uint256" }, + { "internalType": "uint256", "name": "", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "_maxPerDeposit", "type": "uint256" }, + { "internalType": "uint256", "name": "_maxTotalDeposits", "type": "uint256" }, + { "internalType": "contract IERC20", "name": "_underlyingToken", "type": "address" }, + { "internalType": "contract IPauserRegistry", "name": "_pauserRegistry", "type": "address" } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "contract IERC20", "name": "_underlyingToken", "type": "address" }, + { "internalType": "contract IPauserRegistry", "name": "_pauserRegistry", "type": "address" } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "maxPerDeposit", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "maxTotalDeposits", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "newPausedStatus", "type": "uint256" }], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "pauseAll", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint8", "name": "index", "type": "uint8" }], + "name": "paused", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pauserRegistry", + "outputs": [{ "internalType": "contract IPauserRegistry", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "contract IPauserRegistry", "name": "newPauserRegistry", "type": "address" } + ], + "name": "setPauserRegistry", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "newMaxPerDeposit", "type": "uint256" }, + { "internalType": "uint256", "name": "newMaxTotalDeposits", "type": "uint256" } + ], + "name": "setTVLLimits", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "user", "type": "address" }], + "name": "shares", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "amountShares", "type": "uint256" }], + "name": "sharesToUnderlying", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "amountShares", "type": "uint256" }], + "name": "sharesToUnderlyingView", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "strategyManager", + "outputs": [{ "internalType": "contract IStrategyManager", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalShares", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "amountUnderlying", "type": "uint256" }], + "name": "underlyingToShares", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "amountUnderlying", "type": "uint256" }], + "name": "underlyingToSharesView", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "underlyingToken", + "outputs": [{ "internalType": "contract IERC20", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "newPausedStatus", "type": "uint256" }], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "user", "type": "address" }], + "name": "userUnderlying", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "user", "type": "address" }], + "name": "userUnderlyingView", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "depositor", "type": "address" }, + { "internalType": "contract IERC20", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "amountShares", "type": "uint256" } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/packages/sources/token-balance/src/config/index.ts b/packages/sources/token-balance/src/config/index.ts index 50d4af63f1..91b887fd1c 100644 --- a/packages/sources/token-balance/src/config/index.ts +++ b/packages/sources/token-balance/src/config/index.ts @@ -1,6 +1,16 @@ import { AdapterConfig } from '@chainlink/external-adapter-framework/config' export const config = new AdapterConfig({ + ETHEREUM_RPC_URL: { + description: 'RPC url of Ethereum node', + type: 'string', + default: '', + }, + ETHEREUM_RPC_CHAIN_ID: { + description: 'Ethereum chain id', + type: 'number', + default: 1, + }, ARBITRUM_RPC_URL: { description: 'RPC url of Arbitrum node', type: 'string', diff --git a/packages/sources/token-balance/src/endpoint/etherFi.ts b/packages/sources/token-balance/src/endpoint/etherFi.ts new file mode 100644 index 0000000000..7495a1254d --- /dev/null +++ b/packages/sources/token-balance/src/endpoint/etherFi.ts @@ -0,0 +1,55 @@ +import { AdapterEndpoint } from '@chainlink/external-adapter-framework/adapter' +import { InputParameters } from '@chainlink/external-adapter-framework/validation' +import { config } from '../config' +import { etherFiBalanceTransport } from '../transport/etherFi' + +export const inputParameters = new InputParameters( + { + splitMain: { + required: true, + type: 'string', + description: 'Address of splitMain contract', + }, + splitMainAccount: { + required: true, + type: 'string', + description: 'Input to splitMain contract', + }, + eigenStrategy: { + required: true, + type: 'string', + description: 'Address of eigenStrategy contract', + }, + eigenStrategyUser: { + required: true, + type: 'string', + description: 'Input to eigenStrategy contract', + }, + }, + [ + { + splitMain: '0x2ed6c4B5dA6378c7897AC67Ba9e43102Feb694EE', + splitMainAccount: '', + eigenStrategy: '0x93c4b944D05dfe6df7645A86cd2206016c51564D', + eigenStrategyUser: '', + }, + ], +) + +export type BaseEndpointTypes = { + Parameters: typeof inputParameters.definition + Response: { + Result: string + Data: { + result: string + decimals: number + } + } + Settings: typeof config.settings +} + +export const endpoint = new AdapterEndpoint({ + name: 'etherFi', + transport: etherFiBalanceTransport, + inputParameters, +}) diff --git a/packages/sources/token-balance/src/endpoint/index.ts b/packages/sources/token-balance/src/endpoint/index.ts index 2024b04179..be354b6504 100644 --- a/packages/sources/token-balance/src/endpoint/index.ts +++ b/packages/sources/token-balance/src/endpoint/index.ts @@ -1,2 +1,3 @@ export { endpoint as evm } from './evm' export { endpoint as solvJlp } from './solvJlp' +export { endpoint as etherFi } from './etherFi' diff --git a/packages/sources/token-balance/src/index.ts b/packages/sources/token-balance/src/index.ts index 701761bcb2..23027a6ea3 100644 --- a/packages/sources/token-balance/src/index.ts +++ b/packages/sources/token-balance/src/index.ts @@ -1,13 +1,13 @@ import { expose, ServerInstance } from '@chainlink/external-adapter-framework' import { Adapter } from '@chainlink/external-adapter-framework/adapter' import { config } from './config' -import { evm, solvJlp } from './endpoint' +import { evm, solvJlp, etherFi } from './endpoint' export const adapter = new Adapter({ defaultEndpoint: evm.name, name: 'TOKEN_BALANCE', config, - endpoints: [evm, solvJlp], + endpoints: [evm, solvJlp, etherFi], }) export const server = (): Promise => expose(adapter) diff --git a/packages/sources/token-balance/src/transport/etherFi.ts b/packages/sources/token-balance/src/transport/etherFi.ts new file mode 100644 index 0000000000..4b81a5e717 --- /dev/null +++ b/packages/sources/token-balance/src/transport/etherFi.ts @@ -0,0 +1,99 @@ +import { TransportDependencies } from '@chainlink/external-adapter-framework/transports' +import { AdapterResponse, makeLogger, sleep } from '@chainlink/external-adapter-framework/util' +import { SubscriptionTransport } from '@chainlink/external-adapter-framework/transports/abstract/subscription' +import { EndpointContext } from '@chainlink/external-adapter-framework/adapter' +import { AdapterInputError } from '@chainlink/external-adapter-framework/validation/error' +import { BaseEndpointTypes, inputParameters } from '../endpoint/etherFi' +import { ethers } from 'ethers' +import SplitMain from '../config/SplitMain.json' +import StrategyBaseTVLLimits from '../config/StrategyBaseTVLLimits.json' + +const logger = makeLogger('Token Balances - EtherFi') + +type RequestParams = typeof inputParameters.validated + +const RESULT_DECIMALS = 18 + +export class EtherFiBalanceTransport extends SubscriptionTransport { + provider!: ethers.JsonRpcProvider + + async initialize( + dependencies: TransportDependencies, + adapterSettings: BaseEndpointTypes['Settings'], + endpointName: string, + transportName: string, + ): Promise { + if (!adapterSettings.ETHEREUM_RPC_URL) { + logger.error('ETHEREUM_RPC_URL is missing') + } else { + this.provider = new ethers.JsonRpcProvider( + adapterSettings.ETHEREUM_RPC_URL, + adapterSettings.ETHEREUM_RPC_CHAIN_ID, + ) + } + + await super.initialize(dependencies, adapterSettings, endpointName, transportName) + } + + async backgroundHandler(context: EndpointContext, entries: RequestParams[]) { + await Promise.all(entries.map(async (param) => this.handleRequest(param))) + await sleep(context.adapterSettings.BACKGROUND_EXECUTE_MS) + } + + async handleRequest(param: RequestParams) { + let response: AdapterResponse + try { + response = await this._handleRequest(param) + } catch (e: unknown) { + const errorMessage = e instanceof Error ? e.message : 'Unknown error occurred' + logger.error(e, errorMessage) + response = { + statusCode: (e as AdapterInputError)?.statusCode || 502, + errorMessage, + timestamps: { + providerDataRequestedUnixMs: 0, + providerDataReceivedUnixMs: 0, + providerIndicatedTimeUnixMs: undefined, + }, + } + } + await this.responseCache.write(this.name, [{ params: param, response }]) + } + + async _handleRequest( + param: RequestParams, + ): Promise> { + const providerDataRequestedUnixMs = Date.now() + + const splitMainContract = new ethers.Contract(param.splitMain, SplitMain, this.provider) + const splitMainBalance = BigInt(await splitMainContract.getETHBalance(param.splitMainAccount)) + + const eigenContract = new ethers.Contract( + param.eigenStrategy, + StrategyBaseTVLLimits, + this.provider, + ) + const shares = await eigenContract.shares(param.eigenStrategyUser) + const eigenBalance = await eigenContract.sharesToUnderlyingView(shares) + + return { + data: { + result: String(splitMainBalance + eigenBalance), + decimals: RESULT_DECIMALS, + }, + statusCode: 200, + result: String(splitMainBalance + eigenBalance), + timestamps: { + providerDataRequestedUnixMs, + providerDataReceivedUnixMs: Date.now(), + providerIndicatedTimeUnixMs: undefined, + }, + } + } + + getSubscriptionTtlFromConfig(adapterSettings: BaseEndpointTypes['Settings']): number { + return adapterSettings.WARMUP_SUBSCRIPTION_TTL + } +} + +export const etherFiBalanceTransport = new EtherFiBalanceTransport() diff --git a/packages/sources/view-function-multi-chain/test-payload.json b/packages/sources/view-function-multi-chain/test-payload.json index e0348e5505..d522475d08 100644 --- a/packages/sources/view-function-multi-chain/test-payload.json +++ b/packages/sources/view-function-multi-chain/test-payload.json @@ -2,6 +2,6 @@ "requests": [{ "contract": "0x2c1d072e956AFFC0D435Cb7AC38EF18d24d9127c", "function": "function latestAnswer() view returns (int256)", - "network": "ETHEREUM_MAINNET" + "network": "ETHEREUM" }] }