From e80a3ec5adb90fd0426ee826ac3faca9ecae68ee Mon Sep 17 00:00:00 2001 From: neanvo Date: Thu, 20 Jun 2024 18:07:58 +0300 Subject: [PATCH 01/22] wip: draft integration for instETH --- src/abi/inception/inception-vault.json | 991 ++++++++++++++++++++++++ src/dex/inception/config.ts | 23 + src/dex/inception/inception-e2e.test.ts | 71 ++ src/dex/inception/inception-pool.ts | 55 ++ src/dex/inception/inception.ts | 183 +++++ src/dex/inception/types.ts | 12 + src/dex/inception/utils.ts | 29 + src/dex/index.ts | 2 + tests/constants-e2e.ts | 48 ++ 9 files changed, 1414 insertions(+) create mode 100644 src/abi/inception/inception-vault.json create mode 100644 src/dex/inception/config.ts create mode 100644 src/dex/inception/inception-e2e.test.ts create mode 100644 src/dex/inception/inception-pool.ts create mode 100644 src/dex/inception/inception.ts create mode 100644 src/dex/inception/types.ts create mode 100644 src/dex/inception/utils.ts diff --git a/src/abi/inception/inception-vault.json b/src/abi/inception/inception-vault.json new file mode 100644 index 000000000..5e8e85bd8 --- /dev/null +++ b/src/abi/inception/inception-vault.json @@ -0,0 +1,991 @@ +[ + { "inputs": [], "name": "AlreadyDelegated", "type": "error" }, + { "inputs": [], "name": "DelegationManagerImmutable", "type": "error" }, + { "inputs": [], "name": "EigenLayerOperatorAlreadyExists", "type": "error" }, + { + "inputs": [ + { "internalType": "uint256", "name": "max", "type": "uint256" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "ExceedsMaxPerDeposit", + "type": "error" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "max", "type": "uint256" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "ExceedsMaxTotalDeposited", + "type": "error" + }, + { "inputs": [], "name": "ImplementationNotSet", "type": "error" }, + { "inputs": [], "name": "InceptionOnPause", "type": "error" }, + { "inputs": [], "name": "InconsistentData", "type": "error" }, + { + "inputs": [ + { "internalType": "uint256", "name": "capacity", "type": "uint256" } + ], + "name": "InsufficientCapacity", + "type": "error" + }, + { "inputs": [], "name": "NotEigenLayerOperator", "type": "error" }, + { "inputs": [], "name": "NullParams", "type": "error" }, + { "inputs": [], "name": "OperatorNotRegistered", "type": "error" }, + { "inputs": [], "name": "RestakerNotRegistered", "type": "error" }, + { + "inputs": [ + { "internalType": "address", "name": "assetAddress", "type": "address" } + ], + "name": "TransferAssetFailed", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "assetAddress", "type": "address" } + ], + "name": "TransferAssetFromFailed", + "type": "error" + }, + { "inputs": [], "name": "WithdrawFutile", "type": "error" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "stakerAddress", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "operatorAddress", + "type": "address" + } + ], + "name": "DelegatedTo", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "prevValue", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newValue", + "type": "address" + } + ], + "name": "DelegationManagerChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "iShares", + "type": "uint256" + } + ], + "name": "Deposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "prevValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newValue", + "type": "uint256" + } + ], + "name": "DepositFeeChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "stakerAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "DepositedToEL", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "newELOperator", + "type": "address" + } + ], + "name": "ELOperatorAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "prevValue", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newValue", + "type": "address" + } + ], + "name": "ImplementationUpgraded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "prevValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newValue", + "type": "uint256" + } + ], + "name": "MinAmountChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "prevValue", + "type": "string" + }, + { + "indexed": false, + "internalType": "string", + "name": "newValue", + "type": "string" + } + ], + "name": "NameChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "prevValue", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newValue", + "type": "address" + } + ], + "name": "OperatorChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Redeem", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256[]", + "name": "withdrawals", + "type": "uint256[]" + } + ], + "name": "RedeemedRequests", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "code", + "type": "bytes32" + } + ], + "name": "ReferralCode", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "restaker", + "type": "address" + } + ], + "name": "RestakerDeployed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "stakerAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "contract IStrategy", + "name": "strategy", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "withdrawalStartBlock", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "address", + "name": "delegatedAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "nonce", + "type": "uint256" + } + ], + "name": "StartWithdrawal", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "receiver", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "iShares", + "type": "uint256" + } + ], + "name": "Withdraw", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "totalAmount", + "type": "uint256" + } + ], + "name": "WithdrawalClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "depositor", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint96", + "name": "nonce", + "type": "uint96" + }, + { + "indexed": false, + "internalType": "address", + "name": "withdrawer", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "delegatedAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes32", + "name": "withdrawalRoot", + "type": "bytes32" + } + ], + "name": "WithdrawalQueued", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "asset", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "ethAmount", + "type": "uint256" + } + ], + "name": "Withdrawn", + "type": "event" + }, + { + "inputs": [ + { "internalType": "address", "name": "newELOperator", "type": "address" } + ], + "name": "addELOperator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "restaker", "type": "address" }, + { + "components": [ + { "internalType": "address", "name": "staker", "type": "address" }, + { + "internalType": "address", + "name": "delegatedTo", + "type": "address" + }, + { + "internalType": "address", + "name": "withdrawer", + "type": "address" + }, + { "internalType": "uint256", "name": "nonce", "type": "uint256" }, + { "internalType": "uint32", "name": "startBlock", "type": "uint32" }, + { + "internalType": "contract IStrategy[]", + "name": "strategies", + "type": "address[]" + }, + { "internalType": "uint256[]", "name": "shares", "type": "uint256[]" } + ], + "internalType": "struct IDelegationManager.Withdrawal[]", + "name": "withdrawals", + "type": "tuple[]" + } + ], + "name": "claimCompletedWithdrawals", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "claimerWithdrawalsQueue", + "outputs": [ + { "internalType": "uint256", "name": "epoch", "type": "uint256" }, + { "internalType": "address", "name": "receiver", "type": "address" }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "iShares", "type": "uint256" } + ], + "name": "convertToAssets", + "outputs": [ + { "internalType": "uint256", "name": "assets", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "assets", "type": "uint256" } + ], + "name": "convertToShares", + "outputs": [ + { "internalType": "uint256", "name": "shares", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "internalType": "address", "name": "elOperator", "type": "address" }, + { "internalType": "bytes32", "name": "approverSalt", "type": "bytes32" }, + { + "components": [ + { "internalType": "bytes", "name": "signature", "type": "bytes" }, + { "internalType": "uint256", "name": "expiry", "type": "uint256" } + ], + "internalType": "struct IDelegationManager.SignatureWithExpiry", + "name": "approverSignatureAndExpiry", + "type": "tuple" + } + ], + "name": "delegateToOperator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "elOperator", "type": "address" }, + { "internalType": "bytes32", "name": "approverSalt", "type": "bytes32" }, + { + "components": [ + { "internalType": "bytes", "name": "signature", "type": "bytes" }, + { "internalType": "uint256", "name": "expiry", "type": "uint256" } + ], + "internalType": "struct IDelegationManager.SignatureWithExpiry", + "name": "approverSignatureAndExpiry", + "type": "tuple" + } + ], + "name": "delegateToOperatorFromVault", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "delegationManager", + "outputs": [ + { + "internalType": "contract IDelegationManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "internalType": "address", "name": "receiver", "type": "address" } + ], + "name": "deposit", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "depositAssetIntoStrategyFromVault", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "internalType": "address", "name": "receiver", "type": "address" }, + { "internalType": "bytes32", "name": "code", "type": "bytes32" } + ], + "name": "depositWithReferral", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "epoch", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amount", "type": "uint256" }, + { "internalType": "address", "name": "restaker", "type": "address" } + ], + "name": "forceUndelegateRecovery", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "elOperator", "type": "address" } + ], + "name": "getDelegatedTo", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPendingWithdrawalAmountFromEL", + "outputs": [ + { "internalType": "uint256", "name": "total", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "claimer", "type": "address" } + ], + "name": "getPendingWithdrawalOf", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTotalDelegated", + "outputs": [ + { "internalType": "uint256", "name": "total", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTotalDeposited", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "implementation", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "inceptionToken", + "outputs": [ + { + "internalType": "contract IInceptionToken", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "claimer", "type": "address" } + ], + "name": "isAbleToRedeem", + "outputs": [ + { "internalType": "bool", "name": "able", "type": "bool" }, + { "internalType": "uint256[]", "name": "", "type": "uint256[]" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "minAmount", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ratio", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "receiver", "type": "address" } + ], + "name": "redeem", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "redeemReservedAmount", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "name": "restakers", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IDelegationManager", + "name": "newDelegationManager", + "type": "address" + } + ], + "name": "setDelegationManager", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "newMinAmount", "type": "uint256" } + ], + "name": "setMinAmount", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string", "name": "newVaultName", "type": "string" } + ], + "name": "setName", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "newOperator", "type": "address" } + ], + "name": "setOperator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "strategy", + "outputs": [ + { "internalType": "contract IStrategy", "name": "", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "strategyManager", + "outputs": [ + { + "internalType": "contract IStrategyManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalAmountToWithdraw", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalAssets", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "elOperatorAddress", + "type": "address" + }, + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "undelegateFrom", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amount", "type": "uint256" } + ], + "name": "undelegateVault", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "updateEpoch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "upgradeTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "iShares", "type": "uint256" }, + { "internalType": "address", "name": "receiver", "type": "address" } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/src/dex/inception/config.ts b/src/dex/inception/config.ts new file mode 100644 index 000000000..3213ef092 --- /dev/null +++ b/src/dex/inception/config.ts @@ -0,0 +1,23 @@ +import { DexParams } from './types'; +import { AdapterMappings, DexConfigMap } from '../../types'; +import { Network, SwapSide } from '../../constants'; + +export const InceptionConfig: DexConfigMap = { + instETH: { + [Network.MAINNET]: { + vault: '0x814CC6B8fd2555845541FB843f37418b05977d8d', + baseTokenSlug: 'STETH', + }, + }, + // inETH: { + // [Network.MAINNET]: { + // vault: "0xf073bAC22DAb7FaF4a3Dd6c6189a70D54110525C", + // baseTokenSlug: "ETH", + // }, + // }, +}; + +export const Adapters: Record = { + //TODO fixme + [Network.MAINNET]: { [SwapSide.BUY]: [{ name: 'Adapter01', index: 1 }] }, +}; diff --git a/src/dex/inception/inception-e2e.test.ts b/src/dex/inception/inception-e2e.test.ts new file mode 100644 index 000000000..9c7c0baf0 --- /dev/null +++ b/src/dex/inception/inception-e2e.test.ts @@ -0,0 +1,71 @@ +/* eslint-disable no-console */ +import dotenv from 'dotenv'; +dotenv.config(); + +import { testE2E } from '../../../tests/utils-e2e'; +import { Holders, Tokens } from '../../../tests/constants-e2e'; +import { ContractMethod, Network, SwapSide } from '../../constants'; +import { StaticJsonRpcProvider } from '@ethersproject/providers'; +import { generateConfig } from '../../config'; +import { InceptionConfig } from './config'; +import { DexParams } from './types'; + +function testForNetwork( + network: Network, + dexKey: string, + inceptionSlug: string, + config: DexParams, +) { + const provider = new StaticJsonRpcProvider( + generateConfig(network).privateHttpProvider, + network, + ); + + const baseSlug = config.baseTokenSlug; + const tokens = Tokens[network]; + const holders = Holders[network]; + + const sideToContractMethods = new Map([ + // TBD: SwapSide,Sell + [SwapSide.BUY, ['deposit' as ContractMethod]], + ]); + + describe(`${inceptionSlug} on ${network}`, () => { + sideToContractMethods.forEach((contractMethods, side) => + describe(`${side}`, () => { + contractMethods.forEach((contractMethod: ContractMethod) => { + describe(`${contractMethod}`, () => { + it(`${baseSlug} -> ${inceptionSlug}`, async () => { + await testE2E( + tokens[baseSlug], + tokens[inceptionSlug], + // holders[baseSlug], + '0x43594da5d6A03b2137a04DF5685805C676dEf7cB', // holders[baseSlug] holder unfortunately has withdrawed the number of tokens I needed + '1000000000', + side, + inceptionSlug, + contractMethod, + network, + provider, + ); + }); + }); + }); + }), + ); + }); +} + +describe('Inception E2E', () => { + const dexKey = 'Inception'; + + describe('Mainnet', () => { + const network = Network.MAINNET; + + Object.entries(InceptionConfig).forEach( + ([inceptionSymbol, chainConfig]) => { + testForNetwork(network, dexKey, inceptionSymbol, chainConfig[network]); + }, + ); + }); +}); diff --git a/src/dex/inception/inception-pool.ts b/src/dex/inception/inception-pool.ts new file mode 100644 index 000000000..c2b67f315 --- /dev/null +++ b/src/dex/inception/inception-pool.ts @@ -0,0 +1,55 @@ +import VaultABI from '../../abi/inception/inception-vault.json'; + +import { Interface } from '@ethersproject/abi'; +import { DeepReadonly } from 'ts-essentials'; +import { Log, Logger } from '../../types'; +import { StatefulEventSubscriber } from '../../stateful-event-subscriber'; +import { IDexHelper } from '../../dex-helper/idex-helper'; +import { DexParams, PoolState } from './types'; +import { getOnChainRatio } from './utils'; +import { BI_POWS } from '../../bigint-constants'; + +export class InceptionPool extends StatefulEventSubscriber { + logDecoder: (log: Log) => any; + + addressesSubscribed: string[]; + + constructor( + readonly parentName: string, + protected network: number, + protected dexHelper: IDexHelper, + logger: Logger, + protected config: DexParams, + protected inceptionIface = new Interface(VaultABI), + ) { + super(parentName, 'inception_pool', dexHelper, logger); + + this.logDecoder = (log: Log) => this.inceptionIface.parseLog(log); + this.addressesSubscribed = [this.config.vault]; + } + + protected processLog( + state: DeepReadonly, + log: Readonly, + ): DeepReadonly | null { + return state; + } + + async generateState(blockNumber: number): Promise> { + const state = await getOnChainRatio( + this.dexHelper.multiContract, + this.config.vault, + this.inceptionIface, + blockNumber, + ); + + return state; + } + + getPrice(blockNumber: number, ethAmount: bigint): bigint { + const state = this.getState(blockNumber); + if (!state) throw new Error('Cannot compute price'); + + return (ethAmount * state.ratio) / BI_POWS[18]; + } +} diff --git a/src/dex/inception/inception.ts b/src/dex/inception/inception.ts new file mode 100644 index 000000000..660c806f2 --- /dev/null +++ b/src/dex/inception/inception.ts @@ -0,0 +1,183 @@ +import { Interface, JsonFragment } from '@ethersproject/abi'; +import { AsyncOrSync } from 'ts-essentials'; +import { NumberAsString, SwapSide } from '@paraswap/core'; +import { + AdapterExchangeParam, + Address, + DexExchangeParam, + ExchangePrices, + Logger, + PoolLiquidity, + PoolPrices, + Token, +} from '../../types'; +import INCEPTION_ABI from '../../abi/inception/inception-vault.json'; +import { Network, NULL_ADDRESS } from '../../constants'; +import * as CALLDATA_GAS_COST from '../../calldata-gas-cost'; +import { getDexKeysWithNetwork } from '../../utils'; +import { IDex } from '../../dex/idex'; +import { IDexHelper } from '../../dex-helper/idex-helper'; +import { InceptionData } from './types'; +import { SimpleExchange } from '../simple-exchange'; +import { Adapters, InceptionConfig } from './config'; +import { InceptionPool } from './inception-pool'; +import { ethers } from 'ethers'; +import { BI_POWS } from '../../bigint-constants'; + +export const depositETHFunction = 'deposit'; + +export class Inception extends SimpleExchange implements IDex { + protected inceptionPool: InceptionPool; + protected vaultInterface: Interface; + readonly hasConstantPriceLargeAmounts = false; + readonly needWrapNative = true; + readonly isFeeOnTransferSupported = false; + + public static dexKeysWithNetwork: { key: string; networks: Network[] }[] = + getDexKeysWithNetwork(InceptionConfig); + + logger: Logger; + + constructor( + readonly network: Network, + readonly dexKey: string, + readonly dexHelper: IDexHelper, + protected config = InceptionConfig[dexKey][network], + protected adapters = Adapters[network] || {}, + ) { + super(dexHelper, dexKey); + this.logger = dexHelper.getLogger(dexKey); + this.vaultInterface = new Interface(INCEPTION_ABI as JsonFragment[]); + this.inceptionPool = new InceptionPool( + this.dexKey, + network, + dexHelper, + this.logger, + this.config, + this.vaultInterface, + ); + } + + async initializePricing(blockNumber: number) { + const data: { returnData: any[] } = + await this.dexHelper.multiContract.methods + .aggregate([ + { + target: this.config.vault, + callData: this.vaultInterface.encodeFunctionData('ratio', []), + }, + ]) + .call({}, blockNumber); + + const decodedData = data.returnData.map(d => + ethers.utils.defaultAbiCoder.decode(['uint256'], d), + ); + const [ratio] = decodedData.map(d => BigInt(d[0].toString())); + + await Promise.all([ + this.inceptionPool.initialize(blockNumber, { + state: { ratio }, + }), + ]); + } + + getAdapters(side: SwapSide): { name: string; index: number }[] | null { + return this.adapters[side] ? this.adapters[side] : null; + } + + async getPoolIdentifiers( + srcToken: Token, + destToken: Token, + side: SwapSide, + blockNumber: number, + ): Promise { + return [`${srcToken.address}_${destToken.address}`.toLowerCase()]; + } + + async getPricesVolume( + srcToken: Token, + destToken: Token, + amounts: bigint[], + side: SwapSide, + blockNumber: number, + limitPools?: string[], + ): Promise> { + const pool = this.inceptionPool; + + if (!pool.getState(blockNumber)) return null; + + const unitIn = BI_POWS[18]; + const unitOut = pool.getPrice(blockNumber, unitIn); + const amountsOut = amounts.map(amountIn => + pool.getPrice(blockNumber, amountIn), + ); + + return [ + { + prices: amountsOut, + unit: unitOut, + data: { + ratio: unitOut, + }, + exchange: this.dexKey, + poolIdentifier: + `${srcToken.address}_${destToken.address}`.toLowerCase(), + gasCost: 120_000, + poolAddresses: [destToken.address], + }, + ]; + } + + getCalldataGasCost(poolPrices: PoolPrices): number | number[] { + return CALLDATA_GAS_COST.DEX_OVERHEAD + CALLDATA_GAS_COST.LENGTH_SMALL; + } + + getAdapterParam( + srcToken: string, + destToken: string, + srcAmount: string, + destAmount: string, + data: InceptionData, + side: SwapSide, + ): AdapterExchangeParam { + return { + targetExchange: NULL_ADDRESS, + payload: '0x', + networkFee: '0', + }; + } + + getDexParam( + srcToken: Address, + destToken: Address, + srcAmount: NumberAsString, + destAmount: NumberAsString, + recipient: Address, + data: InceptionData, + side: SwapSide, + ): DexExchangeParam { + const swapData = this.vaultInterface.encodeFunctionData( + depositETHFunction, + [destAmount, recipient], + ); + + return { + needWrapNative: this.needWrapNative, + dexFuncHasRecipient: false, + exchangeData: swapData, + targetExchange: destToken, + swappedAmountNotPresentInExchangeData: true, + returnAmountPos: undefined, + }; + } + async updatePoolState(): Promise {} + + async getTopPoolsForToken( + tokenAddress: Address, + limit: number, + ): Promise { + return []; + } + + releaseResources(): AsyncOrSync {} +} diff --git a/src/dex/inception/types.ts b/src/dex/inception/types.ts new file mode 100644 index 000000000..2a9d4827b --- /dev/null +++ b/src/dex/inception/types.ts @@ -0,0 +1,12 @@ +export type PoolState = { + ratio: bigint; +}; + +export type InceptionData = { + ratio: bigint; +}; + +export type DexParams = { + vault: string; + baseTokenSlug: string; +}; diff --git a/src/dex/inception/utils.ts b/src/dex/inception/utils.ts new file mode 100644 index 000000000..684b12213 --- /dev/null +++ b/src/dex/inception/utils.ts @@ -0,0 +1,29 @@ +import { Contract } from 'web3-eth-contract'; +import { PoolState } from './types'; +import { AbiCoder, Interface } from '@ethersproject/abi'; + +const coder = new AbiCoder(); + +export async function getOnChainRatio( + multiContract: Contract, + poolAddress: string, + poolInterface: Interface, + blockNumber: number | 'latest', +): Promise { + const data: { returnData: any[] } = await multiContract.methods + .aggregate([ + { + target: poolAddress, + callData: poolInterface.encodeFunctionData('ratio', []), + }, + ]) + .call({}, blockNumber); + + const decodedData = coder.decode(['uint256'], data.returnData[0]); + + const ratio = BigInt(decodedData[0].toString()); + + return { + ratio, + }; +} diff --git a/src/dex/index.ts b/src/dex/index.ts index bf96463fa..96b8beae2 100644 --- a/src/dex/index.ts +++ b/src/dex/index.ts @@ -83,6 +83,7 @@ import { PharaohV1 } from './solidly/forks-override/pharaohV1'; import { EtherFi } from './etherfi'; import { Spark } from './spark/spark'; import { VelodromeSlipstream } from './uniswap-v3/forks/velodrome-slipstream/velodrome-slipstream'; +import { Inception } from './inception/inception'; const LegacyDexes = [ CurveV2, @@ -162,6 +163,7 @@ const Dexes = [ Swell, PharaohV1, Spark, + Inception, ]; export type LegacyDexConstructor = new (dexHelper: IDexHelper) => IDexTxBuilder< diff --git a/tests/constants-e2e.ts b/tests/constants-e2e.ts index 66c7e7508..13a9eb3a5 100644 --- a/tests/constants-e2e.ts +++ b/tests/constants-e2e.ts @@ -411,6 +411,54 @@ export const Tokens: { address: '0xcd5fe23c85820f7b72d0926fc9b05b43e359b7ee', decimals: 18, }, + instETH: { + address: '0x7FA768E035F956c41d6aeaa3Bd857e7E5141CAd5', + decimals: 18, + }, + inrETH: { + address: '0x80d69e79258FE9D056c822461c4eb0B4ca8802E2', + decimals: 18, + }, + inoETH: { + address: '0x9181f633e9b9f15a32d5e37094f4c93b333e0e92', + decimals: 18, + }, + inosETH: { + address: '0xfD07fD5EBEa6F24888a397997E262179Bf494336', + decimals: 18, + }, + inankrETH: { + address: '0xfa2629B9cF3998D52726994E0FcdB750224D8B9D', + decimals: 18, + }, + incbETH: { + address: '0xbf19eead55a6b100667f04f8fbc5371e03e8ab2e', + decimals: 18, + }, + inwbETH: { + address: '0xda9b11cd701e10c2ec1a284f80820edd128c5246', + decimals: 18, + }, + inswETH: { + address: '0xc3ade5ace1bbb033ccae8177c12ecbfa16bd6a9d', + decimals: 18, + }, + inETHx: { + address: '0x57a5a0567187ff4a8dcc1a9bba86155e355878f2', + decimals: 18, + }, + insfrxETH: { + address: '0x668308d77be3533c909a692302cb4d135bf8041c', + decimals: 18, + }, + inmETH: { + address: '0xeCf3672A6d2147E2A77f07069Fb48d8Cf6F6Fbf9', + decimals: 18, + }, + inlsETH: { + address: '0x94b888e11a9e960a9c3b3528eb6ac807b27ca62e', + decimals: 18, + }, }, [Network.POLYGON]: { jGBP: { From 35bc6348affd0f6e3a747c43fbf7adfb39455d88 Mon Sep 17 00:00:00 2001 From: neanvo Date: Mon, 24 Jun 2024 02:38:54 +0300 Subject: [PATCH 02/22] wip: integration for inETH --- src/abi/inception/inception-ineth-pool.json | 1062 +++++++++++++++++++ src/abi/inception/inception-ineth.json | 609 +++++++++++ src/dex/inception/config.ts | 21 +- src/dex/inception/inception-e2e.test.ts | 14 +- src/dex/inception/inception-native.ts | 188 ++++ src/dex/inception/inception-pool.ts | 11 +- src/dex/inception/inception.ts | 12 +- src/dex/inception/types.ts | 1 + src/dex/inception/utils.ts | 4 +- src/dex/index.ts | 2 + tests/constants-e2e.ts | 6 +- 11 files changed, 1898 insertions(+), 32 deletions(-) create mode 100644 src/abi/inception/inception-ineth-pool.json create mode 100644 src/abi/inception/inception-ineth.json create mode 100644 src/dex/inception/inception-native.ts diff --git a/src/abi/inception/inception-ineth-pool.json b/src/abi/inception/inception-ineth-pool.json new file mode 100644 index 000000000..fb106f833 --- /dev/null +++ b/src/abi/inception/inception-ineth-pool.json @@ -0,0 +1,1062 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "claimed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "AmbiguousFee", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyGovernanceAllowed", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyOperatorAllowed", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyRestakingPoolAllowed", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "uint64", + "name": "max", + "type": "uint64" + } + ], + "name": "PoolDistributeGasLimitNotInRange", + "type": "error" + }, + { + "inputs": [], + "name": "PoolFailedInnerCall", + "type": "error" + }, + { + "inputs": [], + "name": "PoolInsufficientBalance", + "type": "error" + }, + { + "inputs": [], + "name": "PoolRestakerExists", + "type": "error" + }, + { + "inputs": [], + "name": "PoolRestakerNotExists", + "type": "error" + }, + { + "inputs": [], + "name": "PoolStakeAmGreaterThanAvailable", + "type": "error" + }, + { + "inputs": [], + "name": "PoolStakeAmLessThanMin", + "type": "error" + }, + { + "inputs": [], + "name": "PoolUnstakeAmLessThanMin", + "type": "error" + }, + { + "inputs": [], + "name": "PoolWrongInputLength", + "type": "error" + }, + { + "inputs": [], + "name": "PoolZeroAddress", + "type": "error" + }, + { + "inputs": [], + "name": "PoolZeroAmount", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "claimer", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "ClaimExpected", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "string", + "name": "provider", + "type": "string" + }, + { + "indexed": false, + "internalType": "bytes[]", + "name": "pubkeys", + "type": "bytes[]" + } + ], + "name": "Deposited", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint32", + "name": "prevValue", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "newValue", + "type": "uint32" + } + ], + "name": "DistributeGasLimitChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "restaker", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "treasury", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "fee", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "totalClaimed", + "type": "uint256" + } + ], + "name": "FeeClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "prevValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newValue", + "type": "uint256" + } + ], + "name": "MaxTVLChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "prevValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newValue", + "type": "uint256" + } + ], + "name": "MinStakeChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "prevValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newValue", + "type": "uint256" + } + ], + "name": "MinUntakeChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "ownerAddress", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "receiverAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "PendingUnstake", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "Received", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "code", + "type": "bytes32" + } + ], + "name": "ReferralStake", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "string", + "name": "provider", + "type": "string" + }, + { + "indexed": false, + "internalType": "address", + "name": "restaker", + "type": "address" + } + ], + "name": "RestakerAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "staker", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "Staked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "claimer", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "UnstakeClaimed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "Unstaked", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "components": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct IRestakingPool.Unstake[]", + "name": "unstakes", + "type": "tuple[]" + } + ], + "name": "UnstakesDistributed", + "type": "event" + }, + { + "inputs": [], + "name": "CALL_GAS_LIMIT", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "provider", + "type": "string" + } + ], + "name": "activateRestaking", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "provider", + "type": "string" + } + ], + "name": "addRestaker", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "availableToStake", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "provider", + "type": "string" + }, + { + "internalType": "bytes[]", + "name": "pubkeys", + "type": "bytes[]" + }, + { + "internalType": "bytes[]", + "name": "signatures", + "type": "bytes[]" + }, + { + "internalType": "bytes32[]", + "name": "deposit_data_roots", + "type": "bytes32[]" + } + ], + "name": "batchDeposit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "provider", + "type": "string" + }, + { + "internalType": "uint256", + "name": "fee", + "type": "uint256" + } + ], + "name": "claimRestaker", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "claimer", + "type": "address" + } + ], + "name": "claimUnstake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "claimer", + "type": "address" + } + ], + "name": "claimableOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "config", + "outputs": [ + { + "internalType": "contract IProtocolConfig", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "provider", + "type": "string" + }, + { + "internalType": "address", + "name": "elOperator", + "type": "address" + }, + { + "components": [ + { + "internalType": "bytes", + "name": "signature", + "type": "bytes" + }, + { + "internalType": "uint256", + "name": "expiry", + "type": "uint256" + } + ], + "internalType": "struct ISignatureUtils.SignatureWithExpiry", + "name": "approverSignatureAndExpiry", + "type": "tuple" + }, + { + "internalType": "bytes32", + "name": "approverSalt", + "type": "bytes32" + } + ], + "name": "delegateTo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "distributeUnstakes", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getMinStake", + "outputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getMinUnstake", + "outputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getPending", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "provider", + "type": "string" + } + ], + "name": "getRestaker", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTotalClaimable", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getTotalPendingUnstakes", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "getTotalUnstakesOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getUnstakes", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct IRestakingPool.Unstake[]", + "name": "unstakes", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + } + ], + "name": "getUnstakesOf", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "internalType": "struct IRestakingPool.Unstake[]", + "name": "unstakes", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "claimer", + "type": "address" + } + ], + "name": "hasClaimable", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IProtocolConfig", + "name": "config", + "type": "address" + }, + { + "internalType": "uint32", + "name": "distributeGasLimit", + "type": "uint32" + }, + { + "internalType": "uint256", + "name": "maxTVL", + "type": "uint256" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "provider", + "type": "string" + }, + { + "internalType": "contract IERC20[]", + "name": "tokenList", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "amountsToWithdraw", + "type": "uint256[]" + } + ], + "name": "recoverTokens", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint32", + "name": "newValue", + "type": "uint32" + } + ], + "name": "setDistributeGasLimit", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newValue", + "type": "uint256" + } + ], + "name": "setMaxTVL", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newValue", + "type": "uint256" + } + ], + "name": "setMinStake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newValue", + "type": "uint256" + } + ], + "name": "setMinUnstake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "stake", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "code", + "type": "bytes32" + } + ], + "name": "stake", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "provider", + "type": "string" + } + ], + "name": "undelegate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "unstake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "provider", + "type": "string" + }, + { + "internalType": "uint64", + "name": "oracleTimestamp", + "type": "uint64" + }, + { + "components": [ + { + "internalType": "bytes32", + "name": "beaconStateRoot", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "proof", + "type": "bytes" + } + ], + "internalType": "struct BeaconChainProofs.StateRootProof", + "name": "stateRootProof", + "type": "tuple" + }, + { + "internalType": "uint40[]", + "name": "validatorIndices", + "type": "uint40[]" + }, + { + "internalType": "bytes[]", + "name": "validatorFieldsProofs", + "type": "bytes[]" + }, + { + "internalType": "bytes32[][]", + "name": "validatorFields", + "type": "bytes32[][]" + } + ], + "name": "verifyWithdrawalCredentials", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "provider", + "type": "string" + } + ], + "name": "withdrawBeforeRestaking", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "provider", + "type": "string" + }, + { + "internalType": "uint256", + "name": "amountToWithdraw", + "type": "uint256" + } + ], + "name": "withdrawNonBeaconChainETHBalanceWei", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "stateMutability": "payable", + "type": "receive" + } +] diff --git a/src/abi/inception/inception-ineth.json b/src/abi/inception/inception-ineth.json new file mode 100644 index 000000000..8d7b78940 --- /dev/null +++ b/src/abi/inception/inception-ineth.json @@ -0,0 +1,609 @@ +[ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "allowance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientAllowance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientBalance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "approver", + "type": "address" + } + ], + "name": "ERC20InvalidApprover", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "ERC20InvalidReceiver", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "ERC20InvalidSender", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "ERC20InvalidSpender", + "type": "error" + }, + { + "inputs": [], + "name": "EnforcedPause", + "type": "error" + }, + { + "inputs": [], + "name": "ExpectedPause", + "type": "error" + }, + { + "inputs": [], + "name": "InvalidInitialization", + "type": "error" + }, + { + "inputs": [], + "name": "MathOverflowedMulDiv", + "type": "error" + }, + { + "inputs": [], + "name": "NotInitializing", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyGovernanceAllowed", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyOperatorAllowed", + "type": "error" + }, + { + "inputs": [], + "name": "OnlyRestakingPoolAllowed", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "newName", + "type": "string" + } + ], + "name": "NameChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "string", + "name": "newSymbol", + "type": "string" + } + ], + "name": "SymbolChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "burn", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "newName", + "type": "string" + } + ], + "name": "changeName", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "newSymbol", + "type": "string" + } + ], + "name": "changeSymbol", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "config", + "outputs": [ + { + "internalType": "contract IProtocolConfig", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "convertToAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "convertToShares", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "contract IProtocolConfig", + "name": "config", + "type": "address" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "symbol", + "type": "string" + } + ], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "shares", + "type": "uint256" + } + ], + "name": "mint", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ratio", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalAssets", + "outputs": [ + { + "internalType": "uint256", + "name": "totalManagedEth", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/src/dex/inception/config.ts b/src/dex/inception/config.ts index 3213ef092..3584f76ee 100644 --- a/src/dex/inception/config.ts +++ b/src/dex/inception/config.ts @@ -1,6 +1,6 @@ import { DexParams } from './types'; import { AdapterMappings, DexConfigMap } from '../../types'; -import { Network, SwapSide } from '../../constants'; +import { Network } from '../../constants'; export const InceptionConfig: DexConfigMap = { instETH: { @@ -9,15 +9,16 @@ export const InceptionConfig: DexConfigMap = { baseTokenSlug: 'STETH', }, }, - // inETH: { - // [Network.MAINNET]: { - // vault: "0xf073bAC22DAb7FaF4a3Dd6c6189a70D54110525C", - // baseTokenSlug: "ETH", - // }, - // }, }; -export const Adapters: Record = { - //TODO fixme - [Network.MAINNET]: { [SwapSide.BUY]: [{ name: 'Adapter01', index: 1 }] }, +export const InceptionNativeConfig: DexConfigMap = { + inETH: { + [Network.MAINNET]: { + token: '0xf073bAC22DAb7FaF4a3Dd6c6189a70D54110525C', + vault: '0x46199cAa0e453971cedf97f926368d9E5415831a', + baseTokenSlug: 'ETH', + }, + }, }; + +export const Adapters: Record = {}; diff --git a/src/dex/inception/inception-e2e.test.ts b/src/dex/inception/inception-e2e.test.ts index 9c7c0baf0..45301b5f6 100644 --- a/src/dex/inception/inception-e2e.test.ts +++ b/src/dex/inception/inception-e2e.test.ts @@ -7,7 +7,7 @@ import { Holders, Tokens } from '../../../tests/constants-e2e'; import { ContractMethod, Network, SwapSide } from '../../constants'; import { StaticJsonRpcProvider } from '@ethersproject/providers'; import { generateConfig } from '../../config'; -import { InceptionConfig } from './config'; +import { InceptionConfig, InceptionNativeConfig } from './config'; import { DexParams } from './types'; function testForNetwork( @@ -26,8 +26,7 @@ function testForNetwork( const holders = Holders[network]; const sideToContractMethods = new Map([ - // TBD: SwapSide,Sell - [SwapSide.BUY, ['deposit' as ContractMethod]], + [SwapSide.SELL, ['deposit' as ContractMethod]], ]); describe(`${inceptionSlug} on ${network}`, () => { @@ -39,8 +38,7 @@ function testForNetwork( await testE2E( tokens[baseSlug], tokens[inceptionSlug], - // holders[baseSlug], - '0x43594da5d6A03b2137a04DF5685805C676dEf7cB', // holders[baseSlug] holder unfortunately has withdrawed the number of tokens I needed + holders[baseSlug], '1000000000', side, inceptionSlug, @@ -67,5 +65,11 @@ describe('Inception E2E', () => { testForNetwork(network, dexKey, inceptionSymbol, chainConfig[network]); }, ); + + Object.entries(InceptionNativeConfig).forEach( + ([inceptionSymbol, chainConfig]) => { + testForNetwork(network, dexKey, inceptionSymbol, chainConfig[network]); + }, + ); }); }); diff --git a/src/dex/inception/inception-native.ts b/src/dex/inception/inception-native.ts new file mode 100644 index 000000000..27fcffe17 --- /dev/null +++ b/src/dex/inception/inception-native.ts @@ -0,0 +1,188 @@ +import { Interface, JsonFragment } from '@ethersproject/abi'; +import { AsyncOrSync } from 'ts-essentials'; +import { NumberAsString, SwapSide } from '@paraswap/core'; +import { + AdapterExchangeParam, + Address, + DexExchangeParam, + ExchangePrices, + Logger, + PoolLiquidity, + PoolPrices, + Token, +} from '../../types'; +import INETH_ABI from '../../abi/inception/inception-ineth.json'; +import INETH_VAULT_ABI from '../../abi/inception/inception-ineth-pool.json'; +import { Network, NULL_ADDRESS } from '../../constants'; +import * as CALLDATA_GAS_COST from '../../calldata-gas-cost'; +import { getDexKeysWithNetwork } from '../../utils'; +import { IDex } from '../../dex/idex'; +import { IDexHelper } from '../../dex-helper/idex-helper'; +import { InceptionData } from './types'; +import { SimpleExchange } from '../simple-exchange'; +import { Adapters, InceptionNativeConfig } from './config'; +import { InceptionPool } from './inception-pool'; +import { ethers } from 'ethers'; +import { BI_POWS } from '../../bigint-constants'; + +export const depositETHFunction = 'stake()'; + +export class InceptionNative + extends SimpleExchange + implements IDex +{ + protected inceptionPool: InceptionPool; + protected vaultInterface: Interface; + protected tokenInterface: Interface; + readonly hasConstantPriceLargeAmounts = false; + readonly needWrapNative = false; + readonly isFeeOnTransferSupported = false; + + public static dexKeysWithNetwork: { key: string; networks: Network[] }[] = + getDexKeysWithNetwork(InceptionNativeConfig); + + logger: Logger; + + constructor( + readonly network: Network, + readonly dexKey: string, + readonly dexHelper: IDexHelper, + protected config = InceptionNativeConfig[dexKey][network], + protected adapters = Adapters[network] || {}, + ) { + super(dexHelper, dexKey); + this.logger = dexHelper.getLogger(dexKey); + this.vaultInterface = new Interface(INETH_VAULT_ABI as JsonFragment[]); + this.tokenInterface = new Interface(INETH_ABI as JsonFragment[]); + this.inceptionPool = new InceptionPool( + this.dexKey, + network, + dexHelper, + this.logger, + this.config.token!, + this.tokenInterface, + ); + } + + async initializePricing(blockNumber: number) { + const data: { returnData: any[] } = + await this.dexHelper.multiContract.methods + .aggregate([ + { + target: this.config.token, + callData: this.tokenInterface.encodeFunctionData('ratio', []), + }, + ]) + .call({}, blockNumber); + + const decodedData = data.returnData.map(d => + ethers.utils.defaultAbiCoder.decode(['uint256'], d), + ); + const [ratio] = decodedData.map(d => BigInt(d[0].toString())); + + await Promise.all([ + this.inceptionPool.initialize(blockNumber, { + state: { ratio }, + }), + ]); + } + + getAdapters(side: SwapSide): { name: string; index: number }[] | null { + return this.adapters[side] ? this.adapters[side] : null; + } + + async getPoolIdentifiers( + srcToken: Token, + destToken: Token, + side: SwapSide, + blockNumber: number, + ): Promise { + return [`ETH_${destToken.address}`.toLowerCase()]; + } + + async getPricesVolume( + srcToken: Token, + destToken: Token, + amounts: bigint[], + side: SwapSide, + blockNumber: number, + limitPools?: string[], + ): Promise> { + const pool = this.inceptionPool; + + if (!pool.getState(blockNumber)) return null; + + const unitIn = BI_POWS[18]; + const unitOut = pool.getPrice(blockNumber, unitIn); + const amountsOut = amounts.map(amountIn => + pool.getPrice(blockNumber, amountIn), + ); + + return [ + { + prices: amountsOut, + unit: unitOut, + data: { + ratio: unitOut, + }, + exchange: this.dexKey, + poolIdentifier: `ETH_${destToken.address}`.toLowerCase(), + gasCost: 120_000, + poolAddresses: [this.config.vault], + }, + ]; + } + + getCalldataGasCost(poolPrices: PoolPrices): number | number[] { + return CALLDATA_GAS_COST.DEX_OVERHEAD + CALLDATA_GAS_COST.LENGTH_SMALL; + } + + getAdapterParam( + srcToken: string, + destToken: string, + srcAmount: string, + destAmount: string, + data: InceptionData, + side: SwapSide, + ): AdapterExchangeParam { + return { + targetExchange: NULL_ADDRESS, + payload: '0x', + networkFee: '0', + }; + } + + getDexParam( + srcToken: Address, + destToken: Address, + srcAmount: NumberAsString, + destAmount: NumberAsString, + recipient: Address, + data: InceptionData, + side: SwapSide, + ): DexExchangeParam { + const swapData = this.vaultInterface.encodeFunctionData( + depositETHFunction, + [], + ); + + return { + needWrapNative: this.needWrapNative, + dexFuncHasRecipient: false, + exchangeData: swapData, + targetExchange: this.config.vault, + swappedAmountNotPresentInExchangeData: true, + returnAmountPos: undefined, + }; + } + async updatePoolState(): Promise {} + + async getTopPoolsForToken( + tokenAddress: Address, + limit: number, + ): Promise { + return []; + } + + releaseResources(): AsyncOrSync {} +} diff --git a/src/dex/inception/inception-pool.ts b/src/dex/inception/inception-pool.ts index c2b67f315..7ed523cf4 100644 --- a/src/dex/inception/inception-pool.ts +++ b/src/dex/inception/inception-pool.ts @@ -1,5 +1,3 @@ -import VaultABI from '../../abi/inception/inception-vault.json'; - import { Interface } from '@ethersproject/abi'; import { DeepReadonly } from 'ts-essentials'; import { Log, Logger } from '../../types'; @@ -12,20 +10,17 @@ import { BI_POWS } from '../../bigint-constants'; export class InceptionPool extends StatefulEventSubscriber { logDecoder: (log: Log) => any; - addressesSubscribed: string[]; - constructor( readonly parentName: string, protected network: number, protected dexHelper: IDexHelper, logger: Logger, - protected config: DexParams, - protected inceptionIface = new Interface(VaultABI), + protected vault: string, + protected inceptionIface: Interface, ) { super(parentName, 'inception_pool', dexHelper, logger); this.logDecoder = (log: Log) => this.inceptionIface.parseLog(log); - this.addressesSubscribed = [this.config.vault]; } protected processLog( @@ -38,7 +33,7 @@ export class InceptionPool extends StatefulEventSubscriber { async generateState(blockNumber: number): Promise> { const state = await getOnChainRatio( this.dexHelper.multiContract, - this.config.vault, + this.vault, this.inceptionIface, blockNumber, ); diff --git a/src/dex/inception/inception.ts b/src/dex/inception/inception.ts index 660c806f2..2ac572250 100644 --- a/src/dex/inception/inception.ts +++ b/src/dex/inception/inception.ts @@ -30,7 +30,7 @@ export class Inception extends SimpleExchange implements IDex { protected inceptionPool: InceptionPool; protected vaultInterface: Interface; readonly hasConstantPriceLargeAmounts = false; - readonly needWrapNative = true; + readonly needWrapNative = false; readonly isFeeOnTransferSupported = false; public static dexKeysWithNetwork: { key: string; networks: Network[] }[] = @@ -53,7 +53,7 @@ export class Inception extends SimpleExchange implements IDex { network, dexHelper, this.logger, - this.config, + this.config.vault, this.vaultInterface, ); } @@ -123,7 +123,7 @@ export class Inception extends SimpleExchange implements IDex { poolIdentifier: `${srcToken.address}_${destToken.address}`.toLowerCase(), gasCost: 120_000, - poolAddresses: [destToken.address], + poolAddresses: [this.config.vault], }, ]; } @@ -158,14 +158,14 @@ export class Inception extends SimpleExchange implements IDex { ): DexExchangeParam { const swapData = this.vaultInterface.encodeFunctionData( depositETHFunction, - [destAmount, recipient], + [srcAmount, recipient], ); return { needWrapNative: this.needWrapNative, - dexFuncHasRecipient: false, + dexFuncHasRecipient: true, exchangeData: swapData, - targetExchange: destToken, + targetExchange: this.config.vault, swappedAmountNotPresentInExchangeData: true, returnAmountPos: undefined, }; diff --git a/src/dex/inception/types.ts b/src/dex/inception/types.ts index 2a9d4827b..501855173 100644 --- a/src/dex/inception/types.ts +++ b/src/dex/inception/types.ts @@ -8,5 +8,6 @@ export type InceptionData = { export type DexParams = { vault: string; + token?: string; baseTokenSlug: string; }; diff --git a/src/dex/inception/utils.ts b/src/dex/inception/utils.ts index 684b12213..7ef0a5408 100644 --- a/src/dex/inception/utils.ts +++ b/src/dex/inception/utils.ts @@ -6,14 +6,14 @@ const coder = new AbiCoder(); export async function getOnChainRatio( multiContract: Contract, - poolAddress: string, + vaultAddress: string, poolInterface: Interface, blockNumber: number | 'latest', ): Promise { const data: { returnData: any[] } = await multiContract.methods .aggregate([ { - target: poolAddress, + target: vaultAddress, callData: poolInterface.encodeFunctionData('ratio', []), }, ]) diff --git a/src/dex/index.ts b/src/dex/index.ts index 96b8beae2..59dd806f6 100644 --- a/src/dex/index.ts +++ b/src/dex/index.ts @@ -84,6 +84,7 @@ import { EtherFi } from './etherfi'; import { Spark } from './spark/spark'; import { VelodromeSlipstream } from './uniswap-v3/forks/velodrome-slipstream/velodrome-slipstream'; import { Inception } from './inception/inception'; +import { InceptionNative } from './inception/inception-native'; const LegacyDexes = [ CurveV2, @@ -164,6 +165,7 @@ const Dexes = [ PharaohV1, Spark, Inception, + InceptionNative, ]; export type LegacyDexConstructor = new (dexHelper: IDexHelper) => IDexTxBuilder< diff --git a/tests/constants-e2e.ts b/tests/constants-e2e.ts index 13a9eb3a5..aaa507415 100644 --- a/tests/constants-e2e.ts +++ b/tests/constants-e2e.ts @@ -459,6 +459,10 @@ export const Tokens: { address: '0x94b888e11a9e960a9c3b3528eb6ac807b27ca62e', decimals: 18, }, + inETH: { + address: '0xf073bAC22DAb7FaF4a3Dd6c6189a70D54110525C', + decimals: 18, + }, }, [Network.POLYGON]: { jGBP: { @@ -1351,7 +1355,7 @@ export const Holders: { aEthUSDC: '0x42EFD1E0DB4ADa762cc5092ECBD052dE7c6e72E2', MAV: '0x92582aa69BB6117903a01eDdfe6EFfDDe564A69f', BADGER: '0x34e2741a3f8483dbe5231f61c005110ff4b9f50a', - STETH: '0x50b42514389F25E1f471C8F03f6f5954df0204b0', + STETH: '0x43594da5d6A03b2137a04DF5685805C676dEf7cB', SUSHI: '0x8a108e4761386c94b8d2f98A5fFe13E472cFE76a', wstETH: '0x5fEC2f34D80ED82370F733043B6A536d7e9D7f8d', WETH: '0x2fEb1512183545f48f6b9C5b4EbfCaF49CfCa6F3', From 95ed1603bd745656eeb151289ea363e061c9c6f7 Mon Sep 17 00:00:00 2001 From: neanvo Date: Tue, 25 Jun 2024 16:07:51 +0300 Subject: [PATCH 03/22] feat: inception all vaults integration --- src/dex/inception/config.ts | 77 +++++++++++++++++++++++++ src/dex/inception/inception-e2e.test.ts | 5 +- tests/constants-e2e.ts | 60 ++++++++++++++++--- 3 files changed, 131 insertions(+), 11 deletions(-) diff --git a/src/dex/inception/config.ts b/src/dex/inception/config.ts index 3584f76ee..b7b354b3c 100644 --- a/src/dex/inception/config.ts +++ b/src/dex/inception/config.ts @@ -9,6 +9,83 @@ export const InceptionConfig: DexConfigMap = { baseTokenSlug: 'STETH', }, }, + inrETH: { + [Network.MAINNET]: { + token: '0x80d69e79258FE9D056c822461c4eb0B4ca8802E2', + vault: '0x1Aa53BC4Beb82aDf7f5EDEE9e3bBF3434aD59F12', + baseTokenSlug: 'rETH', + }, + }, + inoETH: { + [Network.MAINNET]: { + token: '0x9181f633e9b9f15a32d5e37094f4c93b333e0e92', + vault: '0x4878F636A9Aa314B776Ac51A25021C44CAF86bEd', + baseTokenSlug: 'oETH', + }, + }, + inosETH: { + [Network.MAINNET]: { + token: '0xfD07fD5EBEa6F24888a397997E262179Bf494336', + vault: '0xA9F8c770661BeE8DF2D026edB1Cb6FF763C780FF', + baseTokenSlug: 'osETH', + }, + }, + inankrETH: { + [Network.MAINNET]: { + token: '0xfa2629B9cF3998D52726994E0FcdB750224D8B9D', + vault: '0x36B429439AB227fAB170A4dFb3321741c8815e55', + baseTokenSlug: 'AnkETH', + }, + }, + incbETH: { + [Network.MAINNET]: { + token: '0xbf19eead55a6b100667f04f8fbc5371e03e8ab2e', + vault: '0xfE715358368416E01d3A961D3a037b7359735d5e', + baseTokenSlug: 'cbETH', + }, + }, + inwbETH: { + [Network.MAINNET]: { + token: '0xda9b11cd701e10c2ec1a284f80820edd128c5246', + vault: '0xC0660932C5dCaD4A1409b7975d147203B1e9A2B6', + baseTokenSlug: 'wbETH', + }, + }, + inswETH: { + [Network.MAINNET]: { + token: '0xc3ade5ace1bbb033ccae8177c12ecbfa16bd6a9d', + vault: '0xc4181dC7BB31453C4A48689ce0CBe975e495321c', + baseTokenSlug: 'SWETH', + }, + }, + inETHx: { + [Network.MAINNET]: { + token: '0x57a5a0567187ff4a8dcc1a9bba86155e355878f2', + vault: '0x90E80E25ABDB6205B08DeBa29a87f7eb039023C2', + baseTokenSlug: 'ETHx', + }, + }, + insfrxETH: { + [Network.MAINNET]: { + token: '0x668308d77be3533c909a692302cb4d135bf8041c', + vault: '0x295234B7E370a5Db2D2447aCA83bc7448f151161', + baseTokenSlug: 'sfrxETH', + }, + }, + inmETH: { + [Network.MAINNET]: { + token: '0xeCf3672A6d2147E2A77f07069Fb48d8Cf6F6Fbf9', + vault: '0xd0ee89d82183D7Ddaef14C6b4fC0AA742F426355', + baseTokenSlug: 'mETH', + }, + }, + inlsETH: { + [Network.MAINNET]: { + token: '0x94b888e11a9e960a9c3b3528eb6ac807b27ca62e', + vault: '0x6E17a8b5D33e6DBdB9fC61d758BF554b6AD93322', + baseTokenSlug: 'lsETH', + }, + }, }; export const InceptionNativeConfig: DexConfigMap = { diff --git a/src/dex/inception/inception-e2e.test.ts b/src/dex/inception/inception-e2e.test.ts index 45301b5f6..97f3bd4a2 100644 --- a/src/dex/inception/inception-e2e.test.ts +++ b/src/dex/inception/inception-e2e.test.ts @@ -12,7 +12,6 @@ import { DexParams } from './types'; function testForNetwork( network: Network, - dexKey: string, inceptionSlug: string, config: DexParams, ) { @@ -55,14 +54,12 @@ function testForNetwork( } describe('Inception E2E', () => { - const dexKey = 'Inception'; - describe('Mainnet', () => { const network = Network.MAINNET; Object.entries(InceptionConfig).forEach( ([inceptionSymbol, chainConfig]) => { - testForNetwork(network, dexKey, inceptionSymbol, chainConfig[network]); + testForNetwork(network, inceptionSymbol, chainConfig[network]); }, ); diff --git a/tests/constants-e2e.ts b/tests/constants-e2e.ts index aaa507415..d168f988d 100644 --- a/tests/constants-e2e.ts +++ b/tests/constants-e2e.ts @@ -1,12 +1,12 @@ import { - SmartTokenParams, - balanceOfFn, + _allowancesFn, + _balancesFn, allowanceFn, - SmartToken, - balancesFn, allowedFn, - _balancesFn, - _allowancesFn, + balanceOfFn, + balancesFn, + SmartToken, + SmartTokenParams, } from '../tests/smart-tokens'; import { Address } from '../src/types'; import { ETHER_ADDRESS, Network } from '../src/constants'; @@ -463,6 +463,42 @@ export const Tokens: { address: '0xf073bAC22DAb7FaF4a3Dd6c6189a70D54110525C', decimals: 18, }, + rETH: { + address: '0xae78736Cd615f374D3085123A210448E74Fc6393', + decimals: 18, + }, + oETH: { + address: '0x856c4Efb76C1D1AE02e20CEB03A2A6a08b0b8dC3', + decimals: 18, + }, + osETH: { + address: '0xf1C9acDc66974dFB6dEcB12aA385b9cD01190E38', + decimals: 18, + }, + cbETH: { + address: '0xBe9895146f7AF43049ca1c1AE358B0541Ea49704', + decimals: 18, + }, + wbETH: { + address: '0xa2E3356610840701BDf5611a53974510Ae27E2e1', + decimals: 18, + }, + ETHx: { + address: '0xA35b1B31Ce002FBF2058D22F30f95D405200A15b', + decimals: 18, + }, + sfrxETH: { + address: '0xac3E018457B222d93114458476f3E3416Abbe38F', + decimals: 18, + }, + mETH: { + address: '0xd5F7838F5C461fefF7FE49ea5ebaF7728bB0ADfa', + decimals: 18, + }, + lsETH: { + address: '0x8c1BEd5b9a0928467c9B1341Da1D7BD5e10b6549', + decimals: 18, + }, }, [Network.POLYGON]: { jGBP: { @@ -1365,7 +1401,7 @@ export const Holders: { sDAI: '0x4C612E3B15b96Ff9A6faED838F8d07d479a8dD4c', CVX: '0x0aCA67Fa70B142A3b9bF2eD89A81B40ff85dACdC', MIM: '0xa046a8660e66d178ee07ec97c585eeb6aa18c26c', - AnkETH: '0xF7260D4ADc48fEefd5a19a9Eb23f9747CeE15C92', + AnkETH: '0xBB2d41AcfEA24A3A2aD4A6F95C3AcE1cC98c6ed6', DAI: '0x2fEb1512183545f48f6b9C5b4EbfCaF49CfCa6F3', oldFRAX: '0x183d0dc5867c01bfb1dbbc41d6a9d3de6e044626', newFRAX: '0x183d0dc5867c01bfb1dbbc41d6a9d3de6e044626', @@ -1425,6 +1461,16 @@ export const Holders: { USDe: '0x74e6c48e667d698a4cf90665b6960a5bae39e603', eETH: '0x0f1DfeF1a40557d279d0de6E49aB306891A638b8', weETH: '0xfB4cB868727D9622258bf2B26A8CFc95f6742669', + rETH: '0x9985dF20D7e9103ECBCeb16a84956434B6f06ae8', + oETH: '0xDcEe70654261AF21C44c093C300eD3Bb97b78192', + osETH: '0xe080027Bd47353b5D1639772b4a75E9Ed3658A0d', + cbETH: '0xEA0EFC80A900aA11B9eb7f0392fdA301054b8742', + wbETH: '0xc6C010287683FD3Db7865016e3e1D6468Dd73aA6', + ETHx: '0x4F2083f5fBede34C2714aFfb3105539775f7FE64', + sfrxETH: '0xC2545C68a71F6803264bdE885870fD72D361fB9E', + mETH: '0xc3350595eD42EbE94556277bc77D257c76065291', + lsETH: '0xAe60d8180437b5C34bB956822ac2710972584473', + SWETH: '0xc585DF3a8C9ca0c614D023A812624bE36161502B', }, [Network.POLYGON]: { jGBP: '0x02aa0B826c7BA6386DdBE04C0a8715A1c0A16B24', From a5a517b514ea06a6f23b62408499d40a4ed3dc85 Mon Sep 17 00:00:00 2001 From: Yevhen Shulha Date: Tue, 9 Jul 2024 18:25:31 +0300 Subject: [PATCH 04/22] fix: minor --- src/dex/inception/inception-e2e.test.ts | 4 ++-- src/dex/inception/inception-pool.ts | 2 +- src/dex/inception/inception.ts | 1 - tests/constants-e2e.ts | 5 +++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/dex/inception/inception-e2e.test.ts b/src/dex/inception/inception-e2e.test.ts index 97f3bd4a2..740613d4c 100644 --- a/src/dex/inception/inception-e2e.test.ts +++ b/src/dex/inception/inception-e2e.test.ts @@ -38,7 +38,7 @@ function testForNetwork( tokens[baseSlug], tokens[inceptionSlug], holders[baseSlug], - '1000000000', + '100000000', side, inceptionSlug, contractMethod, @@ -65,7 +65,7 @@ describe('Inception E2E', () => { Object.entries(InceptionNativeConfig).forEach( ([inceptionSymbol, chainConfig]) => { - testForNetwork(network, dexKey, inceptionSymbol, chainConfig[network]); + testForNetwork(network, inceptionSymbol, chainConfig[network]); }, ); }); diff --git a/src/dex/inception/inception-pool.ts b/src/dex/inception/inception-pool.ts index 7ed523cf4..4400469de 100644 --- a/src/dex/inception/inception-pool.ts +++ b/src/dex/inception/inception-pool.ts @@ -3,7 +3,7 @@ import { DeepReadonly } from 'ts-essentials'; import { Log, Logger } from '../../types'; import { StatefulEventSubscriber } from '../../stateful-event-subscriber'; import { IDexHelper } from '../../dex-helper/idex-helper'; -import { DexParams, PoolState } from './types'; +import { PoolState } from './types'; import { getOnChainRatio } from './utils'; import { BI_POWS } from '../../bigint-constants'; diff --git a/src/dex/inception/inception.ts b/src/dex/inception/inception.ts index 2ac572250..705322be9 100644 --- a/src/dex/inception/inception.ts +++ b/src/dex/inception/inception.ts @@ -166,7 +166,6 @@ export class Inception extends SimpleExchange implements IDex { dexFuncHasRecipient: true, exchangeData: swapData, targetExchange: this.config.vault, - swappedAmountNotPresentInExchangeData: true, returnAmountPos: undefined, }; } diff --git a/tests/constants-e2e.ts b/tests/constants-e2e.ts index 82cee8aaa..5039a612f 100644 --- a/tests/constants-e2e.ts +++ b/tests/constants-e2e.ts @@ -1590,7 +1590,7 @@ export const Holders: { sDAI: '0x4C612E3B15b96Ff9A6faED838F8d07d479a8dD4c', CVX: '0x0aCA67Fa70B142A3b9bF2eD89A81B40ff85dACdC', MIM: '0xa046a8660e66d178ee07ec97c585eeb6aa18c26c', - AnkETH: '0xF7260D4ADc48fEefd5a19a9Eb23f9747CeE15C92', + AnkETH: '0xBB2d41AcfEA24A3A2aD4A6F95C3AcE1cC98c6ed6', DAI: '0xd1668fb5f690c59ab4b0cabad0f8c1617895052b', oldFRAX: '0x183d0dc5867c01bfb1dbbc41d6a9d3de6e044626', newFRAX: '0x183d0dc5867c01bfb1dbbc41d6a9d3de6e044626', @@ -1660,13 +1660,14 @@ export const Holders: { rETH: '0x9985dF20D7e9103ECBCeb16a84956434B6f06ae8', oETH: '0xDcEe70654261AF21C44c093C300eD3Bb97b78192', osETH: '0xe080027Bd47353b5D1639772b4a75E9Ed3658A0d', - cbETH: '0xEA0EFC80A900aA11B9eb7f0392fdA301054b8742', + cbETH: '0xd35faBF6Ec806f4D772617956A2058CdE086C6E0', wbETH: '0xc6C010287683FD3Db7865016e3e1D6468Dd73aA6', ETHx: '0x4F2083f5fBede34C2714aFfb3105539775f7FE64', sfrxETH: '0xC2545C68a71F6803264bdE885870fD72D361fB9E', mETH: '0xc3350595eD42EbE94556277bc77D257c76065291', lsETH: '0xAe60d8180437b5C34bB956822ac2710972584473', SWETH: '0xc585DF3a8C9ca0c614D023A812624bE36161502B', + inankrETH: '0x310C8f5274F74258cDe1E5101b50A0502b884aC4', }, [Network.POLYGON]: { jGBP: '0x02aa0B826c7BA6386DdBE04C0a8715A1c0A16B24', From 84da031b6133f9790dca8f473bfefc8ac09c9bd7 Mon Sep 17 00:00:00 2001 From: neanvo Date: Mon, 15 Jul 2024 10:30:36 +0300 Subject: [PATCH 05/22] feat: requested changes --- src/dex/inception/config.ts | 192 ++++++++++----------- src/dex/inception/inception-e2e.test.ts | 77 ++++----- src/dex/inception/inception-native.ts | 188 --------------------- src/dex/inception/inception-pool.ts | 50 ------ src/dex/inception/inception.ts | 216 ++++++++++++++++-------- src/dex/inception/tokens.ts | 37 ++++ src/dex/inception/types.ts | 12 +- src/dex/inception/utils.ts | 60 ++++++- src/dex/index.ts | 2 - 9 files changed, 379 insertions(+), 455 deletions(-) delete mode 100644 src/dex/inception/inception-native.ts delete mode 100644 src/dex/inception/inception-pool.ts create mode 100644 src/dex/inception/tokens.ts diff --git a/src/dex/inception/config.ts b/src/dex/inception/config.ts index b7b354b3c..83d60ee22 100644 --- a/src/dex/inception/config.ts +++ b/src/dex/inception/config.ts @@ -1,101 +1,105 @@ import { DexParams } from './types'; import { AdapterMappings, DexConfigMap } from '../../types'; -import { Network } from '../../constants'; +import { Network, SwapSide } from '../../constants'; -export const InceptionConfig: DexConfigMap = { - instETH: { - [Network.MAINNET]: { - vault: '0x814CC6B8fd2555845541FB843f37418b05977d8d', - baseTokenSlug: 'STETH', - }, - }, - inrETH: { - [Network.MAINNET]: { - token: '0x80d69e79258FE9D056c822461c4eb0B4ca8802E2', - vault: '0x1Aa53BC4Beb82aDf7f5EDEE9e3bBF3434aD59F12', - baseTokenSlug: 'rETH', - }, - }, - inoETH: { - [Network.MAINNET]: { - token: '0x9181f633e9b9f15a32d5e37094f4c93b333e0e92', - vault: '0x4878F636A9Aa314B776Ac51A25021C44CAF86bEd', - baseTokenSlug: 'oETH', - }, - }, - inosETH: { - [Network.MAINNET]: { - token: '0xfD07fD5EBEa6F24888a397997E262179Bf494336', - vault: '0xA9F8c770661BeE8DF2D026edB1Cb6FF763C780FF', - baseTokenSlug: 'osETH', - }, - }, - inankrETH: { - [Network.MAINNET]: { - token: '0xfa2629B9cF3998D52726994E0FcdB750224D8B9D', - vault: '0x36B429439AB227fAB170A4dFb3321741c8815e55', - baseTokenSlug: 'AnkETH', - }, - }, - incbETH: { - [Network.MAINNET]: { - token: '0xbf19eead55a6b100667f04f8fbc5371e03e8ab2e', - vault: '0xfE715358368416E01d3A961D3a037b7359735d5e', - baseTokenSlug: 'cbETH', - }, - }, - inwbETH: { - [Network.MAINNET]: { - token: '0xda9b11cd701e10c2ec1a284f80820edd128c5246', - vault: '0xC0660932C5dCaD4A1409b7975d147203B1e9A2B6', - baseTokenSlug: 'wbETH', - }, - }, - inswETH: { - [Network.MAINNET]: { - token: '0xc3ade5ace1bbb033ccae8177c12ecbfa16bd6a9d', - vault: '0xc4181dC7BB31453C4A48689ce0CBe975e495321c', - baseTokenSlug: 'SWETH', - }, - }, - inETHx: { - [Network.MAINNET]: { - token: '0x57a5a0567187ff4a8dcc1a9bba86155e355878f2', - vault: '0x90E80E25ABDB6205B08DeBa29a87f7eb039023C2', - baseTokenSlug: 'ETHx', - }, - }, - insfrxETH: { - [Network.MAINNET]: { - token: '0x668308d77be3533c909a692302cb4d135bf8041c', - vault: '0x295234B7E370a5Db2D2447aCA83bc7448f151161', - baseTokenSlug: 'sfrxETH', - }, - }, - inmETH: { - [Network.MAINNET]: { - token: '0xeCf3672A6d2147E2A77f07069Fb48d8Cf6F6Fbf9', - vault: '0xd0ee89d82183D7Ddaef14C6b4fC0AA742F426355', - baseTokenSlug: 'mETH', - }, - }, - inlsETH: { - [Network.MAINNET]: { - token: '0x94b888e11a9e960a9c3b3528eb6ac807b27ca62e', - vault: '0x6E17a8b5D33e6DBdB9fC61d758BF554b6AD93322', - baseTokenSlug: 'lsETH', - }, +export const InceptionConfig: DexConfigMap = { + InceptionLRT: { + [Network.MAINNET]: [ + { + symbol: 'instETH', + token: '0x7FA768E035F956c41d6aeaa3Bd857e7E5141CAd5', + vault: '0x814CC6B8fd2555845541FB843f37418b05977d8d', + baseToken: '0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84', + baseTokenSlug: 'STETH', + }, + { + symbol: 'inrETH', + token: '0x80d69e79258FE9D056c822461c4eb0B4ca8802E2', + vault: '0x1Aa53BC4Beb82aDf7f5EDEE9e3bBF3434aD59F12', + baseToken: '0xae78736Cd615f374D3085123A210448E74Fc6393', + baseTokenSlug: 'rETH', + }, + { + symbol: 'inoETH', + token: '0x9181f633e9b9f15a32d5e37094f4c93b333e0e92', + vault: '0x4878F636A9Aa314B776Ac51A25021C44CAF86bEd', + baseToken: '0x856c4Efb76C1D1AE02e20CEB03A2A6a08b0b8dC3', + baseTokenSlug: 'oETH', + }, + { + symbol: 'inosETH', + token: '0xfD07fD5EBEa6F24888a397997E262179Bf494336', + vault: '0xA9F8c770661BeE8DF2D026edB1Cb6FF763C780FF', + baseToken: '0xf1C9acDc66974dFB6dEcB12aA385b9cD01190E38', + baseTokenSlug: 'osETH', + }, + { + symbol: 'inankrETH', + token: '0xfa2629B9cF3998D52726994E0FcdB750224D8B9D', + vault: '0x36B429439AB227fAB170A4dFb3321741c8815e55', + baseToken: '0xE95A203B1a91a908F9B9CE46459d101078c2c3cb', + baseTokenSlug: 'AnkETH', + }, + { + symbol: 'incbETH', + token: '0xbf19eead55a6b100667f04f8fbc5371e03e8ab2e', + vault: '0xfE715358368416E01d3A961D3a037b7359735d5e', + baseToken: '0xBe9895146f7AF43049ca1c1AE358B0541Ea49704', + baseTokenSlug: 'cbETH', + }, + { + symbol: 'inwbETH', + token: '0xda9b11cd701e10c2ec1a284f80820edd128c5246', + vault: '0xC0660932C5dCaD4A1409b7975d147203B1e9A2B6', + baseToken: '0xa2E3356610840701BDf5611a53974510Ae27E2e1', + baseTokenSlug: 'wbETH', + }, + { + symbol: 'inswETH', + token: '0xc3ade5ace1bbb033ccae8177c12ecbfa16bd6a9d', + vault: '0xc4181dC7BB31453C4A48689ce0CBe975e495321c', + baseToken: '0xf951E335afb289353dc249e82926178EaC7DEd78', + baseTokenSlug: 'SWETH', + }, + { + symbol: 'inETHx', + token: '0x57a5a0567187ff4a8dcc1a9bba86155e355878f2', + vault: '0x90E80E25ABDB6205B08DeBa29a87f7eb039023C2', + baseToken: '0xA35b1B31Ce002FBF2058D22F30f95D405200A15b', + baseTokenSlug: 'ETHx', + }, + { + symbol: 'insfrxETH', + token: '0x668308d77be3533c909a692302cb4d135bf8041c', + vault: '0x295234B7E370a5Db2D2447aCA83bc7448f151161', + baseToken: '0xac3E018457B222d93114458476f3E3416Abbe38F', + baseTokenSlug: 'sfrxETH', + }, + { + symbol: 'inmETH', + token: '0xeCf3672A6d2147E2A77f07069Fb48d8Cf6F6Fbf9', + vault: '0xd0ee89d82183D7Ddaef14C6b4fC0AA742F426355', + baseToken: '0xd5F7838F5C461fefF7FE49ea5ebaF7728bB0ADfa', + baseTokenSlug: 'mETH', + }, + { + symbol: 'inlsETH', + token: '0x94b888e11a9e960a9c3b3528eb6ac807b27ca62e', + vault: '0x6E17a8b5D33e6DBdB9fC61d758BF554b6AD93322', + baseToken: '0x8c1BEd5b9a0928467c9B1341Da1D7BD5e10b6549', + baseTokenSlug: 'lsETH', + }, + { + symbol: 'inETH', + token: '0xf073bAC22DAb7FaF4a3Dd6c6189a70D54110525C', + vault: '0x46199cAa0e453971cedf97f926368d9E5415831a', + baseToken: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee', + baseTokenSlug: 'ETH', + }, + ], }, }; -export const InceptionNativeConfig: DexConfigMap = { - inETH: { - [Network.MAINNET]: { - token: '0xf073bAC22DAb7FaF4a3Dd6c6189a70D54110525C', - vault: '0x46199cAa0e453971cedf97f926368d9E5415831a', - baseTokenSlug: 'ETH', - }, - }, +export const Adapters: Record = { + [Network.MAINNET]: { [SwapSide.SELL]: [{ name: '', index: 0 }] }, }; - -export const Adapters: Record = {}; diff --git a/src/dex/inception/inception-e2e.test.ts b/src/dex/inception/inception-e2e.test.ts index 740613d4c..fce7fd111 100644 --- a/src/dex/inception/inception-e2e.test.ts +++ b/src/dex/inception/inception-e2e.test.ts @@ -7,66 +7,61 @@ import { Holders, Tokens } from '../../../tests/constants-e2e'; import { ContractMethod, Network, SwapSide } from '../../constants'; import { StaticJsonRpcProvider } from '@ethersproject/providers'; import { generateConfig } from '../../config'; -import { InceptionConfig, InceptionNativeConfig } from './config'; +import { InceptionConfig } from './config'; import { DexParams } from './types'; function testForNetwork( network: Network, - inceptionSlug: string, - config: DexParams, + inceptionName: string, + configs: DexParams[], ) { const provider = new StaticJsonRpcProvider( generateConfig(network).privateHttpProvider, network, ); - const baseSlug = config.baseTokenSlug; - const tokens = Tokens[network]; - const holders = Holders[network]; + for (const config of configs) { + const baseSlug = config.baseTokenSlug; + const tokens = Tokens[network]; + const holders = Holders[network]; + const inceptionSlug = config.symbol; - const sideToContractMethods = new Map([ - [SwapSide.SELL, ['deposit' as ContractMethod]], - ]); + const sideToContractMethods = new Map([ + [SwapSide.SELL, ['deposit' as ContractMethod]], + ]); - describe(`${inceptionSlug} on ${network}`, () => { - sideToContractMethods.forEach((contractMethods, side) => - describe(`${side}`, () => { - contractMethods.forEach((contractMethod: ContractMethod) => { - describe(`${contractMethod}`, () => { - it(`${baseSlug} -> ${inceptionSlug}`, async () => { - await testE2E( - tokens[baseSlug], - tokens[inceptionSlug], - holders[baseSlug], - '100000000', - side, - inceptionSlug, - contractMethod, - network, - provider, - ); + describe(`${inceptionSlug} on ${network}`, () => { + sideToContractMethods.forEach((contractMethods, side) => + describe(`${side}`, () => { + contractMethods.forEach((contractMethod: ContractMethod) => { + describe(`${contractMethod}`, () => { + it(`${baseSlug} -> ${inceptionSlug}`, async () => { + await testE2E( + tokens[baseSlug], + tokens[inceptionSlug], + holders[baseSlug], + '100000000', + side, + inceptionName, + contractMethod, + network, + provider, + ); + }); }); }); - }); - }), - ); - }); + }), + ); + }); + } } describe('Inception E2E', () => { describe('Mainnet', () => { const network = Network.MAINNET; - Object.entries(InceptionConfig).forEach( - ([inceptionSymbol, chainConfig]) => { - testForNetwork(network, inceptionSymbol, chainConfig[network]); - }, - ); - - Object.entries(InceptionNativeConfig).forEach( - ([inceptionSymbol, chainConfig]) => { - testForNetwork(network, inceptionSymbol, chainConfig[network]); - }, - ); + Object.entries(InceptionConfig).forEach(([inceptionName, chainConfig]) => { + testForNetwork(network, inceptionName, chainConfig[network]); + }); }); }); diff --git a/src/dex/inception/inception-native.ts b/src/dex/inception/inception-native.ts deleted file mode 100644 index 27fcffe17..000000000 --- a/src/dex/inception/inception-native.ts +++ /dev/null @@ -1,188 +0,0 @@ -import { Interface, JsonFragment } from '@ethersproject/abi'; -import { AsyncOrSync } from 'ts-essentials'; -import { NumberAsString, SwapSide } from '@paraswap/core'; -import { - AdapterExchangeParam, - Address, - DexExchangeParam, - ExchangePrices, - Logger, - PoolLiquidity, - PoolPrices, - Token, -} from '../../types'; -import INETH_ABI from '../../abi/inception/inception-ineth.json'; -import INETH_VAULT_ABI from '../../abi/inception/inception-ineth-pool.json'; -import { Network, NULL_ADDRESS } from '../../constants'; -import * as CALLDATA_GAS_COST from '../../calldata-gas-cost'; -import { getDexKeysWithNetwork } from '../../utils'; -import { IDex } from '../../dex/idex'; -import { IDexHelper } from '../../dex-helper/idex-helper'; -import { InceptionData } from './types'; -import { SimpleExchange } from '../simple-exchange'; -import { Adapters, InceptionNativeConfig } from './config'; -import { InceptionPool } from './inception-pool'; -import { ethers } from 'ethers'; -import { BI_POWS } from '../../bigint-constants'; - -export const depositETHFunction = 'stake()'; - -export class InceptionNative - extends SimpleExchange - implements IDex -{ - protected inceptionPool: InceptionPool; - protected vaultInterface: Interface; - protected tokenInterface: Interface; - readonly hasConstantPriceLargeAmounts = false; - readonly needWrapNative = false; - readonly isFeeOnTransferSupported = false; - - public static dexKeysWithNetwork: { key: string; networks: Network[] }[] = - getDexKeysWithNetwork(InceptionNativeConfig); - - logger: Logger; - - constructor( - readonly network: Network, - readonly dexKey: string, - readonly dexHelper: IDexHelper, - protected config = InceptionNativeConfig[dexKey][network], - protected adapters = Adapters[network] || {}, - ) { - super(dexHelper, dexKey); - this.logger = dexHelper.getLogger(dexKey); - this.vaultInterface = new Interface(INETH_VAULT_ABI as JsonFragment[]); - this.tokenInterface = new Interface(INETH_ABI as JsonFragment[]); - this.inceptionPool = new InceptionPool( - this.dexKey, - network, - dexHelper, - this.logger, - this.config.token!, - this.tokenInterface, - ); - } - - async initializePricing(blockNumber: number) { - const data: { returnData: any[] } = - await this.dexHelper.multiContract.methods - .aggregate([ - { - target: this.config.token, - callData: this.tokenInterface.encodeFunctionData('ratio', []), - }, - ]) - .call({}, blockNumber); - - const decodedData = data.returnData.map(d => - ethers.utils.defaultAbiCoder.decode(['uint256'], d), - ); - const [ratio] = decodedData.map(d => BigInt(d[0].toString())); - - await Promise.all([ - this.inceptionPool.initialize(blockNumber, { - state: { ratio }, - }), - ]); - } - - getAdapters(side: SwapSide): { name: string; index: number }[] | null { - return this.adapters[side] ? this.adapters[side] : null; - } - - async getPoolIdentifiers( - srcToken: Token, - destToken: Token, - side: SwapSide, - blockNumber: number, - ): Promise { - return [`ETH_${destToken.address}`.toLowerCase()]; - } - - async getPricesVolume( - srcToken: Token, - destToken: Token, - amounts: bigint[], - side: SwapSide, - blockNumber: number, - limitPools?: string[], - ): Promise> { - const pool = this.inceptionPool; - - if (!pool.getState(blockNumber)) return null; - - const unitIn = BI_POWS[18]; - const unitOut = pool.getPrice(blockNumber, unitIn); - const amountsOut = amounts.map(amountIn => - pool.getPrice(blockNumber, amountIn), - ); - - return [ - { - prices: amountsOut, - unit: unitOut, - data: { - ratio: unitOut, - }, - exchange: this.dexKey, - poolIdentifier: `ETH_${destToken.address}`.toLowerCase(), - gasCost: 120_000, - poolAddresses: [this.config.vault], - }, - ]; - } - - getCalldataGasCost(poolPrices: PoolPrices): number | number[] { - return CALLDATA_GAS_COST.DEX_OVERHEAD + CALLDATA_GAS_COST.LENGTH_SMALL; - } - - getAdapterParam( - srcToken: string, - destToken: string, - srcAmount: string, - destAmount: string, - data: InceptionData, - side: SwapSide, - ): AdapterExchangeParam { - return { - targetExchange: NULL_ADDRESS, - payload: '0x', - networkFee: '0', - }; - } - - getDexParam( - srcToken: Address, - destToken: Address, - srcAmount: NumberAsString, - destAmount: NumberAsString, - recipient: Address, - data: InceptionData, - side: SwapSide, - ): DexExchangeParam { - const swapData = this.vaultInterface.encodeFunctionData( - depositETHFunction, - [], - ); - - return { - needWrapNative: this.needWrapNative, - dexFuncHasRecipient: false, - exchangeData: swapData, - targetExchange: this.config.vault, - swappedAmountNotPresentInExchangeData: true, - returnAmountPos: undefined, - }; - } - async updatePoolState(): Promise {} - - async getTopPoolsForToken( - tokenAddress: Address, - limit: number, - ): Promise { - return []; - } - - releaseResources(): AsyncOrSync {} -} diff --git a/src/dex/inception/inception-pool.ts b/src/dex/inception/inception-pool.ts deleted file mode 100644 index 4400469de..000000000 --- a/src/dex/inception/inception-pool.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Interface } from '@ethersproject/abi'; -import { DeepReadonly } from 'ts-essentials'; -import { Log, Logger } from '../../types'; -import { StatefulEventSubscriber } from '../../stateful-event-subscriber'; -import { IDexHelper } from '../../dex-helper/idex-helper'; -import { PoolState } from './types'; -import { getOnChainRatio } from './utils'; -import { BI_POWS } from '../../bigint-constants'; - -export class InceptionPool extends StatefulEventSubscriber { - logDecoder: (log: Log) => any; - - constructor( - readonly parentName: string, - protected network: number, - protected dexHelper: IDexHelper, - logger: Logger, - protected vault: string, - protected inceptionIface: Interface, - ) { - super(parentName, 'inception_pool', dexHelper, logger); - - this.logDecoder = (log: Log) => this.inceptionIface.parseLog(log); - } - - protected processLog( - state: DeepReadonly, - log: Readonly, - ): DeepReadonly | null { - return state; - } - - async generateState(blockNumber: number): Promise> { - const state = await getOnChainRatio( - this.dexHelper.multiContract, - this.vault, - this.inceptionIface, - blockNumber, - ); - - return state; - } - - getPrice(blockNumber: number, ethAmount: bigint): bigint { - const state = this.getState(blockNumber); - if (!state) throw new Error('Cannot compute price'); - - return (ethAmount * state.ratio) / BI_POWS[18]; - } -} diff --git a/src/dex/inception/inception.ts b/src/dex/inception/inception.ts index 705322be9..e0bdacce6 100644 --- a/src/dex/inception/inception.ts +++ b/src/dex/inception/inception.ts @@ -12,23 +12,30 @@ import { Token, } from '../../types'; import INCEPTION_ABI from '../../abi/inception/inception-vault.json'; +import INCEPTION_POOL_ABI from '../../abi/inception/inception-ineth-pool.json'; import { Network, NULL_ADDRESS } from '../../constants'; import * as CALLDATA_GAS_COST from '../../calldata-gas-cost'; -import { getDexKeysWithNetwork } from '../../utils'; +import { getDexKeysWithNetwork, Utils } from '../../utils'; import { IDex } from '../../dex/idex'; import { IDexHelper } from '../../dex-helper/idex-helper'; -import { InceptionData } from './types'; +import { DexParams, InceptionDexData, PoolState } from './types'; import { SimpleExchange } from '../simple-exchange'; import { Adapters, InceptionConfig } from './config'; -import { InceptionPool } from './inception-pool'; -import { ethers } from 'ethers'; -import { BI_POWS } from '../../bigint-constants'; +import { getTokenFromAddress, setTokensOnNetwork, Tokens } from './tokens'; +import { fetchTokenList, getOnChainRatio, getOnChainState } from './utils'; -export const depositETHFunction = 'deposit'; +const DECIMALS = BigInt(1e18); -export class Inception extends SimpleExchange implements IDex { - protected inceptionPool: InceptionPool; +export const TOKEN_LIST_CACHE_KEY = 'inceptionlrt-token-list'; +const TOKEN_LIST_TTL_SECONDS = 24 * 60 * 60; // 1 day + +export class Inception + extends SimpleExchange + implements IDex +{ protected vaultInterface: Interface; + protected poolInterface: Interface; + readonly hasConstantPriceLargeAmounts = false; readonly needWrapNative = false; readonly isFeeOnTransferSupported = false; @@ -38,6 +45,8 @@ export class Inception extends SimpleExchange implements IDex { logger: Logger; + private state: Record = {}; + constructor( readonly network: Network, readonly dexKey: string, @@ -48,41 +57,65 @@ export class Inception extends SimpleExchange implements IDex { super(dexHelper, dexKey); this.logger = dexHelper.getLogger(dexKey); this.vaultInterface = new Interface(INCEPTION_ABI as JsonFragment[]); - this.inceptionPool = new InceptionPool( - this.dexKey, - network, - dexHelper, - this.logger, - this.config.vault, + this.poolInterface = new Interface(INCEPTION_POOL_ABI as JsonFragment[]); + } + + async initializePricing(blockNumber: number) { + const poolState = await getOnChainState( + this.dexHelper.multiContract, this.vaultInterface, + this.network, + blockNumber, ); + await this.initializeTokens(poolState, blockNumber); } - async initializePricing(blockNumber: number) { - const data: { returnData: any[] } = - await this.dexHelper.multiContract.methods - .aggregate([ - { - target: this.config.vault, - callData: this.vaultInterface.encodeFunctionData('ratio', []), - }, - ]) - .call({}, blockNumber); - - const decodedData = data.returnData.map(d => - ethers.utils.defaultAbiCoder.decode(['uint256'], d), + async initializeTokens(poolState: PoolState, blockNumber: number) { + let cachedTokenList = await this.dexHelper.cache.getAndCacheLocally( + this.dexKey, + this.network, + TOKEN_LIST_CACHE_KEY, + TOKEN_LIST_TTL_SECONDS, ); - const [ratio] = decodedData.map(d => BigInt(d[0].toString())); - await Promise.all([ - this.inceptionPool.initialize(blockNumber, { - state: { ratio }, - }), - ]); + if (cachedTokenList !== null) { + if (Object.keys(Tokens).length !== 0) return; + + const tokenListParsed = JSON.parse(cachedTokenList); + setTokensOnNetwork(this.network, tokenListParsed); + + tokenListParsed.forEach((p: DexParams) => { + this.state[p.token] = { + blockNumber, + ratio: poolState[p.symbol.toLowerCase()].ratio, + }; + }); + return; + } + + let tokenList = await fetchTokenList(this.network); + + await this.dexHelper.cache.setexAndCacheLocally( + this.dexKey, + this.network, + TOKEN_LIST_CACHE_KEY, + TOKEN_LIST_TTL_SECONDS, + JSON.stringify(tokenList), + ); + + setTokensOnNetwork(this.network, tokenList); + + // init state for all tokens as empty + tokenList.forEach(p => { + this.state[p.token] = { + blockNumber, + ratio: poolState[p.symbol.toLowerCase()].ratio, + }; + }); } getAdapters(side: SwapSide): { name: string; index: number }[] | null { - return this.adapters[side] ? this.adapters[side] : null; + return null; } async getPoolIdentifiers( @@ -91,7 +124,13 @@ export class Inception extends SimpleExchange implements IDex { side: SwapSide, blockNumber: number, ): Promise { - return [`${srcToken.address}_${destToken.address}`.toLowerCase()]; + return [ + this.dexKey + + '-' + + [srcToken.address.toLowerCase(), destToken.address.toLowerCase()] + .sort((a, b) => (a > b ? 1 : -1)) + .join('_'), + ]; } async getPricesVolume( @@ -101,45 +140,73 @@ export class Inception extends SimpleExchange implements IDex { side: SwapSide, blockNumber: number, limitPools?: string[], - ): Promise> { - const pool = this.inceptionPool; + ): Promise> { + const src = getTokenFromAddress(this.network, srcToken.address); + const dest = getTokenFromAddress(this.network, destToken.address); - if (!pool.getState(blockNumber)) return null; + if (src !== dest) { + return null; + } - const unitIn = BI_POWS[18]; - const unitOut = pool.getPrice(blockNumber, unitIn); - const amountsOut = amounts.map(amountIn => - pool.getPrice(blockNumber, amountIn), - ); + const [inceptionToken] = [src.token.toLowerCase()]; + + if ( + !this.state[inceptionToken]?.blockNumber || + blockNumber > this.state[inceptionToken].blockNumber + ) { + const cached = await this.dexHelper.cache.get( + this.dexKey, + this.network, + `state_${inceptionToken}`, + ); + if (cached) { + this.state[inceptionToken] = Utils.Parse(cached); + } else { + const ratio = await getOnChainRatio( + this.dexHelper.multiContract, + src.symbol === 'ETH' ? this.poolInterface : this.vaultInterface, + this.network, + src, + blockNumber, + ); + this.state[inceptionToken] = { + blockNumber, + ratio, + }; + this.dexHelper.cache.setex( + this.dexKey, + this.network, + `state_${inceptionToken}`, + 60, + Utils.Serialize(this.state[inceptionToken]), + ); + } + } return [ { - prices: amountsOut, - unit: unitOut, + prices: amounts.map(amount => { + const ratio = this.state[inceptionToken].ratio; + return (ratio * amount) / DECIMALS; + }), + unit: DECIMALS, + gasCost: 120_000, + exchange: this.dexKey, data: { - ratio: unitOut, + exchange: dest.vault, }, - exchange: this.dexKey, - poolIdentifier: - `${srcToken.address}_${destToken.address}`.toLowerCase(), - gasCost: 120_000, - poolAddresses: [this.config.vault], + poolAddresses: [inceptionToken], }, ]; } - getCalldataGasCost(poolPrices: PoolPrices): number | number[] { + getCalldataGasCost( + poolPrices: PoolPrices, + ): number | number[] { return CALLDATA_GAS_COST.DEX_OVERHEAD + CALLDATA_GAS_COST.LENGTH_SMALL; } - getAdapterParam( - srcToken: string, - destToken: string, - srcAmount: string, - destAmount: string, - data: InceptionData, - side: SwapSide, - ): AdapterExchangeParam { + getAdapterParam(): AdapterExchangeParam { return { targetExchange: NULL_ADDRESS, payload: '0x', @@ -153,22 +220,35 @@ export class Inception extends SimpleExchange implements IDex { srcAmount: NumberAsString, destAmount: NumberAsString, recipient: Address, - data: InceptionData, + data: InceptionDexData, side: SwapSide, ): DexExchangeParam { - const swapData = this.vaultInterface.encodeFunctionData( - depositETHFunction, - [srcAmount, recipient], - ); + const dexParams = getTokenFromAddress(this.network, srcToken); + + const isNative = dexParams.baseTokenSlug === 'ETH'; + let swapData; + let dexFuncHasRecipient; + if (isNative) { + swapData = this.poolInterface.encodeFunctionData('stake()', []); + dexFuncHasRecipient = false; + } else { + swapData = this.vaultInterface.encodeFunctionData('deposit', [ + srcAmount, + recipient, + ]); + dexFuncHasRecipient = true; + } return { - needWrapNative: this.needWrapNative, - dexFuncHasRecipient: true, + needWrapNative: false, + dexFuncHasRecipient, exchangeData: swapData, - targetExchange: this.config.vault, + targetExchange: dexParams.vault, + swappedAmountNotPresentInExchangeData: true, returnAmountPos: undefined, }; } + async updatePoolState(): Promise {} async getTopPoolsForToken( diff --git a/src/dex/inception/tokens.ts b/src/dex/inception/tokens.ts new file mode 100644 index 000000000..2a452e163 --- /dev/null +++ b/src/dex/inception/tokens.ts @@ -0,0 +1,37 @@ +import { Network } from '../../constants'; +import { DexParams } from './types'; + +export const Tokens: { [network: number]: { [symbol: string]: DexParams } } = + {}; + +const TokensByAddress: { + [network: number]: { [address: string]: DexParams }; +} = {}; + +export function setTokensOnNetwork(network: Network, tokens: DexParams[]) { + if (Tokens[network] === undefined) { + Tokens[network] = {}; + } + + if (TokensByAddress[network] === undefined) { + TokensByAddress[network] = {}; + } + + for (let token of tokens) { + token.vault = token.vault.toLowerCase(); + token.token = token.token.toLowerCase(); + token.baseToken = token.baseToken.toLowerCase(); + + Tokens[network][token.symbol] = token; + TokensByAddress[network][token.vault] = token; + TokensByAddress[network][token.token] = token; + TokensByAddress[network][token.baseToken] = token; + } +} + +export function getTokenFromAddress( + network: Network, + address: string, +): DexParams { + return TokensByAddress[network]?.[address.toLowerCase()]; +} diff --git a/src/dex/inception/types.ts b/src/dex/inception/types.ts index 501855173..9926a7f3a 100644 --- a/src/dex/inception/types.ts +++ b/src/dex/inception/types.ts @@ -1,13 +1,17 @@ export type PoolState = { - ratio: bigint; + [symbol: string]: { + ratio: bigint; + }; }; -export type InceptionData = { - ratio: bigint; +export type InceptionDexData = { + exchange: string; }; export type DexParams = { + symbol: string; vault: string; - token?: string; + token: string; + baseToken: string; baseTokenSlug: string; }; diff --git a/src/dex/inception/utils.ts b/src/dex/inception/utils.ts index 7ef0a5408..9ada8e3cc 100644 --- a/src/dex/inception/utils.ts +++ b/src/dex/inception/utils.ts @@ -1,29 +1,73 @@ import { Contract } from 'web3-eth-contract'; -import { PoolState } from './types'; +import { DexParams, PoolState } from './types'; import { AbiCoder, Interface } from '@ethersproject/abi'; +import { InceptionConfig } from './config'; +import { Network } from '../../constants'; const coder = new AbiCoder(); export async function getOnChainRatio( multiContract: Contract, - vaultAddress: string, poolInterface: Interface, + network: Network, + p: DexParams, blockNumber: number | 'latest', -): Promise { +): Promise { + let addr; + if (p.baseTokenSlug === 'ETH') { + addr = p.token; + } else { + addr = p.vault; + } const data: { returnData: any[] } = await multiContract.methods .aggregate([ { - target: vaultAddress, + target: addr, callData: poolInterface.encodeFunctionData('ratio', []), }, ]) .call({}, blockNumber); const decodedData = coder.decode(['uint256'], data.returnData[0]); - const ratio = BigInt(decodedData[0].toString()); + return ratio; +} - return { - ratio, - }; +export async function getOnChainState( + multiContract: Contract, + poolInterface: Interface, + network: Network, + blockNumber: number | 'latest', +): Promise { + let tokenList = await fetchTokenList(network); + const state: PoolState = {}; + for (const p of tokenList) { + let addr; + if (p.baseTokenSlug === 'ETH') { + addr = p.token; + } else { + addr = p.vault; + } + + const data = await multiContract.methods + .aggregate([ + { + target: addr, + callData: poolInterface.encodeFunctionData('ratio', []), + }, + ]) + .call({}, blockNumber); + + const decodedData = coder.decode(['uint256'], data.returnData[0]); + const ratio = BigInt(decodedData[0].toString()); + state[p.symbol.toLowerCase()] = { ratio }; + } + + return state; } + +export const fetchTokenList = async ( + network: Network, +): Promise => { + return InceptionConfig['InceptionLRT'][network]; +}; diff --git a/src/dex/index.ts b/src/dex/index.ts index c677c92eb..2e1026e36 100644 --- a/src/dex/index.ts +++ b/src/dex/index.ts @@ -89,7 +89,6 @@ import { Spark } from './spark/spark'; import { VelodromeSlipstream } from './uniswap-v3/forks/velodrome-slipstream/velodrome-slipstream'; import { AaveV3Stata } from './aave-v3-stata/aave-v3-stata'; import { Inception } from './inception/inception'; -import { InceptionNative } from './inception/inception-native'; const LegacyDexes = [ CurveV2, @@ -175,7 +174,6 @@ const Dexes = [ Spark, AaveV3Stata, Inception, - InceptionNative, ]; export type LegacyDexConstructor = new (dexHelper: IDexHelper) => IDexTxBuilder< From 35511e78b0f4ff287e381558b7d4639da9360b25 Mon Sep 17 00:00:00 2001 From: neanvo Date: Fri, 2 Aug 2024 12:21:52 +0300 Subject: [PATCH 06/22] feat: pr fixes --- src/abi/inception/inception-ratio-feed.json | 93 ++++++++++++++++++ src/dex/inception/config.ts | 10 +- src/dex/inception/inception-event-pool.ts | 57 +++++++++++ src/dex/inception/inception-events.test.ts | 66 +++++++++++++ .../inception/inception-integration.test.ts | 67 +++++++++++++ src/dex/inception/inception-price-feed.ts | 72 ++++++++++++++ src/dex/inception/inception.ts | 94 ++++++++----------- src/dex/inception/types.ts | 9 ++ 8 files changed, 410 insertions(+), 58 deletions(-) create mode 100644 src/abi/inception/inception-ratio-feed.json create mode 100644 src/dex/inception/inception-event-pool.ts create mode 100644 src/dex/inception/inception-events.test.ts create mode 100644 src/dex/inception/inception-integration.test.ts create mode 100644 src/dex/inception/inception-price-feed.ts diff --git a/src/abi/inception/inception-ratio-feed.json b/src/abi/inception/inception-ratio-feed.json new file mode 100644 index 000000000..23e76639e --- /dev/null +++ b/src/abi/inception/inception-ratio-feed.json @@ -0,0 +1,93 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldRatio", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newRatio", + "type": "uint256" + } + ], + "name": "RatioUpdated", + "type": "event" + }, + { + "inputs": [], + "name": "MAX_THRESHOLD", + "outputs": [{ "internalType": "uint32", "name": "", "type": "uint32" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "day", "type": "uint256" } + ], + "name": "averagePercentageRate", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" } + ], + "name": "getRatioFor", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "address", "name": "", "type": "address" }], + "name": "historicalRatios", + "outputs": [ + { "internalType": "uint40", "name": "lastUpdate", "type": "uint40" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "inceptionOperator", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "initialize", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "ratioThreshold", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address[]", "name": "addresses", "type": "address[]" }, + { "internalType": "uint256[]", "name": "ratios", "type": "uint256[]" } + ], + "name": "updateRatioBatch", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/src/dex/inception/config.ts b/src/dex/inception/config.ts index 83d60ee22..288e62261 100644 --- a/src/dex/inception/config.ts +++ b/src/dex/inception/config.ts @@ -1,4 +1,4 @@ -import { DexParams } from './types'; +import { DexParams, PricePoolParams } from './types'; import { AdapterMappings, DexConfigMap } from '../../types'; import { Network, SwapSide } from '../../constants'; @@ -100,6 +100,14 @@ export const InceptionConfig: DexConfigMap = { }, }; +export const InceptionPricePoolConfig: DexConfigMap = { + InceptionLRT: { + [Network.MAINNET]: { + ratioFeed: '0xFd73Be536503B5Aa80Bf99D1Fd65b1306c69B191', + }, + }, +}; + export const Adapters: Record = { [Network.MAINNET]: { [SwapSide.SELL]: [{ name: '', index: 0 }] }, }; diff --git a/src/dex/inception/inception-event-pool.ts b/src/dex/inception/inception-event-pool.ts new file mode 100644 index 000000000..e73cdf577 --- /dev/null +++ b/src/dex/inception/inception-event-pool.ts @@ -0,0 +1,57 @@ +import { DeepReadonly } from 'ts-essentials'; +import { Logger } from '../../types'; +import { ComposedEventSubscriber } from '../../composed-event-subscriber'; +import { IDexHelper } from '../../dex-helper/idex-helper'; +import { lens } from '../../lens'; +import { PoolConfig, PoolState } from './types'; +import { getOnChainState } from './utils'; +import { Interface, JsonFragment } from '@ethersproject/abi'; +import { InceptionPriceFeed } from './inception-price-feed'; + +export class InceptionEventPool extends ComposedEventSubscriber { + constructor( + parentName: string, + protected network: number, + protected dexHelper: IDexHelper, + logger: Logger, + public poolConfig: PoolConfig, + public poolInterface: Interface, + ) { + const ratioFeedEvent = new InceptionPriceFeed( + poolConfig.ratioFeedAddress, + network, + lens>(), + logger, + ); + super( + parentName, + 'pool', + logger, + dexHelper, + [ratioFeedEvent], + poolConfig.initState, + ); + } + + getIdentifier(): string { + return `${this.parentName}_${this.poolConfig.ratioFeedAddress}`.toLowerCase(); + } + + /** + * The function generates state using on-chain calls. This + * function is called to regenerate state if the event based + * system fails to fetch events and the local state is no + * more correct. + * @param blockNumber - Blocknumber for which the state should + * should be generated + * @returns state of the event subscriber at blocknumber + */ + async generateState(blockNumber: number): Promise> { + return getOnChainState( + this.dexHelper.multiContract, + this.poolInterface, + this.network, + blockNumber, + ); + } +} diff --git a/src/dex/inception/inception-events.test.ts b/src/dex/inception/inception-events.test.ts new file mode 100644 index 000000000..59b2b550b --- /dev/null +++ b/src/dex/inception/inception-events.test.ts @@ -0,0 +1,66 @@ +import dotenv from 'dotenv'; +dotenv.config(); +import { InceptionEventPool } from './inception-event-pool'; +import { InceptionPricePoolConfig } from './config'; +import { Network } from '../../constants'; +import { DummyDexHelper } from '../../dex-helper/index'; +import INCEPTION_VAULT_ABI from '../../abi/inception/inception-vault.json'; +import { testEventSubscriber } from '../../../tests/utils-events'; +import { PoolState } from './types'; +import { Interface } from '@ethersproject/abi'; +import { fetchTokenList } from './utils'; +import { setTokensOnNetwork } from './tokens'; + +jest.setTimeout(50 * 1000); +const dexKey = 'InceptionLRT'; +const network = Network.MAINNET; +const ratioFeedAddress = InceptionPricePoolConfig[dexKey][network].ratioFeed; +const poolInterface = new Interface(INCEPTION_VAULT_ABI); + +async function fetchPoolState( + inceptionEventPool: InceptionEventPool, + blockNumber: number, +): Promise { + return inceptionEventPool.generateState(blockNumber); +} + +describe('Inception Event', function () { + const blockNumbers: { [eventName: string]: number[] } = { + RatioUpdated: [20438962, 20431765, 20424601], + }; + + describe('InceptionEventPool', function () { + Object.keys(blockNumbers).forEach((event: string) => { + blockNumbers[event].forEach((blockNumber: number) => { + it(`Should return the correct state after the ${blockNumber}: ${event}`, async function () { + const dexHelper = new DummyDexHelper(network); + const logger = dexHelper.getLogger(dexKey); + const inceptionEventPool = new InceptionEventPool( + dexKey, + network, + dexHelper, + logger, + { + ratioFeedAddress: ratioFeedAddress, + initState: {}, + }, + poolInterface, + ); + + const tokenList = await fetchTokenList(network); + setTokensOnNetwork(network, tokenList); + + await testEventSubscriber( + inceptionEventPool, + inceptionEventPool.addressesSubscribed, + (_blockNumber: number) => + fetchPoolState(inceptionEventPool, _blockNumber), + blockNumber, + `${dexKey}_${ratioFeedAddress}`.toLowerCase(), + dexHelper.provider, + ); + }); + }); + }); + }); +}); diff --git a/src/dex/inception/inception-integration.test.ts b/src/dex/inception/inception-integration.test.ts new file mode 100644 index 000000000..0e49f7b2b --- /dev/null +++ b/src/dex/inception/inception-integration.test.ts @@ -0,0 +1,67 @@ +import dotenv from 'dotenv'; +dotenv.config(); + +import { DummyDexHelper } from '../../dex-helper/index'; +import { Network, SwapSide } from '../../constants'; +import { BI_POWS } from '../../bigint-constants'; +import { Inception } from './inception'; +import { checkPoolPrices } from '../../../tests/utils'; +import { Tokens } from '../../../tests/constants-e2e'; +import { InceptionConfig } from './config'; + +const network = Network.MAINNET; +const dexKey = 'InceptionLRT'; +const dexHelper = new DummyDexHelper(network); + +describe('Inception', function () { + let blockNumber: number; + let inception: Inception; + + beforeAll(async () => { + blockNumber = await dexHelper.web3Provider.eth.getBlockNumber(); + inception = new Inception(network, dexKey, dexHelper); + await inception.initializePricing(blockNumber); + }); + + InceptionConfig[dexKey][network].forEach(Pair => { + const TokenASymbol = Pair.baseTokenSlug; + const TokenA = Tokens[network][TokenASymbol]; + + const TokenBSymbol = Pair.symbol; + const TokenB = Tokens[network][TokenBSymbol]; + const TokenBAmounts = [0n, BI_POWS[6], 2000000n]; + + describe(`deposit() Swap Function for ${TokenBSymbol} <> ${TokenASymbol}`, () => { + it('getPoolIdentifiers and getPricesVolume SELL', async function () { + const pools = await inception.getPoolIdentifiers( + TokenB, + TokenA, + SwapSide.SELL, + blockNumber, + ); + console.log( + `${TokenBSymbol} <> ${TokenASymbol} Pool Identifiers: `, + pools, + ); + + expect(pools.length).toBeGreaterThan(0); + + const poolPrices = await inception.getPricesVolume( + TokenB, + TokenA, + TokenBAmounts, + SwapSide.SELL, + blockNumber, + pools, + ); + console.log( + `${TokenBSymbol} <> ${TokenASymbol} Pool Prices: `, + poolPrices, + ); + + expect(poolPrices).not.toBeNull(); + checkPoolPrices(poolPrices!, TokenBAmounts, SwapSide.SELL, dexKey); + }); + }); + }); +}); diff --git a/src/dex/inception/inception-price-feed.ts b/src/dex/inception/inception-price-feed.ts new file mode 100644 index 000000000..96b158faa --- /dev/null +++ b/src/dex/inception/inception-price-feed.ts @@ -0,0 +1,72 @@ +import _ from 'lodash'; +import { Interface } from '@ethersproject/abi'; +import { DeepReadonly } from 'ts-essentials'; +import { PartialEventSubscriber } from '../../composed-event-subscriber'; +import { + Address, + BlockHeader, + Log, + Logger, + MultiCallInput, + MultiCallOutput, +} from '../../types'; +import { Lens } from '../../lens'; +import { PoolState } from './types'; +import INCEPTION_RATIO_FEED from '../../abi/inception/inception-ratio-feed.json'; +import { getTokenFromAddress } from './tokens'; +import { Network } from '../../constants'; + +export class InceptionPriceFeed extends PartialEventSubscriber< + State, + PoolState +> { + protected ratioFeedInterface: Interface = new Interface(INCEPTION_RATIO_FEED); + constructor( + private ratioFeedAddress: Address, + private network: Network, + lens: Lens, DeepReadonly>, + logger: Logger, + ) { + super([ratioFeedAddress], lens, logger); + } + + getRatio(state: DeepReadonly) { + return this.lens.get()(state).ratio; + } + + public processLog( + state: DeepReadonly, + log: Readonly, + blockHeader: Readonly, + ): DeepReadonly | null { + try { + const parsed = this.ratioFeedInterface.parseLog(log); + const _state: PoolState = _.cloneDeep(state); + switch (parsed.name) { + case 'RatioUpdated': { + const tokenInfo = getTokenFromAddress(this.network, parsed.args[0]); + _state[tokenInfo.symbol.toLowerCase()] = { + ratio: BigInt(parsed.args[2].toString()), + }; + return _state; + } + default: + return null; + } + } catch (e) { + this.logger.error('Failed to parse log', e); + return null; + } + } + + public getGenerateStateMultiCallInputs(): MultiCallInput[] { + return []; + } + + public generateState( + multicallOutputs: MultiCallOutput[], + blockNumber?: number | 'latest', + ): DeepReadonly { + return {}; + } +} diff --git a/src/dex/inception/inception.ts b/src/dex/inception/inception.ts index e0bdacce6..3b92cf1fa 100644 --- a/src/dex/inception/inception.ts +++ b/src/dex/inception/inception.ts @@ -1,5 +1,5 @@ import { Interface, JsonFragment } from '@ethersproject/abi'; -import { AsyncOrSync } from 'ts-essentials'; +import { AsyncOrSync, DeepReadonly } from 'ts-essentials'; import { NumberAsString, SwapSide } from '@paraswap/core'; import { AdapterExchangeParam, @@ -20,9 +20,10 @@ import { IDex } from '../../dex/idex'; import { IDexHelper } from '../../dex-helper/idex-helper'; import { DexParams, InceptionDexData, PoolState } from './types'; import { SimpleExchange } from '../simple-exchange'; -import { Adapters, InceptionConfig } from './config'; +import { Adapters, InceptionConfig, InceptionPricePoolConfig } from './config'; import { getTokenFromAddress, setTokensOnNetwork, Tokens } from './tokens'; -import { fetchTokenList, getOnChainRatio, getOnChainState } from './utils'; +import { fetchTokenList, getOnChainState } from './utils'; +import { InceptionEventPool } from './inception-event-pool'; const DECIMALS = BigInt(1e18); @@ -45,7 +46,7 @@ export class Inception logger: Logger; - private state: Record = {}; + private eventPool: InceptionEventPool; constructor( readonly network: Network, @@ -58,6 +59,17 @@ export class Inception this.logger = dexHelper.getLogger(dexKey); this.vaultInterface = new Interface(INCEPTION_ABI as JsonFragment[]); this.poolInterface = new Interface(INCEPTION_POOL_ABI as JsonFragment[]); + this.eventPool = new InceptionEventPool( + dexKey, + network, + dexHelper, + this.logger, + { + ratioFeedAddress: InceptionPricePoolConfig[dexKey][network].ratioFeed, + initState: {}, + }, + this.poolInterface, + ); } async initializePricing(blockNumber: number) { @@ -67,10 +79,14 @@ export class Inception this.network, blockNumber, ); - await this.initializeTokens(poolState, blockNumber); + + await this.eventPool.initialize(blockNumber, { + state: poolState, + }); + await this.initializeTokens(); } - async initializeTokens(poolState: PoolState, blockNumber: number) { + async initializeTokens() { let cachedTokenList = await this.dexHelper.cache.getAndCacheLocally( this.dexKey, this.network, @@ -83,13 +99,6 @@ export class Inception const tokenListParsed = JSON.parse(cachedTokenList); setTokensOnNetwork(this.network, tokenListParsed); - - tokenListParsed.forEach((p: DexParams) => { - this.state[p.token] = { - blockNumber, - ratio: poolState[p.symbol.toLowerCase()].ratio, - }; - }); return; } @@ -104,14 +113,6 @@ export class Inception ); setTokensOnNetwork(this.network, tokenList); - - // init state for all tokens as empty - tokenList.forEach(p => { - this.state[p.token] = { - blockNumber, - ratio: poolState[p.symbol.toLowerCase()].ratio, - }; - }); } getAdapters(side: SwapSide): { name: string; index: number }[] | null { @@ -133,6 +134,17 @@ export class Inception ]; } + async getPoolState( + pool: InceptionEventPool, + blockNumber: number, + ): Promise { + const eventState = pool.getState(blockNumber); + if (eventState) return eventState; + const onChainState = await pool.generateState(blockNumber); + pool.setState(onChainState, blockNumber); + return onChainState; + } + async getPricesVolume( srcToken: Token, destToken: Token, @@ -148,45 +160,13 @@ export class Inception return null; } - const [inceptionToken] = [src.token.toLowerCase()]; - - if ( - !this.state[inceptionToken]?.blockNumber || - blockNumber > this.state[inceptionToken].blockNumber - ) { - const cached = await this.dexHelper.cache.get( - this.dexKey, - this.network, - `state_${inceptionToken}`, - ); - if (cached) { - this.state[inceptionToken] = Utils.Parse(cached); - } else { - const ratio = await getOnChainRatio( - this.dexHelper.multiContract, - src.symbol === 'ETH' ? this.poolInterface : this.vaultInterface, - this.network, - src, - blockNumber, - ); - this.state[inceptionToken] = { - blockNumber, - ratio, - }; - this.dexHelper.cache.setex( - this.dexKey, - this.network, - `state_${inceptionToken}`, - 60, - Utils.Serialize(this.state[inceptionToken]), - ); - } - } + const poolState = await this.getPoolState(this.eventPool, blockNumber); + if (!poolState) return null; return [ { prices: amounts.map(amount => { - const ratio = this.state[inceptionToken].ratio; + const ratio = poolState[src.symbol.toLowerCase()].ratio; return (ratio * amount) / DECIMALS; }), unit: DECIMALS, @@ -195,7 +175,7 @@ export class Inception data: { exchange: dest.vault, }, - poolAddresses: [inceptionToken], + poolAddresses: [src.token.toLowerCase()], }, ]; } diff --git a/src/dex/inception/types.ts b/src/dex/inception/types.ts index 9926a7f3a..0736e2eeb 100644 --- a/src/dex/inception/types.ts +++ b/src/dex/inception/types.ts @@ -4,6 +4,11 @@ export type PoolState = { }; }; +export type PoolConfig = { + ratioFeedAddress: string; + initState: PoolState; +}; + export type InceptionDexData = { exchange: string; }; @@ -15,3 +20,7 @@ export type DexParams = { baseToken: string; baseTokenSlug: string; }; + +export type PricePoolParams = { + ratioFeed: string; +}; From 536c26fd0b71135cdd08098affecc7dcc86a91a6 Mon Sep 17 00:00:00 2001 From: Yevhen Shulha Date: Mon, 5 Aug 2024 22:09:58 +0300 Subject: [PATCH 07/22] fix: minor --- src/dex/inception/inception-events.test.ts | 4 +-- src/dex/inception/inception.ts | 32 +++------------------- src/dex/inception/utils.ts | 6 ++-- 3 files changed, 8 insertions(+), 34 deletions(-) diff --git a/src/dex/inception/inception-events.test.ts b/src/dex/inception/inception-events.test.ts index 59b2b550b..b64d3b4d1 100644 --- a/src/dex/inception/inception-events.test.ts +++ b/src/dex/inception/inception-events.test.ts @@ -8,8 +8,8 @@ import INCEPTION_VAULT_ABI from '../../abi/inception/inception-vault.json'; import { testEventSubscriber } from '../../../tests/utils-events'; import { PoolState } from './types'; import { Interface } from '@ethersproject/abi'; -import { fetchTokenList } from './utils'; import { setTokensOnNetwork } from './tokens'; +import { getTokenList } from './utils'; jest.setTimeout(50 * 1000); const dexKey = 'InceptionLRT'; @@ -47,7 +47,7 @@ describe('Inception Event', function () { poolInterface, ); - const tokenList = await fetchTokenList(network); + const tokenList = await getTokenList(network); setTokensOnNetwork(network, tokenList); await testEventSubscriber( diff --git a/src/dex/inception/inception.ts b/src/dex/inception/inception.ts index 3b92cf1fa..8a4205436 100644 --- a/src/dex/inception/inception.ts +++ b/src/dex/inception/inception.ts @@ -22,14 +22,11 @@ import { DexParams, InceptionDexData, PoolState } from './types'; import { SimpleExchange } from '../simple-exchange'; import { Adapters, InceptionConfig, InceptionPricePoolConfig } from './config'; import { getTokenFromAddress, setTokensOnNetwork, Tokens } from './tokens'; -import { fetchTokenList, getOnChainState } from './utils'; +import { getOnChainState, getTokenList } from './utils'; import { InceptionEventPool } from './inception-event-pool'; const DECIMALS = BigInt(1e18); -export const TOKEN_LIST_CACHE_KEY = 'inceptionlrt-token-list'; -const TOKEN_LIST_TTL_SECONDS = 24 * 60 * 60; // 1 day - export class Inception extends SimpleExchange implements IDex @@ -87,30 +84,7 @@ export class Inception } async initializeTokens() { - let cachedTokenList = await this.dexHelper.cache.getAndCacheLocally( - this.dexKey, - this.network, - TOKEN_LIST_CACHE_KEY, - TOKEN_LIST_TTL_SECONDS, - ); - - if (cachedTokenList !== null) { - if (Object.keys(Tokens).length !== 0) return; - - const tokenListParsed = JSON.parse(cachedTokenList); - setTokensOnNetwork(this.network, tokenListParsed); - return; - } - - let tokenList = await fetchTokenList(this.network); - - await this.dexHelper.cache.setexAndCacheLocally( - this.dexKey, - this.network, - TOKEN_LIST_CACHE_KEY, - TOKEN_LIST_TTL_SECONDS, - JSON.stringify(tokenList), - ); + let tokenList = await getTokenList(this.network); setTokensOnNetwork(this.network, tokenList); } @@ -235,6 +209,8 @@ export class Inception tokenAddress: Address, limit: number, ): Promise { + await this.initializeTokens(); + // TODO: Implement return []; } diff --git a/src/dex/inception/utils.ts b/src/dex/inception/utils.ts index 9ada8e3cc..10b9684a0 100644 --- a/src/dex/inception/utils.ts +++ b/src/dex/inception/utils.ts @@ -39,7 +39,7 @@ export async function getOnChainState( network: Network, blockNumber: number | 'latest', ): Promise { - let tokenList = await fetchTokenList(network); + let tokenList = await getTokenList(network); const state: PoolState = {}; for (const p of tokenList) { let addr; @@ -66,8 +66,6 @@ export async function getOnChainState( return state; } -export const fetchTokenList = async ( - network: Network, -): Promise => { +export const getTokenList = async (network: Network): Promise => { return InceptionConfig['InceptionLRT'][network]; }; From e918d17e1180ddc7e65d360919478e8cca370fe8 Mon Sep 17 00:00:00 2001 From: neanvo Date: Fri, 23 Aug 2024 09:56:14 +0300 Subject: [PATCH 08/22] feat: requested changes --- src/dex/inception/inception-event-pool.ts | 54 ++++++++------ .../inception/inception-integration.test.ts | 11 ++- src/dex/inception/inception-price-feed.ts | 72 ------------------- src/dex/inception/inception.ts | 19 +++++ 4 files changed, 61 insertions(+), 95 deletions(-) delete mode 100644 src/dex/inception/inception-price-feed.ts diff --git a/src/dex/inception/inception-event-pool.ts b/src/dex/inception/inception-event-pool.ts index e73cdf577..bcf38b514 100644 --- a/src/dex/inception/inception-event-pool.ts +++ b/src/dex/inception/inception-event-pool.ts @@ -1,14 +1,16 @@ -import { DeepReadonly } from 'ts-essentials'; -import { Logger } from '../../types'; -import { ComposedEventSubscriber } from '../../composed-event-subscriber'; +import _ from 'lodash'; +import { AsyncOrSync, DeepReadonly } from 'ts-essentials'; +import { BlockHeader, Log, Logger } from '../../types'; +import { StatefulEventSubscriber } from '../../stateful-event-subscriber'; import { IDexHelper } from '../../dex-helper/idex-helper'; -import { lens } from '../../lens'; import { PoolConfig, PoolState } from './types'; import { getOnChainState } from './utils'; import { Interface, JsonFragment } from '@ethersproject/abi'; -import { InceptionPriceFeed } from './inception-price-feed'; +import INCEPTION_RATIO_FEED from '../../abi/inception/inception-ratio-feed.json'; +import { getTokenFromAddress } from './tokens'; -export class InceptionEventPool extends ComposedEventSubscriber { +export class InceptionEventPool extends StatefulEventSubscriber { + protected ratioFeedInterface: Interface = new Interface(INCEPTION_RATIO_FEED); constructor( parentName: string, protected network: number, @@ -17,24 +19,32 @@ export class InceptionEventPool extends ComposedEventSubscriber { public poolConfig: PoolConfig, public poolInterface: Interface, ) { - const ratioFeedEvent = new InceptionPriceFeed( - poolConfig.ratioFeedAddress, - network, - lens>(), - logger, - ); - super( - parentName, - 'pool', - logger, - dexHelper, - [ratioFeedEvent], - poolConfig.initState, - ); + super(parentName, 'pool', dexHelper, logger); } - getIdentifier(): string { - return `${this.parentName}_${this.poolConfig.ratioFeedAddress}`.toLowerCase(); + protected processLog( + state: DeepReadonly, + log: Readonly, + blockHeader: Readonly, + ): AsyncOrSync | null> { + try { + const parsed = this.ratioFeedInterface.parseLog(log); + const _state: PoolState = _.cloneDeep(state); + switch (parsed.name) { + case 'RatioUpdated': { + const tokenInfo = getTokenFromAddress(this.network, parsed.args[0]); + _state[tokenInfo.symbol.toLowerCase()] = { + ratio: BigInt(parsed.args[2].toString()), + }; + return _state; + } + default: + return null; + } + } catch (e) { + this.logger.error('Failed to parse log', e); + return null; + } } /** diff --git a/src/dex/inception/inception-integration.test.ts b/src/dex/inception/inception-integration.test.ts index 0e49f7b2b..706e7f12c 100644 --- a/src/dex/inception/inception-integration.test.ts +++ b/src/dex/inception/inception-integration.test.ts @@ -5,7 +5,7 @@ import { DummyDexHelper } from '../../dex-helper/index'; import { Network, SwapSide } from '../../constants'; import { BI_POWS } from '../../bigint-constants'; import { Inception } from './inception'; -import { checkPoolPrices } from '../../../tests/utils'; +import { checkPoolPrices, checkPoolsLiquidity } from '../../../tests/utils'; import { Tokens } from '../../../tests/constants-e2e'; import { InceptionConfig } from './config'; @@ -62,6 +62,15 @@ describe('Inception', function () { expect(poolPrices).not.toBeNull(); checkPoolPrices(poolPrices!, TokenBAmounts, SwapSide.SELL, dexKey); }); + + it('getTopPoolsForToken', async function () { + const poolLiquidity = await inception.getTopPoolsForToken( + TokenA.address, + 10, + ); + + checkPoolsLiquidity(poolLiquidity, TokenA.address, dexKey); + }); }); }); }); diff --git a/src/dex/inception/inception-price-feed.ts b/src/dex/inception/inception-price-feed.ts deleted file mode 100644 index 96b158faa..000000000 --- a/src/dex/inception/inception-price-feed.ts +++ /dev/null @@ -1,72 +0,0 @@ -import _ from 'lodash'; -import { Interface } from '@ethersproject/abi'; -import { DeepReadonly } from 'ts-essentials'; -import { PartialEventSubscriber } from '../../composed-event-subscriber'; -import { - Address, - BlockHeader, - Log, - Logger, - MultiCallInput, - MultiCallOutput, -} from '../../types'; -import { Lens } from '../../lens'; -import { PoolState } from './types'; -import INCEPTION_RATIO_FEED from '../../abi/inception/inception-ratio-feed.json'; -import { getTokenFromAddress } from './tokens'; -import { Network } from '../../constants'; - -export class InceptionPriceFeed extends PartialEventSubscriber< - State, - PoolState -> { - protected ratioFeedInterface: Interface = new Interface(INCEPTION_RATIO_FEED); - constructor( - private ratioFeedAddress: Address, - private network: Network, - lens: Lens, DeepReadonly>, - logger: Logger, - ) { - super([ratioFeedAddress], lens, logger); - } - - getRatio(state: DeepReadonly) { - return this.lens.get()(state).ratio; - } - - public processLog( - state: DeepReadonly, - log: Readonly, - blockHeader: Readonly, - ): DeepReadonly | null { - try { - const parsed = this.ratioFeedInterface.parseLog(log); - const _state: PoolState = _.cloneDeep(state); - switch (parsed.name) { - case 'RatioUpdated': { - const tokenInfo = getTokenFromAddress(this.network, parsed.args[0]); - _state[tokenInfo.symbol.toLowerCase()] = { - ratio: BigInt(parsed.args[2].toString()), - }; - return _state; - } - default: - return null; - } - } catch (e) { - this.logger.error('Failed to parse log', e); - return null; - } - } - - public getGenerateStateMultiCallInputs(): MultiCallInput[] { - return []; - } - - public generateState( - multicallOutputs: MultiCallOutput[], - blockNumber?: number | 'latest', - ): DeepReadonly { - return {}; - } -} diff --git a/src/dex/inception/inception.ts b/src/dex/inception/inception.ts index 3b92cf1fa..b2319f0a9 100644 --- a/src/dex/inception/inception.ts +++ b/src/dex/inception/inception.ts @@ -235,6 +235,25 @@ export class Inception tokenAddress: Address, limit: number, ): Promise { + await this.initializeTokens(); + const token: DexParams = getTokenFromAddress(this.network, tokenAddress); + + if (token) { + return [ + { + liquidityUSD: 1e9, + exchange: this.dexKey, + address: token.vault, + connectorTokens: [ + { + address: token.token, + decimals: 1e18, + }, + ], + }, + ]; + } + return []; } From e50b9270c82792806fbad7279b572e26d4e8f077 Mon Sep 17 00:00:00 2001 From: Yevhen Shulha Date: Tue, 27 Aug 2024 15:13:29 +0300 Subject: [PATCH 09/22] fix: small changes --- src/dex/inception/inception-e2e.test.ts | 2 +- src/dex/inception/inception-event-pool.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dex/inception/inception-e2e.test.ts b/src/dex/inception/inception-e2e.test.ts index fce7fd111..846d86869 100644 --- a/src/dex/inception/inception-e2e.test.ts +++ b/src/dex/inception/inception-e2e.test.ts @@ -30,7 +30,7 @@ function testForNetwork( [SwapSide.SELL, ['deposit' as ContractMethod]], ]); - describe(`${inceptionSlug} on ${network}`, () => { + describe(`${inceptionSlug}`, () => { sideToContractMethods.forEach((contractMethods, side) => describe(`${side}`, () => { contractMethods.forEach((contractMethod: ContractMethod) => { diff --git a/src/dex/inception/inception-event-pool.ts b/src/dex/inception/inception-event-pool.ts index bcf38b514..7f4710e65 100644 --- a/src/dex/inception/inception-event-pool.ts +++ b/src/dex/inception/inception-event-pool.ts @@ -39,7 +39,7 @@ export class InceptionEventPool extends StatefulEventSubscriber { return _state; } default: - return null; + return _state; } } catch (e) { this.logger.error('Failed to parse log', e); From 243c227bdd0b3d791d84d28b0234f2bd0091d972 Mon Sep 17 00:00:00 2001 From: neanvo Date: Fri, 6 Sep 2024 10:08:31 +0300 Subject: [PATCH 10/22] fix: event test bugfix --- src/dex/inception/inception-events.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dex/inception/inception-events.test.ts b/src/dex/inception/inception-events.test.ts index b64d3b4d1..087e05dc9 100644 --- a/src/dex/inception/inception-events.test.ts +++ b/src/dex/inception/inception-events.test.ts @@ -26,7 +26,7 @@ async function fetchPoolState( describe('Inception Event', function () { const blockNumbers: { [eventName: string]: number[] } = { - RatioUpdated: [20438962, 20431765, 20424601], + RatioUpdated: [20438963, 20431766, 20424602], }; describe('InceptionEventPool', function () { From a53e90c39a787c37e88b5bdde8398b3d58788bec Mon Sep 17 00:00:00 2001 From: Yevhen Shulha Date: Mon, 9 Sep 2024 11:51:47 +0300 Subject: [PATCH 11/22] fix: minor --- src/dex/inception/config.ts | 8 ++------ src/dex/inception/inception-e2e.test.ts | 2 +- src/dex/inception/inception-event-pool.ts | 11 +---------- src/dex/inception/inception.ts | 9 ++++++--- 4 files changed, 10 insertions(+), 20 deletions(-) diff --git a/src/dex/inception/config.ts b/src/dex/inception/config.ts index 288e62261..dcddd8608 100644 --- a/src/dex/inception/config.ts +++ b/src/dex/inception/config.ts @@ -1,6 +1,6 @@ import { DexParams, PricePoolParams } from './types'; -import { AdapterMappings, DexConfigMap } from '../../types'; -import { Network, SwapSide } from '../../constants'; +import { DexConfigMap } from '../../types'; +import { Network } from '../../constants'; export const InceptionConfig: DexConfigMap = { InceptionLRT: { @@ -107,7 +107,3 @@ export const InceptionPricePoolConfig: DexConfigMap = { }, }, }; - -export const Adapters: Record = { - [Network.MAINNET]: { [SwapSide.SELL]: [{ name: '', index: 0 }] }, -}; diff --git a/src/dex/inception/inception-e2e.test.ts b/src/dex/inception/inception-e2e.test.ts index 846d86869..f6fc05b6a 100644 --- a/src/dex/inception/inception-e2e.test.ts +++ b/src/dex/inception/inception-e2e.test.ts @@ -27,7 +27,7 @@ function testForNetwork( const inceptionSlug = config.symbol; const sideToContractMethods = new Map([ - [SwapSide.SELL, ['deposit' as ContractMethod]], + [SwapSide.SELL, [ContractMethod.swapExactAmountIn]], ]); describe(`${inceptionSlug}`, () => { diff --git a/src/dex/inception/inception-event-pool.ts b/src/dex/inception/inception-event-pool.ts index 7f4710e65..c32a30e87 100644 --- a/src/dex/inception/inception-event-pool.ts +++ b/src/dex/inception/inception-event-pool.ts @@ -5,7 +5,7 @@ import { StatefulEventSubscriber } from '../../stateful-event-subscriber'; import { IDexHelper } from '../../dex-helper/idex-helper'; import { PoolConfig, PoolState } from './types'; import { getOnChainState } from './utils'; -import { Interface, JsonFragment } from '@ethersproject/abi'; +import { Interface } from '@ethersproject/abi'; import INCEPTION_RATIO_FEED from '../../abi/inception/inception-ratio-feed.json'; import { getTokenFromAddress } from './tokens'; @@ -47,15 +47,6 @@ export class InceptionEventPool extends StatefulEventSubscriber { } } - /** - * The function generates state using on-chain calls. This - * function is called to regenerate state if the event based - * system fails to fetch events and the local state is no - * more correct. - * @param blockNumber - Blocknumber for which the state should - * should be generated - * @returns state of the event subscriber at blocknumber - */ async generateState(blockNumber: number): Promise> { return getOnChainState( this.dexHelper.multiContract, diff --git a/src/dex/inception/inception.ts b/src/dex/inception/inception.ts index d3a9ef970..10c1362bf 100644 --- a/src/dex/inception/inception.ts +++ b/src/dex/inception/inception.ts @@ -1,5 +1,5 @@ import { Interface, JsonFragment } from '@ethersproject/abi'; -import { AsyncOrSync, DeepReadonly } from 'ts-essentials'; +import { AsyncOrSync } from 'ts-essentials'; import { NumberAsString, SwapSide } from '@paraswap/core'; import { AdapterExchangeParam, @@ -20,7 +20,7 @@ import { IDex } from '../../dex/idex'; import { IDexHelper } from '../../dex-helper/idex-helper'; import { DexParams, InceptionDexData, PoolState } from './types'; import { SimpleExchange } from '../simple-exchange'; -import { Adapters, InceptionConfig, InceptionPricePoolConfig } from './config'; +import { InceptionConfig, InceptionPricePoolConfig } from './config'; import { getTokenFromAddress, setTokensOnNetwork, Tokens } from './tokens'; import { getOnChainState, getTokenList } from './utils'; import { InceptionEventPool } from './inception-event-pool'; @@ -50,7 +50,6 @@ export class Inception readonly dexKey: string, readonly dexHelper: IDexHelper, protected config = InceptionConfig[dexKey][network], - protected adapters = Adapters[network] || {}, ) { super(dexHelper, dexKey); this.logger = dexHelper.getLogger(dexKey); @@ -127,6 +126,10 @@ export class Inception blockNumber: number, limitPools?: string[], ): Promise> { + if (side === SwapSide.BUY) { + return null; + } + const src = getTokenFromAddress(this.network, srcToken.address); const dest = getTokenFromAddress(this.network, destToken.address); From 14c2da34706fbd3bfc5e2ac81cc4a4d7a24dd5c4 Mon Sep 17 00:00:00 2001 From: Yevhen Shulha Date: Mon, 9 Sep 2024 11:54:01 +0300 Subject: [PATCH 12/22] 3.8.32-inception --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 08b0f22d0..e8df93829 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@paraswap/dex-lib", - "version": "3.8.32", + "version": "3.8.32-inception", "main": "build/index.js", "types": "build/index.d.ts", "repository": "https://github.com/paraswap/paraswap-dex-lib", From 88272487d76255e35e3839f45fff2f31e8fedf96 Mon Sep 17 00:00:00 2001 From: Yevhen Shulha Date: Mon, 9 Sep 2024 13:03:58 +0300 Subject: [PATCH 13/22] fix: update event handler --- src/abi/inception/inception-ratio-feed.json | 242 +++++++++++++++++++- src/dex/inception/inception-event-pool.ts | 2 + src/dex/inception/inception-events.test.ts | 3 +- 3 files changed, 243 insertions(+), 4 deletions(-) diff --git a/src/abi/inception/inception-ratio-feed.json b/src/abi/inception/inception-ratio-feed.json index 23e76639e..743534031 100644 --- a/src/abi/inception/inception-ratio-feed.json +++ b/src/abi/inception/inception-ratio-feed.json @@ -1,4 +1,109 @@ [ + { "inputs": [], "name": "EnforcedPause", "type": "error" }, + { "inputs": [], "name": "ExpectedPause", "type": "error" }, + { "inputs": [], "name": "InconsistentInputData", "type": "error" }, + { + "inputs": [{ "internalType": "uint256", "name": "day", "type": "uint256" }], + "name": "IncorrectDay", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" } + ], + "name": "IncorrectToken", + "type": "error" + }, + { "inputs": [], "name": "InvalidInitialization", "type": "error" }, + { "inputs": [], "name": "NewRatioThresholdInvalid", "type": "error" }, + { "inputs": [], "name": "NotInitializing", "type": "error" }, + { "inputs": [], "name": "NullParams", "type": "error" }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "OperatorUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { "inputs": [], "name": "RatioThresholdNotSet", "type": "error" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint64", + "name": "version", + "type": "uint64" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "prevValue", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "newValue", + "type": "address" + } + ], + "name": "OperatorUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Paused", + "type": "event" + }, { "anonymous": false, "inputs": [ @@ -11,19 +116,76 @@ { "indexed": false, "internalType": "uint256", - "name": "oldRatio", + "name": "failedRatio", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "string", + "name": "reason", + "type": "string" + } + ], + "name": "RatioNotUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "prevValue", "type": "uint256" }, { "indexed": false, "internalType": "uint256", - "name": "newRatio", + "name": "newValue", + "type": "uint256" + } + ], + "name": "RatioThresholdChanged", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "tokenAddress", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "prevValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newValue", "type": "uint256" } ], "name": "RatioUpdated", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "Unpaused", + "type": "event" + }, { "inputs": [], "name": "MAX_THRESHOLD", @@ -67,12 +229,35 @@ "type": "function" }, { - "inputs": [], + "inputs": [ + { "internalType": "address", "name": "operator", "type": "address" } + ], "name": "initialize", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [], + "name": "owner", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "pause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "paused", + "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], "name": "ratioThreshold", @@ -80,6 +265,57 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "ratio", "type": "uint256" } + ], + "name": "repairRatioFor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "newOperator", "type": "address" } + ], + "name": "setInceptionOperator", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "newValue", "type": "uint256" } + ], + "name": "setRatioThreshold", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "newOwner", "type": "address" } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "unpause", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { "internalType": "address[]", "name": "addresses", "type": "address[]" }, diff --git a/src/dex/inception/inception-event-pool.ts b/src/dex/inception/inception-event-pool.ts index c32a30e87..3cda35af2 100644 --- a/src/dex/inception/inception-event-pool.ts +++ b/src/dex/inception/inception-event-pool.ts @@ -20,6 +20,8 @@ export class InceptionEventPool extends StatefulEventSubscriber { public poolInterface: Interface, ) { super(parentName, 'pool', dexHelper, logger); + + this.addressesSubscribed = [poolConfig.ratioFeedAddress]; } protected processLog( diff --git a/src/dex/inception/inception-events.test.ts b/src/dex/inception/inception-events.test.ts index 087e05dc9..43402e5e8 100644 --- a/src/dex/inception/inception-events.test.ts +++ b/src/dex/inception/inception-events.test.ts @@ -26,7 +26,8 @@ async function fetchPoolState( describe('Inception Event', function () { const blockNumbers: { [eventName: string]: number[] } = { - RatioUpdated: [20438963, 20431766, 20424602], + // RatioUpdated: [20438963, 20431766, 20424602], + RatioUpdated: [20438962, 20431765, 20424601, 20703959, 20711125], }; describe('InceptionEventPool', function () { From 973cd75a7a217a991b4755ae59ec9f2e56e96d2b Mon Sep 17 00:00:00 2001 From: Yevhen Shulha Date: Mon, 9 Sep 2024 13:05:12 +0300 Subject: [PATCH 14/22] 3.8.32-inception.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e8df93829..2503b5204 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@paraswap/dex-lib", - "version": "3.8.32-inception", + "version": "3.8.32-inception.2", "main": "build/index.js", "types": "build/index.d.ts", "repository": "https://github.com/paraswap/paraswap-dex-lib", From 9a52d13ee09feb62864fd0083639e56ee8c71813 Mon Sep 17 00:00:00 2001 From: Yevhen Shulha Date: Mon, 9 Sep 2024 13:20:22 +0300 Subject: [PATCH 15/22] fix: update pool name --- src/dex/inception/inception-event-pool.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dex/inception/inception-event-pool.ts b/src/dex/inception/inception-event-pool.ts index 3cda35af2..38971fbda 100644 --- a/src/dex/inception/inception-event-pool.ts +++ b/src/dex/inception/inception-event-pool.ts @@ -19,7 +19,7 @@ export class InceptionEventPool extends StatefulEventSubscriber { public poolConfig: PoolConfig, public poolInterface: Interface, ) { - super(parentName, 'pool', dexHelper, logger); + super(parentName, poolConfig.ratioFeedAddress, dexHelper, logger); this.addressesSubscribed = [poolConfig.ratioFeedAddress]; } From 13d31759a2585b8974c2e9649b71945510209fc6 Mon Sep 17 00:00:00 2001 From: Yevhen Shulha Date: Mon, 9 Sep 2024 23:36:43 +0300 Subject: [PATCH 16/22] fix: acc to review --- src/abi/inception/inception-ineth.json | 609 --------------------- src/dex/inception/inception-event-pool.ts | 16 +- src/dex/inception/inception-events.test.ts | 5 +- src/dex/inception/inception.ts | 27 +- src/dex/inception/tokens.ts | 2 +- 5 files changed, 28 insertions(+), 631 deletions(-) delete mode 100644 src/abi/inception/inception-ineth.json diff --git a/src/abi/inception/inception-ineth.json b/src/abi/inception/inception-ineth.json deleted file mode 100644 index 8d7b78940..000000000 --- a/src/abi/inception/inception-ineth.json +++ /dev/null @@ -1,609 +0,0 @@ -[ - { - "inputs": [], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "allowance", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "needed", - "type": "uint256" - } - ], - "name": "ERC20InsufficientAllowance", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "balance", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "needed", - "type": "uint256" - } - ], - "name": "ERC20InsufficientBalance", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "approver", - "type": "address" - } - ], - "name": "ERC20InvalidApprover", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "receiver", - "type": "address" - } - ], - "name": "ERC20InvalidReceiver", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "sender", - "type": "address" - } - ], - "name": "ERC20InvalidSender", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - } - ], - "name": "ERC20InvalidSpender", - "type": "error" - }, - { - "inputs": [], - "name": "EnforcedPause", - "type": "error" - }, - { - "inputs": [], - "name": "ExpectedPause", - "type": "error" - }, - { - "inputs": [], - "name": "InvalidInitialization", - "type": "error" - }, - { - "inputs": [], - "name": "MathOverflowedMulDiv", - "type": "error" - }, - { - "inputs": [], - "name": "NotInitializing", - "type": "error" - }, - { - "inputs": [], - "name": "OnlyGovernanceAllowed", - "type": "error" - }, - { - "inputs": [], - "name": "OnlyOperatorAllowed", - "type": "error" - }, - { - "inputs": [], - "name": "OnlyRestakingPoolAllowed", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Approval", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "uint64", - "name": "version", - "type": "uint64" - } - ], - "name": "Initialized", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "string", - "name": "newName", - "type": "string" - } - ], - "name": "NameChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "Paused", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "string", - "name": "newSymbol", - "type": "string" - } - ], - "name": "SymbolChanged", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": false, - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "Unpaused", - "type": "event" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "spender", - "type": "address" - } - ], - "name": "allowance", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "spender", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "approve", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "balanceOf", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "internalType": "uint256", - "name": "shares", - "type": "uint256" - } - ], - "name": "burn", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "newName", - "type": "string" - } - ], - "name": "changeName", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "newSymbol", - "type": "string" - } - ], - "name": "changeSymbol", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "config", - "outputs": [ - { - "internalType": "contract IProtocolConfig", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "shares", - "type": "uint256" - } - ], - "name": "convertToAmount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "convertToShares", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "decimals", - "outputs": [ - { - "internalType": "uint8", - "name": "", - "type": "uint8" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "contract IProtocolConfig", - "name": "config", - "type": "address" - }, - { - "internalType": "string", - "name": "name", - "type": "string" - }, - { - "internalType": "string", - "name": "symbol", - "type": "string" - } - ], - "name": "initialize", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "internalType": "uint256", - "name": "shares", - "type": "uint256" - } - ], - "name": "mint", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "name", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "pause", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "paused", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "ratio", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "symbol", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalAssets", - "outputs": [ - { - "internalType": "uint256", - "name": "totalManagedEth", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalSupply", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "transfer", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "transferFrom", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "unpause", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] diff --git a/src/dex/inception/inception-event-pool.ts b/src/dex/inception/inception-event-pool.ts index 38971fbda..0f7eb2c84 100644 --- a/src/dex/inception/inception-event-pool.ts +++ b/src/dex/inception/inception-event-pool.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; import { AsyncOrSync, DeepReadonly } from 'ts-essentials'; -import { BlockHeader, Log, Logger } from '../../types'; +import { Address, BlockHeader, Log, Logger } from '../../types'; import { StatefulEventSubscriber } from '../../stateful-event-subscriber'; import { IDexHelper } from '../../dex-helper/idex-helper'; import { PoolConfig, PoolState } from './types'; @@ -16,12 +16,12 @@ export class InceptionEventPool extends StatefulEventSubscriber { protected network: number, protected dexHelper: IDexHelper, logger: Logger, - public poolConfig: PoolConfig, + protected ratioFeedAddress: Address, public poolInterface: Interface, ) { - super(parentName, poolConfig.ratioFeedAddress, dexHelper, logger); + super(parentName, ratioFeedAddress, dexHelper, logger); - this.addressesSubscribed = [poolConfig.ratioFeedAddress]; + this.addressesSubscribed = [ratioFeedAddress]; } protected processLog( @@ -35,9 +35,11 @@ export class InceptionEventPool extends StatefulEventSubscriber { switch (parsed.name) { case 'RatioUpdated': { const tokenInfo = getTokenFromAddress(this.network, parsed.args[0]); - _state[tokenInfo.symbol.toLowerCase()] = { - ratio: BigInt(parsed.args[2].toString()), - }; + if (tokenInfo) { + _state[tokenInfo.symbol.toLowerCase()] = { + ratio: BigInt(parsed.args[2].toString()), + }; + } return _state; } default: diff --git a/src/dex/inception/inception-events.test.ts b/src/dex/inception/inception-events.test.ts index 43402e5e8..5d5a19834 100644 --- a/src/dex/inception/inception-events.test.ts +++ b/src/dex/inception/inception-events.test.ts @@ -41,10 +41,7 @@ describe('Inception Event', function () { network, dexHelper, logger, - { - ratioFeedAddress: ratioFeedAddress, - initState: {}, - }, + ratioFeedAddress, poolInterface, ); diff --git a/src/dex/inception/inception.ts b/src/dex/inception/inception.ts index 10c1362bf..883f1ec8b 100644 --- a/src/dex/inception/inception.ts +++ b/src/dex/inception/inception.ts @@ -15,7 +15,7 @@ import INCEPTION_ABI from '../../abi/inception/inception-vault.json'; import INCEPTION_POOL_ABI from '../../abi/inception/inception-ineth-pool.json'; import { Network, NULL_ADDRESS } from '../../constants'; import * as CALLDATA_GAS_COST from '../../calldata-gas-cost'; -import { getDexKeysWithNetwork, Utils } from '../../utils'; +import { getDexKeysWithNetwork } from '../../utils'; import { IDex } from '../../dex/idex'; import { IDexHelper } from '../../dex-helper/idex-helper'; import { DexParams, InceptionDexData, PoolState } from './types'; @@ -60,10 +60,7 @@ export class Inception network, dexHelper, this.logger, - { - ratioFeedAddress: InceptionPricePoolConfig[dexKey][network].ratioFeed, - initState: {}, - }, + InceptionPricePoolConfig[dexKey][network].ratioFeed, this.poolInterface, ); } @@ -133,7 +130,7 @@ export class Inception const src = getTokenFromAddress(this.network, srcToken.address); const dest = getTokenFromAddress(this.network, destToken.address); - if (src !== dest) { + if (!src || !dest || src !== dest) { return null; } @@ -152,7 +149,7 @@ export class Inception data: { exchange: dest.vault, }, - poolAddresses: [src.token.toLowerCase()], + poolAddresses: [dest.vault], }, ]; } @@ -182,18 +179,25 @@ export class Inception ): DexExchangeParam { const dexParams = getTokenFromAddress(this.network, srcToken); + if (!dexParams) { + throw new Error('Unknown token'); + } + const isNative = dexParams.baseTokenSlug === 'ETH'; let swapData; let dexFuncHasRecipient; + let swappedAmountNotPresentInExchangeData; if (isNative) { swapData = this.poolInterface.encodeFunctionData('stake()', []); dexFuncHasRecipient = false; + swappedAmountNotPresentInExchangeData = true; } else { swapData = this.vaultInterface.encodeFunctionData('deposit', [ srcAmount, recipient, ]); dexFuncHasRecipient = true; + swappedAmountNotPresentInExchangeData = false; } return { @@ -201,7 +205,7 @@ export class Inception dexFuncHasRecipient, exchangeData: swapData, targetExchange: dexParams.vault, - swappedAmountNotPresentInExchangeData: true, + swappedAmountNotPresentInExchangeData, returnAmountPos: undefined, }; } @@ -214,7 +218,7 @@ export class Inception ): Promise { await this.initializeTokens(); - const token: DexParams = getTokenFromAddress(this.network, tokenAddress); + const token = getTokenFromAddress(this.network, tokenAddress); if (token) { return [ @@ -224,7 +228,10 @@ export class Inception address: token.vault, connectorTokens: [ { - address: token.token, + address: + tokenAddress.toLowerCase() === token.token.toLowerCase() + ? token.baseToken + : token.token, decimals: 1e18, }, ], diff --git a/src/dex/inception/tokens.ts b/src/dex/inception/tokens.ts index 2a452e163..7211644ea 100644 --- a/src/dex/inception/tokens.ts +++ b/src/dex/inception/tokens.ts @@ -32,6 +32,6 @@ export function setTokensOnNetwork(network: Network, tokens: DexParams[]) { export function getTokenFromAddress( network: Network, address: string, -): DexParams { +): DexParams | undefined { return TokensByAddress[network]?.[address.toLowerCase()]; } From 7c1b978d6fab13654f0bb4b9d45275731e7ec0eb Mon Sep 17 00:00:00 2001 From: Yevhen Shulha Date: Tue, 10 Sep 2024 14:51:06 +0300 Subject: [PATCH 17/22] fix: add steth support with fee --- src/dex/inception/inception.ts | 44 +++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/src/dex/inception/inception.ts b/src/dex/inception/inception.ts index 883f1ec8b..2ece91e5c 100644 --- a/src/dex/inception/inception.ts +++ b/src/dex/inception/inception.ts @@ -10,20 +10,28 @@ import { PoolLiquidity, PoolPrices, Token, + TransferFeeParams, } from '../../types'; import INCEPTION_ABI from '../../abi/inception/inception-vault.json'; import INCEPTION_POOL_ABI from '../../abi/inception/inception-ineth-pool.json'; -import { Network, NULL_ADDRESS } from '../../constants'; +import { + DEST_TOKEN_PARASWAP_TRANSFERS, + Network, + NULL_ADDRESS, + SRC_TOKEN_PARASWAP_TRANSFERS, +} from '../../constants'; import * as CALLDATA_GAS_COST from '../../calldata-gas-cost'; import { getDexKeysWithNetwork } from '../../utils'; import { IDex } from '../../dex/idex'; import { IDexHelper } from '../../dex-helper/idex-helper'; -import { DexParams, InceptionDexData, PoolState } from './types'; +import { InceptionDexData, PoolState } from './types'; import { SimpleExchange } from '../simple-exchange'; import { InceptionConfig, InceptionPricePoolConfig } from './config'; -import { getTokenFromAddress, setTokensOnNetwork, Tokens } from './tokens'; +import { getTokenFromAddress, setTokensOnNetwork } from './tokens'; import { getOnChainState, getTokenList } from './utils'; import { InceptionEventPool } from './inception-event-pool'; +import { applyTransferFee } from '../../lib/token-transfer-fee'; +import { RETURN_AMOUNT_POS_0 } from '../../executor/constants'; const DECIMALS = BigInt(1e18); @@ -36,7 +44,7 @@ export class Inception readonly hasConstantPriceLargeAmounts = false; readonly needWrapNative = false; - readonly isFeeOnTransferSupported = false; + readonly isFeeOnTransferSupported = true; public static dexKeysWithNetwork: { key: string; networks: Network[] }[] = getDexKeysWithNetwork(InceptionConfig); @@ -122,6 +130,12 @@ export class Inception side: SwapSide, blockNumber: number, limitPools?: string[], + transferFees: TransferFeeParams = { + srcFee: 0, + destFee: 0, + srcDexFee: 0, + destDexFee: 0, + }, ): Promise> { if (side === SwapSide.BUY) { return null; @@ -137,13 +151,25 @@ export class Inception const poolState = await this.getPoolState(this.eventPool, blockNumber); if (!poolState) return null; + const unitPrice = DECIMALS; + const prices = amounts.map(amount => { + const ratio = poolState[src.symbol.toLowerCase()].ratio; + return (ratio * amount) / DECIMALS; + }); + + const [unitPriceWithFee, ...pricesWithFee] = applyTransferFee( + [unitPrice, ...prices], + side, + side === SwapSide.SELL ? transferFees.srcFee : transferFees.destFee, + side === SwapSide.SELL + ? SRC_TOKEN_PARASWAP_TRANSFERS + : DEST_TOKEN_PARASWAP_TRANSFERS, + ); + return [ { - prices: amounts.map(amount => { - const ratio = poolState[src.symbol.toLowerCase()].ratio; - return (ratio * amount) / DECIMALS; - }), - unit: DECIMALS, + prices: pricesWithFee, + unit: unitPriceWithFee, gasCost: 120_000, exchange: this.dexKey, data: { From 7d104003eecddb668fe287f1dc97fc6135cd98fd Mon Sep 17 00:00:00 2001 From: Yevhen Shulha Date: Tue, 10 Sep 2024 14:59:54 +0300 Subject: [PATCH 18/22] 3.8.32-inception.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2503b5204..4b8964be1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@paraswap/dex-lib", - "version": "3.8.32-inception.2", + "version": "3.8.32-inception.3", "main": "build/index.js", "types": "build/index.d.ts", "repository": "https://github.com/paraswap/paraswap-dex-lib", From a40eb28c158640093cad1a3cfad84ef96f9f6050 Mon Sep 17 00:00:00 2001 From: Yevhen Shulha Date: Wed, 11 Sep 2024 16:18:54 +0300 Subject: [PATCH 19/22] 3.8.34-inception --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f53a932b9..be0d194f4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@paraswap/dex-lib", - "version": "3.8.34", + "version": "3.8.34-inception", "main": "build/index.js", "types": "build/index.d.ts", "repository": "https://github.com/paraswap/paraswap-dex-lib", From 0de6b78ad4533712b1898574052f3d7d1e9398e0 Mon Sep 17 00:00:00 2001 From: Yevhen Shulha Date: Wed, 11 Sep 2024 18:20:25 +0300 Subject: [PATCH 20/22] fix: improvements --- src/dex/inception/inception-e2e.test.ts | 13 ++++++++++++ src/dex/inception/inception.ts | 7 +++++++ src/dex/inception/utils.ts | 27 ------------------------- 3 files changed, 20 insertions(+), 27 deletions(-) diff --git a/src/dex/inception/inception-e2e.test.ts b/src/dex/inception/inception-e2e.test.ts index f6fc05b6a..ec9cb3cd8 100644 --- a/src/dex/inception/inception-e2e.test.ts +++ b/src/dex/inception/inception-e2e.test.ts @@ -48,6 +48,19 @@ function testForNetwork( provider, ); }); + it(`${inceptionSlug} -> ${baseSlug}`, async () => { + await testE2E( + tokens[inceptionSlug], + tokens[baseSlug], + holders[inceptionSlug], + '100000000', + side, + inceptionName, + contractMethod, + network, + provider, + ); + }); }); }); }), diff --git a/src/dex/inception/inception.ts b/src/dex/inception/inception.ts index 2ece91e5c..c8566452d 100644 --- a/src/dex/inception/inception.ts +++ b/src/dex/inception/inception.ts @@ -148,6 +148,12 @@ export class Inception return null; } + // TODO: Implement in both directions + // only baseToken -> inception token swaps are supported + if (src.baseToken.toLowerCase() !== srcToken.address.toLowerCase()) { + return null; + } + const poolState = await this.getPoolState(this.eventPool, blockNumber); if (!poolState) return null; @@ -232,6 +238,7 @@ export class Inception exchangeData: swapData, targetExchange: dexParams.vault, swappedAmountNotPresentInExchangeData, + // TODO: Implement returnAmountPos: undefined, }; } diff --git a/src/dex/inception/utils.ts b/src/dex/inception/utils.ts index 10b9684a0..c59995b51 100644 --- a/src/dex/inception/utils.ts +++ b/src/dex/inception/utils.ts @@ -6,33 +6,6 @@ import { Network } from '../../constants'; const coder = new AbiCoder(); -export async function getOnChainRatio( - multiContract: Contract, - poolInterface: Interface, - network: Network, - p: DexParams, - blockNumber: number | 'latest', -): Promise { - let addr; - if (p.baseTokenSlug === 'ETH') { - addr = p.token; - } else { - addr = p.vault; - } - const data: { returnData: any[] } = await multiContract.methods - .aggregate([ - { - target: addr, - callData: poolInterface.encodeFunctionData('ratio', []), - }, - ]) - .call({}, blockNumber); - - const decodedData = coder.decode(['uint256'], data.returnData[0]); - const ratio = BigInt(decodedData[0].toString()); - return ratio; -} - export async function getOnChainState( multiContract: Contract, poolInterface: Interface, From 06cd33b6a4fcb092306f5b5453f7fe8f074e63a6 Mon Sep 17 00:00:00 2001 From: Yevhen Shulha Date: Wed, 11 Sep 2024 18:23:14 +0300 Subject: [PATCH 21/22] fix: returnAMountPOs --- src/dex/inception/inception.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dex/inception/inception.ts b/src/dex/inception/inception.ts index c8566452d..69539fc41 100644 --- a/src/dex/inception/inception.ts +++ b/src/dex/inception/inception.ts @@ -219,6 +219,7 @@ export class Inception let swapData; let dexFuncHasRecipient; let swappedAmountNotPresentInExchangeData; + let returnAmountPos = undefined; if (isNative) { swapData = this.poolInterface.encodeFunctionData('stake()', []); dexFuncHasRecipient = false; @@ -230,6 +231,7 @@ export class Inception ]); dexFuncHasRecipient = true; swappedAmountNotPresentInExchangeData = false; + returnAmountPos = RETURN_AMOUNT_POS_0; } return { @@ -238,8 +240,7 @@ export class Inception exchangeData: swapData, targetExchange: dexParams.vault, swappedAmountNotPresentInExchangeData, - // TODO: Implement - returnAmountPos: undefined, + returnAmountPos, }; } From 42e4d69d69b545ef58e39ba82f69b2fcd18f6b39 Mon Sep 17 00:00:00 2001 From: Yevhen Shulha Date: Wed, 11 Sep 2024 18:24:32 +0300 Subject: [PATCH 22/22] 3.8.34-inception.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index be0d194f4..426e00853 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@paraswap/dex-lib", - "version": "3.8.34-inception", + "version": "3.8.34-inception.2", "main": "build/index.js", "types": "build/index.d.ts", "repository": "https://github.com/paraswap/paraswap-dex-lib",